Skip to content

Commit 5a07cb4

Browse files
committed
UT[BMQ]: log default allocations
Signed-off-by: Evgeny Malygin <emalygin@bloomberg.net>
1 parent 9ad0ff3 commit 5a07cb4

File tree

5 files changed

+267
-36
lines changed

5 files changed

+267
-36
lines changed

src/groups/bmq/bmqma/bmqma_countingallocator.cpp

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -182,33 +182,7 @@ void CountingAllocator::configureStatContextTableInfoProvider(
182182
.zeroString("");
183183
}
184184

185-
void CountingAllocator::onAllocationChange(bsls::Types::Int64 deltaValue)
186-
{
187-
const bsls::Types::Uint64 totalAllocated = d_allocated.addRelaxed(
188-
deltaValue);
189-
190-
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(totalAllocated >
191-
d_allocationLimit)) {
192-
BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
193-
const bsls::Types::Uint64 uint64Max =
194-
bsl::numeric_limits<bsls::Types::Uint64>::max();
195-
if (d_allocationLimit.swap(uint64Max) != uint64Max) {
196-
// Use an atomic swap to only invoke the callback the first time
197-
// limit is crossed. Swap 'allocationLimit' with 'Uint64::max',
198-
// which will disable all further maximum allocation checks.
199-
200-
BSLS_ASSERT_SAFE(d_allocationLimitCb);
201-
// If d_allocationLimit was set, 'd_allocationLimitCb' must be
202-
// a valid callback
203-
d_allocationLimitCb();
204-
}
205-
}
206-
207-
// Propagate allocation change to parent, if any
208-
if (d_parentCounting_p) {
209-
d_parentCounting_p->onAllocationChange(deltaValue);
210-
}
211-
}
185+
// CREATORS
212186

213187
CountingAllocator::CountingAllocator(const bslstl::StringRef& name,
214188
bslma::Allocator* allocator)
@@ -270,6 +244,34 @@ CountingAllocator::~CountingAllocator()
270244

271245
// MANIPULATORS
272246

247+
void CountingAllocator::onAllocationChange(bsls::Types::Int64 deltaValue)
248+
{
249+
const bsls::Types::Uint64 totalAllocated = d_allocated.addRelaxed(
250+
deltaValue);
251+
252+
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(totalAllocated >
253+
d_allocationLimit)) {
254+
BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
255+
const bsls::Types::Uint64 uint64Max =
256+
bsl::numeric_limits<bsls::Types::Uint64>::max();
257+
if (d_allocationLimit.swap(uint64Max) != uint64Max) {
258+
// Use an atomic swap to only invoke the callback the first time
259+
// limit is crossed. Swap 'allocationLimit' with 'Uint64::max',
260+
// which will disable all further maximum allocation checks.
261+
262+
BSLS_ASSERT_SAFE(d_allocationLimitCb);
263+
// If d_allocationLimit was set, 'd_allocationLimitCb' must be
264+
// a valid callback
265+
d_allocationLimitCb();
266+
}
267+
}
268+
269+
// Propagate allocation change to parent, if any
270+
if (d_parentCounting_p) {
271+
d_parentCounting_p->onAllocationChange(deltaValue);
272+
}
273+
}
274+
273275
void CountingAllocator::setAllocationLimit(
274276
bsls::Types::Uint64 limit,
275277
const AllocationLimitCallback& callback)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2014-2025 Bloomberg Finance L.P.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
// bmqtst_loggingallocator.cpp -*-C++-*-
17+
#include <bmqtst_loggingallocator.h>
18+
19+
#include <bmqscm_version.h>
20+
21+
#include <bmqu_memoutstream.h>
22+
23+
// BDE
24+
#include <balst_stacktraceprintutil.h> // balst::StackTracePrintUtil
25+
#include <bsl_iostream.h> // bsl::cerr
26+
27+
namespace BloombergLP {
28+
namespace bmqtst {
29+
30+
namespace {
31+
32+
static bool isStackInExceptions(const bslstl::StringRef& str)
33+
{
34+
struct StackExceptionRule {
35+
const char* d_stackStr;
36+
} k_RULES[] = {
37+
{"bdlmt::EventSchedulerTestTimeSource::EventSchedulerTestTimeSource"},
38+
{"bdls::FilesystemUtil::makeUnsafeTemporaryFilename"},
39+
{"bdls::FilesystemUtil::remove"},
40+
{"MessageGUIDUtil::initialize"}};
41+
42+
const size_t k_NUM_RULES = sizeof(k_RULES) / sizeof(*k_RULES);
43+
for (size_t idx = 0; idx < k_NUM_RULES; ++idx) {
44+
const StackExceptionRule& rule = k_RULES[idx];
45+
if (str.find(rule.d_stackStr) != bsl::string::npos)
46+
return true;
47+
}
48+
return false;
49+
}
50+
51+
}
52+
53+
// ----------------------
54+
// class LoggingAllocator
55+
// ----------------------
56+
57+
// CREATORS
58+
59+
LoggingAllocator::LoggingAllocator(bool failFast, bslma::Allocator* allocator)
60+
: d_allocator_p(allocator)
61+
, d_failFast(failFast)
62+
, d_failed(false)
63+
{
64+
// NOTHING
65+
}
66+
67+
LoggingAllocator::~LoggingAllocator()
68+
{
69+
// NOTHING
70+
}
71+
72+
// MANIPULATORS
73+
74+
void* LoggingAllocator::allocate(size_type size)
75+
{
76+
bmqu::MemOutStream os(d_allocator_p);
77+
balst::StackTracePrintUtil::printStackTrace(os);
78+
79+
if (!isStackInExceptions(os.str())) {
80+
bsl::cerr << os.str() << bsl::endl;
81+
bsl::flush(bsl::cerr);
82+
83+
d_failed.storeRelaxed(true);
84+
if (d_failFast) {
85+
BSLS_ASSERT_OPT(false && "The allocation was recorded");
86+
}
87+
}
88+
return d_allocator_p->allocate(size);
89+
}
90+
91+
void LoggingAllocator::deallocate(void* address)
92+
{
93+
d_allocator_p->deallocate(address);
94+
}
95+
96+
void LoggingAllocator::checkNoAllocations()
97+
{
98+
BSLS_ASSERT_OPT(!d_failed.load() && "The allocations were recorded");
99+
}
100+
101+
} // close package namespace
102+
} // close enterprise namespace
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2014-2025 Bloomberg Finance L.P.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
// bmqtst_loggingallocator.h -*-C++-*-
17+
#ifndef INCLUDED_BMQTST_LOGGINGALLOCATOR
18+
#define INCLUDED_BMQTST_LOGGINGALLOCATOR
19+
20+
//@PURPOSE: Provide a 'bslma::Allocator' caching allocation stack traces.
21+
//
22+
//@CLASSES:
23+
// bmqtst::LoggingAllocator : allocator adaptor.
24+
//
25+
//@DESCRIPTION: This component defines a mechanism, 'bmqtst::LoggingAllocator'
26+
// which implements the 'bslma::Allocator' protocol and which logs stack traces
27+
// on any allocation.
28+
29+
// BDE
30+
#include <bsl_string.h>
31+
#include <bsl_vector.h>
32+
#include <bslma_allocator.h>
33+
#include <bsls_atomic.h>
34+
#include <bsls_keyword.h>
35+
36+
namespace BloombergLP {
37+
38+
namespace bmqtst {
39+
40+
// ======================
41+
// class LoggingAllocator
42+
// ======================
43+
44+
/// An allocator caching allocation stack traces.
45+
class LoggingAllocator BSLS_KEYWORD_FINAL : public bslma::Allocator {
46+
public:
47+
// PUBLIC TYPES
48+
49+
/// Alias for a signed integral type capable of representing the number
50+
/// of bytes in this platform's virtual address space.
51+
typedef bsls::Types::size_type size_type;
52+
53+
private:
54+
// DATA
55+
/// @brief Base allocator to use.
56+
bslma::Allocator* d_allocator_p;
57+
58+
/// @brief The flag indicaing that the allocator must fail instantly if any
59+
/// undesired allocation happens.
60+
bool d_failFast;
61+
62+
/// @brief The flag indicating that this allocator has failed.
63+
bsls::AtomicBool d_failed;
64+
65+
private:
66+
// NOT IMPLEMENTED
67+
LoggingAllocator(const LoggingAllocator&) BSLS_KEYWORD_DELETED;
68+
LoggingAllocator& operator=(const LoggingAllocator&) BSLS_KEYWORD_DELETED;
69+
70+
public:
71+
// CREATORS
72+
73+
/// @brief Construct a logging allocator.
74+
/// @param failFast The flag indicating whether this allocator must raise
75+
/// an exception instantly on any undesired allocation.
76+
/// @param allocator The base allocator to use, or use the default
77+
/// allocator if this arg is null.
78+
explicit LoggingAllocator(bool failFast = false,
79+
bslma::Allocator* allocator = 0);
80+
81+
/// Destroy this object.
82+
~LoggingAllocator() BSLS_KEYWORD_OVERRIDE;
83+
84+
// MANIPULATORS
85+
86+
// (virtual bslma::Allocator)
87+
88+
/// @brief Return a newly allocated block of memory. If this allocator
89+
/// cannot return the requested number of bytes, then it will
90+
/// throw a `bsl::bad_alloc` exception in an exception-enabled
91+
/// build, or else will abort the program in a non-exception build.
92+
/// The behavior is undefined unless `0 <= size`. Note that the
93+
/// alignment of the address returned conforms to the platform
94+
/// requirement for any object of the specified `size`.
95+
/// @param size The minimum size of the newly allocated block (in bytes).
96+
/// If `size` is 0, a null pointer is returned with no other
97+
/// effect.
98+
/// @return The memory address of the allocated block or a null pointer.
99+
void* allocate(size_type size) BSLS_KEYWORD_OVERRIDE;
100+
101+
/// @brief Return the memory block at the specified `address` back to this
102+
/// allocator. The behavior is undefined unless `address` was
103+
/// allocated using this allocator object and has not already been
104+
/// deallocated.
105+
/// @param address The address of the memory block to deallocate. If this
106+
/// argument is a null pointer, the function doesn't have an effect.
107+
void deallocate(void* address) BSLS_KEYWORD_OVERRIDE;
108+
109+
/// @brief Check whether the undesired allocations were recorded and raise
110+
/// an exception if any.
111+
void checkNoAllocations();
112+
};
113+
114+
} // close package namespace
115+
} // close enterprise namespace
116+
117+
#endif

src/groups/bmq/bmqtst/bmqtst_testhelper.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@
278278
// }
279279
//..
280280

281+
// BMQ
282+
#include <bmqtst_loggingallocator.h>
283+
281284
// BDE
282285
#include <ball_attributecontext.h>
283286
#include <ball_fileobserver.h>
@@ -637,7 +640,10 @@
637640
bslma::TestAllocator _defAlloc( \
638641
"default", \
639642
(bmqtst::TestHelperUtil::verbosityLevel() >= 4)); \
640-
bslma::DefaultAllocatorGuard _defAllocGuard(&_defAlloc); \
643+
bmqtst::LoggingAllocator _loggingAlloc( \
644+
(F) & bmqtst::TestHelper::e_INSTANT_DEF_ALLOC_FAIL, \
645+
&_defAlloc); \
646+
bslma::DefaultAllocatorGuard _defAllocGuard(&_loggingAlloc); \
641647
\
642648
/* Test driver allocator */ \
643649
bslma::TestAllocator _testAlloc( \
@@ -663,13 +669,14 @@
663669
BMQTST_ASSERT_EQ(_testAlloc.numBlocksInUse(), 0); \
664670
\
665671
/* Verify no default allocator usage */ \
666-
if (F & bmqtst::TestHelper::e_CHECK_DEF_ALLOC && \
672+
if ((F) & bmqtst::TestHelper::e_CHECK_DEF_ALLOC && \
667673
!bmqtst::TestHelperUtil::ignoreCheckDefAlloc()) { \
674+
_loggingAlloc.checkNoAllocations(); \
668675
BMQTST_ASSERT_EQ(_defAlloc.numBlocksTotal(), 0); \
669676
} \
670677
\
671678
/* Verify no global allocator usage */ \
672-
if (F & bmqtst::TestHelper::e_CHECK_GBL_ALLOC && \
679+
if ((F) & bmqtst::TestHelper::e_CHECK_GBL_ALLOC && \
673680
!bmqtst::TestHelperUtil::ignoreCheckGblAlloc()) { \
674681
BMQTST_ASSERT_EQ(_gblAlloc.numBlocksTotal(), 0); \
675682
} \
@@ -895,14 +902,16 @@ struct TestHelper {
895902
// TYPES
896903
enum e_FLAGS {
897904
/// Flags to provide to `TEST_PROLOG` and `TEST_EPILOG` macros.
898-
e_DEFAULT = 0
905+
e_DEFAULT = 0,
899906

900-
// PROLOG FLAGS
901-
,
902-
e_USE_STACKTRACE_ALLOCATOR = 1 << 0
907+
/// ==== PROLOG FLAGS ====
908+
/// Use `balst::StackTraceTestAllocator` to search for memory leaks.
909+
e_USE_STACKTRACE_ALLOCATOR = 1 << 0,
910+
/// Raise an exception instantly if any unneeded default allocation
911+
/// happens.
912+
e_INSTANT_DEF_ALLOC_FAIL = 1 << 1,
903913

904-
// EPILOG FLAGS
905-
,
914+
/// ==== EPILOG FLAGS ====
906915
e_CHECK_DEF_ALLOC = 1 << 0,
907916
e_CHECK_GBL_ALLOC = 1 << 1,
908917
e_CHECK_DEF_GBL_ALLOC = e_CHECK_DEF_ALLOC | e_CHECK_GBL_ALLOC
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
bmqtst_blobtestutil
2+
bmqtst_loggingallocator
23
bmqtst_scopedlogobserver
34
bmqtst_testhelper

0 commit comments

Comments
 (0)