Skip to content

Commit 54f35d0

Browse files
committed
UT[BMQ]: log default allocations
Signed-off-by: Evgeny Malygin <emalygin@bloomberg.net>
1 parent 85886b2 commit 54f35d0

File tree

5 files changed

+269
-37
lines changed

5 files changed

+269
-37
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: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
41+
const size_t k_NUM_RULES = sizeof(k_RULES) / sizeof(*k_RULES);
42+
for (size_t idx = 0; idx < k_NUM_RULES; ++idx) {
43+
const StackExceptionRule& rule = k_RULES[idx];
44+
if (str.find(rule.d_stackStr) != bsl::string::npos)
45+
return true;
46+
}
47+
return false;
48+
}
49+
50+
}
51+
52+
// ----------------------
53+
// class LoggingAllocator
54+
// ----------------------
55+
56+
// CREATORS
57+
58+
LoggingAllocator::LoggingAllocator(bool failFast, bslma::Allocator* allocator)
59+
: d_allocator_p(allocator)
60+
, d_stacks(d_allocator_p)
61+
, d_failFast(failFast)
62+
{
63+
// NOTHING
64+
}
65+
66+
LoggingAllocator::~LoggingAllocator()
67+
{
68+
// NOTHING
69+
}
70+
71+
// MANIPULATORS
72+
73+
void* LoggingAllocator::allocate(size_type size)
74+
{
75+
bmqu::MemOutStream os(d_allocator_p);
76+
balst::StackTracePrintUtil::printStackTrace(os);
77+
78+
if (!isStackInExceptions(os.str())) {
79+
if (d_failFast) {
80+
bsl::cerr << os.str() << bsl::endl;
81+
BSLS_ASSERT_OPT(false && "The allocation was recorded");
82+
}
83+
else {
84+
d_stacks.push_back(bsl::string(os.str(), d_allocator_p));
85+
}
86+
}
87+
return d_allocator_p->allocate(size);
88+
}
89+
90+
void LoggingAllocator::deallocate(void* address)
91+
{
92+
d_allocator_p->deallocate(address);
93+
}
94+
95+
void LoggingAllocator::checkNoAllocations()
96+
{
97+
for (size_t i = 0; i < d_stacks.size(); i++) {
98+
bsl::cerr << d_stacks[i] << bsl::endl;
99+
}
100+
BSLS_ASSERT_OPT(d_stacks.empty() && "The allocations were recorded");
101+
}
102+
103+
} // close package namespace
104+
} // close enterprise namespace
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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_keyword.h>
34+
35+
namespace BloombergLP {
36+
37+
namespace bmqtst {
38+
39+
// ======================
40+
// class LoggingAllocator
41+
// ======================
42+
43+
/// An allocator caching allocation stack traces.
44+
class LoggingAllocator BSLS_KEYWORD_FINAL : public bslma::Allocator {
45+
public:
46+
// PUBLIC TYPES
47+
48+
/// Alias for a signed integral type capable of representing the number
49+
/// of bytes in this platform's virtual address space.
50+
typedef bsls::Types::size_type size_type;
51+
52+
private:
53+
// DATA
54+
/// @brief Base allocator to use.
55+
bslma::Allocator* d_allocator_p;
56+
57+
/// @brief The recorded stack traces.
58+
bsl::vector<bsl::string> d_stacks;
59+
60+
/// @brief The flag indicaing that the allocator must fail instantly if any
61+
/// undesired allocation happens.
62+
bool d_failFast;
63+
64+
private:
65+
// NOT IMPLEMENTED
66+
LoggingAllocator(const LoggingAllocator&) BSLS_KEYWORD_DELETED;
67+
LoggingAllocator& operator=(const LoggingAllocator&) BSLS_KEYWORD_DELETED;
68+
69+
public:
70+
// CREATORS
71+
72+
/// @brief Construct a logging allocator.
73+
/// @param failFast The flag indicating whether this allocator must raise
74+
/// an exception instantly on any undesired allocation.
75+
/// @param allocator The base allocator to use, or use the default
76+
/// allocator if this arg is null.
77+
explicit LoggingAllocator(bool failFast = false,
78+
bslma::Allocator* allocator = 0);
79+
80+
/// Destroy this object.
81+
~LoggingAllocator() BSLS_KEYWORD_OVERRIDE;
82+
83+
// MANIPULATORS
84+
85+
// (virtual bslma::Allocator)
86+
87+
/// @brief Return a newly allocated block of memory. If this allocator
88+
/// cannot return the requested number of bytes, then it will
89+
/// throw a `bsl::bad_alloc` exception in an exception-enabled
90+
/// build, or else will abort the program in a non-exception build.
91+
/// The behavior is undefined unless `0 <= size`. Note that the
92+
/// alignment of the address returned conforms to the platform
93+
/// requirement for any object of the specified `size`.
94+
/// @param size The minimum size of the newly allocated block (in bytes).
95+
/// If `size` is 0, a null pointer is returned with no other
96+
/// effect.
97+
/// @return The memory address of the allocated block or a null pointer.
98+
void* allocate(size_type size) BSLS_KEYWORD_OVERRIDE;
99+
100+
/// @brief Return the memory block at the specified `address` back to this
101+
/// allocator. The behavior is undefined unless `address` was
102+
/// allocated using this allocator object and has not already been
103+
/// deallocated.
104+
/// @param address The address of the memory block to deallocate. If this
105+
/// argument is a null pointer, the function doesn't have an effect.
106+
void deallocate(void* address) BSLS_KEYWORD_OVERRIDE;
107+
108+
/// @brief Check whether the undesired allocations were recorded and raise
109+
/// an exception if any.
110+
void checkNoAllocations();
111+
};
112+
113+
} // close package namespace
114+
} // close enterprise namespace
115+
116+
#endif

src/groups/bmq/bmqtst/bmqtst_testhelper.h

Lines changed: 19 additions & 10 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) != 0, \
645+
&_defAlloc); \
646+
bslma::DefaultAllocatorGuard _defAllocGuard(&_loggingAlloc); \
641647
\
642648
/* Test driver allocator */ \
643649
bslma::TestAllocator _testAlloc( \
@@ -648,7 +654,7 @@
648654
balst::StackTraceTestAllocator _stTestAlloc; \
649655
_stTestAlloc.setName("test"); \
650656
\
651-
if ((F) & bmqtst::TestHelper::e_USE_STACKTRACE_ALLOCATOR) { \
657+
if ((F)&bmqtst::TestHelper::e_USE_STACKTRACE_ALLOCATOR) { \
652658
bmqtst::TestHelperUtil::allocator() = &_stTestAlloc; \
653659
} \
654660
else { \
@@ -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)