608 lines
19 KiB
C++
608 lines
19 KiB
C++
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/debug/thread_heap_usage_tracker.h"
|
|
|
|
#include <map>
|
|
|
|
#include "base/allocator/allocator_shim.h"
|
|
#include "base/allocator/buildflags.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
#if defined(OS_MACOSX)
|
|
#include "base/allocator/allocator_interception_mac.h"
|
|
#endif
|
|
|
|
namespace base {
|
|
namespace debug {
|
|
|
|
namespace {
|
|
|
|
class TestingThreadHeapUsageTracker : public ThreadHeapUsageTracker {
|
|
public:
|
|
using ThreadHeapUsageTracker::DisableHeapTrackingForTesting;
|
|
using ThreadHeapUsageTracker::EnsureTLSInitialized;
|
|
using ThreadHeapUsageTracker::GetDispatchForTesting;
|
|
};
|
|
|
|
// A fixture class that allows testing the AllocatorDispatch associated with
|
|
// the ThreadHeapUsageTracker class in isolation against a mocked
|
|
// underlying
|
|
// heap implementation.
|
|
class ThreadHeapUsageTrackerTest : public testing::Test {
|
|
public:
|
|
using AllocatorDispatch = base::allocator::AllocatorDispatch;
|
|
|
|
static const size_t kAllocationPadding;
|
|
enum SizeFunctionKind {
|
|
EXACT_SIZE_FUNCTION,
|
|
PADDING_SIZE_FUNCTION,
|
|
ZERO_SIZE_FUNCTION,
|
|
};
|
|
|
|
ThreadHeapUsageTrackerTest() : size_function_kind_(EXACT_SIZE_FUNCTION) {
|
|
EXPECT_EQ(nullptr, g_self);
|
|
g_self = this;
|
|
}
|
|
|
|
~ThreadHeapUsageTrackerTest() override {
|
|
EXPECT_EQ(this, g_self);
|
|
g_self = nullptr;
|
|
}
|
|
|
|
void set_size_function_kind(SizeFunctionKind kind) {
|
|
size_function_kind_ = kind;
|
|
}
|
|
|
|
void SetUp() override {
|
|
TestingThreadHeapUsageTracker::EnsureTLSInitialized();
|
|
|
|
dispatch_under_test_ =
|
|
TestingThreadHeapUsageTracker::GetDispatchForTesting();
|
|
ASSERT_EQ(nullptr, dispatch_under_test_->next);
|
|
|
|
dispatch_under_test_->next = &g_mock_dispatch;
|
|
}
|
|
|
|
void TearDown() override {
|
|
ASSERT_EQ(&g_mock_dispatch, dispatch_under_test_->next);
|
|
|
|
dispatch_under_test_->next = nullptr;
|
|
}
|
|
|
|
void* MockMalloc(size_t size) {
|
|
return dispatch_under_test_->alloc_function(dispatch_under_test_, size,
|
|
nullptr);
|
|
}
|
|
|
|
void* MockCalloc(size_t n, size_t size) {
|
|
return dispatch_under_test_->alloc_zero_initialized_function(
|
|
dispatch_under_test_, n, size, nullptr);
|
|
}
|
|
|
|
void* MockAllocAligned(size_t alignment, size_t size) {
|
|
return dispatch_under_test_->alloc_aligned_function(
|
|
dispatch_under_test_, alignment, size, nullptr);
|
|
}
|
|
|
|
void* MockRealloc(void* address, size_t size) {
|
|
return dispatch_under_test_->realloc_function(dispatch_under_test_, address,
|
|
size, nullptr);
|
|
}
|
|
|
|
void MockFree(void* address) {
|
|
dispatch_under_test_->free_function(dispatch_under_test_, address, nullptr);
|
|
}
|
|
|
|
size_t MockGetSizeEstimate(void* address) {
|
|
return dispatch_under_test_->get_size_estimate_function(
|
|
dispatch_under_test_, address, nullptr);
|
|
}
|
|
|
|
private:
|
|
void RecordAlloc(void* address, size_t size) {
|
|
if (address != nullptr)
|
|
allocation_size_map_[address] = size;
|
|
}
|
|
|
|
void DeleteAlloc(void* address) {
|
|
if (address != nullptr)
|
|
EXPECT_EQ(1U, allocation_size_map_.erase(address));
|
|
}
|
|
|
|
size_t GetSizeEstimate(void* address) {
|
|
auto it = allocation_size_map_.find(address);
|
|
if (it == allocation_size_map_.end())
|
|
return 0;
|
|
|
|
size_t ret = it->second;
|
|
switch (size_function_kind_) {
|
|
case EXACT_SIZE_FUNCTION:
|
|
break;
|
|
case PADDING_SIZE_FUNCTION:
|
|
ret += kAllocationPadding;
|
|
break;
|
|
case ZERO_SIZE_FUNCTION:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void* OnAllocFn(const AllocatorDispatch* self,
|
|
size_t size,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
void* ret = malloc(size);
|
|
g_self->RecordAlloc(ret, size);
|
|
return ret;
|
|
}
|
|
|
|
static void* OnAllocZeroInitializedFn(const AllocatorDispatch* self,
|
|
size_t n,
|
|
size_t size,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
void* ret = calloc(n, size);
|
|
g_self->RecordAlloc(ret, n * size);
|
|
return ret;
|
|
}
|
|
|
|
static void* OnAllocAlignedFn(const AllocatorDispatch* self,
|
|
size_t alignment,
|
|
size_t size,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
// This is a cheat as it doesn't return aligned allocations. This has the
|
|
// advantage of working for all platforms for this test.
|
|
void* ret = malloc(size);
|
|
g_self->RecordAlloc(ret, size);
|
|
return ret;
|
|
}
|
|
|
|
static void* OnReallocFn(const AllocatorDispatch* self,
|
|
void* address,
|
|
size_t size,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
g_self->DeleteAlloc(address);
|
|
void* ret = realloc(address, size);
|
|
g_self->RecordAlloc(ret, size);
|
|
return ret;
|
|
}
|
|
|
|
static void OnFreeFn(const AllocatorDispatch* self,
|
|
void* address,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
g_self->DeleteAlloc(address);
|
|
free(address);
|
|
}
|
|
|
|
static size_t OnGetSizeEstimateFn(const AllocatorDispatch* self,
|
|
void* address,
|
|
void* context) {
|
|
EXPECT_EQ(&g_mock_dispatch, self);
|
|
|
|
return g_self->GetSizeEstimate(address);
|
|
}
|
|
|
|
using AllocationSizeMap = std::map<void*, size_t>;
|
|
|
|
SizeFunctionKind size_function_kind_;
|
|
AllocationSizeMap allocation_size_map_;
|
|
AllocatorDispatch* dispatch_under_test_;
|
|
|
|
static base::allocator::AllocatorDispatch g_mock_dispatch;
|
|
static ThreadHeapUsageTrackerTest* g_self;
|
|
};
|
|
|
|
const size_t ThreadHeapUsageTrackerTest::kAllocationPadding = 23;
|
|
|
|
ThreadHeapUsageTrackerTest* ThreadHeapUsageTrackerTest::g_self = nullptr;
|
|
|
|
base::allocator::AllocatorDispatch ThreadHeapUsageTrackerTest::g_mock_dispatch =
|
|
{
|
|
&ThreadHeapUsageTrackerTest::OnAllocFn, // alloc_function
|
|
&ThreadHeapUsageTrackerTest::
|
|
OnAllocZeroInitializedFn, // alloc_zero_initialized_function
|
|
&ThreadHeapUsageTrackerTest::
|
|
OnAllocAlignedFn, // alloc_aligned_function
|
|
&ThreadHeapUsageTrackerTest::OnReallocFn, // realloc_function
|
|
&ThreadHeapUsageTrackerTest::OnFreeFn, // free_function
|
|
&ThreadHeapUsageTrackerTest::
|
|
OnGetSizeEstimateFn, // get_size_estimate_function
|
|
nullptr, // batch_malloc
|
|
nullptr, // batch_free
|
|
nullptr, // free_definite_size_function
|
|
nullptr, // next
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithExactSizeFunction) {
|
|
set_size_function_kind(EXACT_SIZE_FUNCTION);
|
|
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
|
|
EXPECT_EQ(0U, u1.alloc_ops);
|
|
EXPECT_EQ(0U, u1.alloc_bytes);
|
|
EXPECT_EQ(0U, u1.alloc_overhead_bytes);
|
|
EXPECT_EQ(0U, u1.free_ops);
|
|
EXPECT_EQ(0U, u1.free_bytes);
|
|
EXPECT_EQ(0U, u1.max_allocated_bytes);
|
|
|
|
const size_t kAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kAllocSize);
|
|
MockFree(ptr);
|
|
|
|
usage_tracker.Stop(false);
|
|
ThreadHeapUsage u2 = usage_tracker.usage();
|
|
|
|
EXPECT_EQ(1U, u2.alloc_ops);
|
|
EXPECT_EQ(kAllocSize, u2.alloc_bytes);
|
|
EXPECT_EQ(0U, u2.alloc_overhead_bytes);
|
|
EXPECT_EQ(1U, u2.free_ops);
|
|
EXPECT_EQ(kAllocSize, u2.free_bytes);
|
|
EXPECT_EQ(kAllocSize, u2.max_allocated_bytes);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithPaddingSizeFunction) {
|
|
set_size_function_kind(PADDING_SIZE_FUNCTION);
|
|
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
|
|
EXPECT_EQ(0U, u1.alloc_ops);
|
|
EXPECT_EQ(0U, u1.alloc_bytes);
|
|
EXPECT_EQ(0U, u1.alloc_overhead_bytes);
|
|
EXPECT_EQ(0U, u1.free_ops);
|
|
EXPECT_EQ(0U, u1.free_bytes);
|
|
EXPECT_EQ(0U, u1.max_allocated_bytes);
|
|
|
|
const size_t kAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kAllocSize);
|
|
MockFree(ptr);
|
|
|
|
usage_tracker.Stop(false);
|
|
ThreadHeapUsage u2 = usage_tracker.usage();
|
|
|
|
EXPECT_EQ(1U, u2.alloc_ops);
|
|
EXPECT_EQ(kAllocSize + kAllocationPadding, u2.alloc_bytes);
|
|
EXPECT_EQ(kAllocationPadding, u2.alloc_overhead_bytes);
|
|
EXPECT_EQ(1U, u2.free_ops);
|
|
EXPECT_EQ(kAllocSize + kAllocationPadding, u2.free_bytes);
|
|
EXPECT_EQ(kAllocSize + kAllocationPadding, u2.max_allocated_bytes);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithZeroSizeFunction) {
|
|
set_size_function_kind(ZERO_SIZE_FUNCTION);
|
|
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(0U, u1.alloc_ops);
|
|
EXPECT_EQ(0U, u1.alloc_bytes);
|
|
EXPECT_EQ(0U, u1.alloc_overhead_bytes);
|
|
EXPECT_EQ(0U, u1.free_ops);
|
|
EXPECT_EQ(0U, u1.free_bytes);
|
|
EXPECT_EQ(0U, u1.max_allocated_bytes);
|
|
|
|
const size_t kAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kAllocSize);
|
|
MockFree(ptr);
|
|
|
|
usage_tracker.Stop(false);
|
|
ThreadHeapUsage u2 = usage_tracker.usage();
|
|
|
|
// With a get-size function that returns zero, there's no way to get the size
|
|
// of an allocation that's being freed, hence the shim can't tally freed bytes
|
|
// nor the high-watermark allocated bytes.
|
|
EXPECT_EQ(1U, u2.alloc_ops);
|
|
EXPECT_EQ(kAllocSize, u2.alloc_bytes);
|
|
EXPECT_EQ(0U, u2.alloc_overhead_bytes);
|
|
EXPECT_EQ(1U, u2.free_ops);
|
|
EXPECT_EQ(0U, u2.free_bytes);
|
|
EXPECT_EQ(0U, u2.max_allocated_bytes);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, ReallocCorrectlyTallied) {
|
|
const size_t kAllocSize = 237U;
|
|
|
|
{
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
// Reallocating nullptr should count as a single alloc.
|
|
void* ptr = MockRealloc(nullptr, kAllocSize);
|
|
ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(1U, usage.alloc_ops);
|
|
EXPECT_EQ(kAllocSize, usage.alloc_bytes);
|
|
EXPECT_EQ(0U, usage.alloc_overhead_bytes);
|
|
EXPECT_EQ(0U, usage.free_ops);
|
|
EXPECT_EQ(0U, usage.free_bytes);
|
|
EXPECT_EQ(kAllocSize, usage.max_allocated_bytes);
|
|
|
|
// Reallocating a valid pointer to a zero size should count as a single
|
|
// free.
|
|
ptr = MockRealloc(ptr, 0U);
|
|
|
|
usage_tracker.Stop(false);
|
|
EXPECT_EQ(1U, usage_tracker.usage().alloc_ops);
|
|
EXPECT_EQ(kAllocSize, usage_tracker.usage().alloc_bytes);
|
|
EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes);
|
|
EXPECT_EQ(1U, usage_tracker.usage().free_ops);
|
|
EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes);
|
|
EXPECT_EQ(kAllocSize, usage_tracker.usage().max_allocated_bytes);
|
|
|
|
// Realloc to zero size may or may not return a nullptr - make sure to
|
|
// free the zero-size alloc in the latter case.
|
|
if (ptr != nullptr)
|
|
MockFree(ptr);
|
|
}
|
|
|
|
{
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
void* ptr = MockMalloc(kAllocSize);
|
|
ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(1U, usage.alloc_ops);
|
|
|
|
// Now try reallocating a valid pointer to a larger size, this should count
|
|
// as one free and one alloc.
|
|
const size_t kLargerAllocSize = kAllocSize + 928U;
|
|
ptr = MockRealloc(ptr, kLargerAllocSize);
|
|
|
|
usage_tracker.Stop(false);
|
|
EXPECT_EQ(2U, usage_tracker.usage().alloc_ops);
|
|
EXPECT_EQ(kAllocSize + kLargerAllocSize, usage_tracker.usage().alloc_bytes);
|
|
EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes);
|
|
EXPECT_EQ(1U, usage_tracker.usage().free_ops);
|
|
EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes);
|
|
EXPECT_EQ(kLargerAllocSize, usage_tracker.usage().max_allocated_bytes);
|
|
|
|
MockFree(ptr);
|
|
}
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, NestedMaxWorks) {
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
const size_t kOuterAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kOuterAllocSize);
|
|
MockFree(ptr);
|
|
|
|
EXPECT_EQ(kOuterAllocSize,
|
|
ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
|
|
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
const size_t kInnerAllocSize = 673U;
|
|
ptr = MockMalloc(kInnerAllocSize);
|
|
MockFree(ptr);
|
|
|
|
inner_usage_tracker.Stop(false);
|
|
|
|
EXPECT_EQ(kInnerAllocSize, inner_usage_tracker.usage().max_allocated_bytes);
|
|
}
|
|
|
|
// The greater, outer allocation size should have been restored.
|
|
EXPECT_EQ(kOuterAllocSize,
|
|
ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
|
|
|
|
const size_t kLargerInnerAllocSize = kOuterAllocSize + 673U;
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
ptr = MockMalloc(kLargerInnerAllocSize);
|
|
MockFree(ptr);
|
|
|
|
inner_usage_tracker.Stop(false);
|
|
EXPECT_EQ(kLargerInnerAllocSize,
|
|
inner_usage_tracker.usage().max_allocated_bytes);
|
|
}
|
|
|
|
// The greater, inner allocation size should have been preserved.
|
|
EXPECT_EQ(kLargerInnerAllocSize,
|
|
ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
|
|
|
|
// Now try the case with an outstanding net alloc size when entering the
|
|
// inner scope.
|
|
void* outer_ptr = MockMalloc(kOuterAllocSize);
|
|
EXPECT_EQ(kLargerInnerAllocSize,
|
|
ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
ptr = MockMalloc(kLargerInnerAllocSize);
|
|
MockFree(ptr);
|
|
|
|
inner_usage_tracker.Stop(false);
|
|
EXPECT_EQ(kLargerInnerAllocSize,
|
|
inner_usage_tracker.usage().max_allocated_bytes);
|
|
}
|
|
|
|
// While the inner scope saw only the inner net outstanding allocation size,
|
|
// the outer scope saw both outstanding at the same time.
|
|
EXPECT_EQ(kOuterAllocSize + kLargerInnerAllocSize,
|
|
ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
|
|
|
|
MockFree(outer_ptr);
|
|
|
|
// Test a net-negative scope.
|
|
ptr = MockMalloc(kLargerInnerAllocSize);
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
MockFree(ptr);
|
|
|
|
const size_t kInnerAllocSize = 1;
|
|
ptr = MockMalloc(kInnerAllocSize);
|
|
|
|
inner_usage_tracker.Stop(false);
|
|
// Since the scope is still net-negative, the max is clamped at zero.
|
|
EXPECT_EQ(0U, inner_usage_tracker.usage().max_allocated_bytes);
|
|
}
|
|
|
|
MockFree(ptr);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, NoStopImpliesInclusive) {
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
const size_t kOuterAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kOuterAllocSize);
|
|
MockFree(ptr);
|
|
|
|
ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes);
|
|
|
|
const size_t kInnerLargerAllocSize = kOuterAllocSize + 673U;
|
|
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
// Make a larger allocation than the outer scope.
|
|
ptr = MockMalloc(kInnerLargerAllocSize);
|
|
MockFree(ptr);
|
|
|
|
// inner_usage_tracker goes out of scope without a Stop().
|
|
}
|
|
|
|
ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(usage.alloc_ops + 1, current.alloc_ops);
|
|
EXPECT_EQ(usage.alloc_bytes + kInnerLargerAllocSize, current.alloc_bytes);
|
|
EXPECT_EQ(usage.free_ops + 1, current.free_ops);
|
|
EXPECT_EQ(usage.free_bytes + kInnerLargerAllocSize, current.free_bytes);
|
|
EXPECT_EQ(kInnerLargerAllocSize, current.max_allocated_bytes);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, ExclusiveScopesWork) {
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
const size_t kOuterAllocSize = 1029U;
|
|
void* ptr = MockMalloc(kOuterAllocSize);
|
|
MockFree(ptr);
|
|
|
|
ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes);
|
|
|
|
{
|
|
ThreadHeapUsageTracker inner_usage_tracker;
|
|
inner_usage_tracker.Start();
|
|
|
|
// Make a larger allocation than the outer scope.
|
|
ptr = MockMalloc(kOuterAllocSize + 673U);
|
|
MockFree(ptr);
|
|
|
|
// This tracker is exlusive, all activity should be private to this scope.
|
|
inner_usage_tracker.Stop(true);
|
|
}
|
|
|
|
ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
EXPECT_EQ(usage.alloc_ops, current.alloc_ops);
|
|
EXPECT_EQ(usage.alloc_bytes, current.alloc_bytes);
|
|
EXPECT_EQ(usage.alloc_overhead_bytes, current.alloc_overhead_bytes);
|
|
EXPECT_EQ(usage.free_ops, current.free_ops);
|
|
EXPECT_EQ(usage.free_bytes, current.free_bytes);
|
|
EXPECT_EQ(usage.max_allocated_bytes, current.max_allocated_bytes);
|
|
}
|
|
|
|
TEST_F(ThreadHeapUsageTrackerTest, AllShimFunctionsAreProvided) {
|
|
const size_t kAllocSize = 100;
|
|
void* alloc = MockMalloc(kAllocSize);
|
|
size_t estimate = MockGetSizeEstimate(alloc);
|
|
ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
|
|
MockFree(alloc);
|
|
|
|
alloc = MockCalloc(kAllocSize, 1);
|
|
estimate = MockGetSizeEstimate(alloc);
|
|
ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
|
|
MockFree(alloc);
|
|
|
|
alloc = MockAllocAligned(1, kAllocSize);
|
|
estimate = MockGetSizeEstimate(alloc);
|
|
ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
|
|
|
|
alloc = MockRealloc(alloc, kAllocSize);
|
|
estimate = MockGetSizeEstimate(alloc);
|
|
ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
|
|
MockFree(alloc);
|
|
}
|
|
|
|
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
|
class ThreadHeapUsageShimTest : public testing::Test {
|
|
#if defined(OS_MACOSX)
|
|
void SetUp() override { allocator::InitializeAllocatorShim(); }
|
|
void TearDown() override { allocator::UninterceptMallocZonesForTesting(); }
|
|
#endif
|
|
};
|
|
|
|
TEST_F(ThreadHeapUsageShimTest, HooksIntoMallocWhenShimAvailable) {
|
|
ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
|
|
|
|
ThreadHeapUsageTracker::EnableHeapTracking();
|
|
|
|
ASSERT_TRUE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
|
|
|
|
const size_t kAllocSize = 9993;
|
|
// This test verifies that the scoped heap data is affected by malloc &
|
|
// free only when the shim is available.
|
|
ThreadHeapUsageTracker usage_tracker;
|
|
usage_tracker.Start();
|
|
|
|
ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
void* ptr = malloc(kAllocSize);
|
|
// Prevent the compiler from optimizing out the malloc/free pair.
|
|
ASSERT_NE(nullptr, ptr);
|
|
|
|
ThreadHeapUsage u2 = ThreadHeapUsageTracker::GetUsageSnapshot();
|
|
free(ptr);
|
|
|
|
usage_tracker.Stop(false);
|
|
ThreadHeapUsage u3 = usage_tracker.usage();
|
|
|
|
// Verify that at least one allocation operation was recorded, and that free
|
|
// operations are at least monotonically growing.
|
|
EXPECT_LE(0U, u1.alloc_ops);
|
|
EXPECT_LE(u1.alloc_ops + 1, u2.alloc_ops);
|
|
EXPECT_LE(u1.alloc_ops + 1, u3.alloc_ops);
|
|
|
|
// Verify that at least the bytes above were recorded.
|
|
EXPECT_LE(u1.alloc_bytes + kAllocSize, u2.alloc_bytes);
|
|
|
|
// Verify that at least the one free operation above was recorded.
|
|
EXPECT_LE(u2.free_ops + 1, u3.free_ops);
|
|
|
|
TestingThreadHeapUsageTracker::DisableHeapTrackingForTesting();
|
|
|
|
ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
|
|
}
|
|
#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
|
|
|
|
} // namespace debug
|
|
} // namespace base
|