mirror of
https://github.com/gperftools/gperftools
synced 2024-12-19 05:54:34 +00:00
implement TestingPortal
Some our tests are intended to test behavior of external APIs (like operator new etc), but they do need occasional ability to peek into the guts of tcmalloc. We want to make our .so libraries to be built with -fvisibility=hidden, so we'll loose this ability. In order to have this limited ability for tests to get into the guts of tcmalloc, we introduce TestingPortal exactly for that. It uses carefuly designed and thouroughly undocumented GetNumericProperty extension to grab pointer to TestingPortal. Then through calling member functions of this instance, we're able to do the right thing.
This commit is contained in:
parent
66bdf24061
commit
ddc162f1d1
@ -1055,6 +1055,13 @@ static inline void DebugDeallocate(void* ptr, int type, size_t given_size) {
|
||||
if (ptr) MallocBlock::FromRawPointer(ptr)->Deallocate(type, given_size);
|
||||
}
|
||||
|
||||
class ATTRIBUTE_HIDDEN DebugTestingPortal : public TestingPortalImpl {
|
||||
public:
|
||||
~DebugTestingPortal() override = default;
|
||||
bool IsDebuggingMalloc() override { return true; }
|
||||
int32_t& GetMaxFreeQueueSize() override { return FLAGS_max_free_queue_size; }
|
||||
};
|
||||
|
||||
// ========================================================================= //
|
||||
|
||||
// The following functions may be called via MallocExtension::instance()
|
||||
@ -1062,6 +1069,18 @@ static inline void DebugDeallocate(void* ptr, int type, size_t given_size) {
|
||||
class DebugMallocImplementation : public TCMallocImplementation {
|
||||
public:
|
||||
virtual bool GetNumericProperty(const char* name, size_t* value) {
|
||||
if (TestingPortal** portal = TestingPortal::CheckGetPortal(name, value); portal) {
|
||||
static DebugTestingPortal* ptr = ([] () {
|
||||
static struct {
|
||||
alignas(DebugTestingPortal) char memory[sizeof(DebugTestingPortal)];
|
||||
} storage;
|
||||
return new (storage.memory) DebugTestingPortal;
|
||||
})();
|
||||
*portal = ptr;
|
||||
*value = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = TCMallocImplementation::GetNumericProperty(name, value);
|
||||
if (result && (strcmp(name, "generic.current_allocated_bytes") == 0)) {
|
||||
// Subtract bytes kept in the free queue
|
||||
|
@ -2383,3 +2383,18 @@ const void* HeapLeakChecker::GetAllocCaller(void* ptr) {
|
||||
RAW_CHECK(info.stack_depth >= 1, "");
|
||||
return info.call_stack[0];
|
||||
}
|
||||
|
||||
namespace tcmalloc {
|
||||
|
||||
ATTRIBUTE_HIDDEN
|
||||
void DoIterateMemoryRegionMap(tcmalloc::FunctionRef<void(const void*)> callback) {
|
||||
{ MemoryRegionMap::LockHolder l;
|
||||
for (MemoryRegionMap::RegionIterator
|
||||
i = MemoryRegionMap::BeginRegionLocked();
|
||||
i != MemoryRegionMap::EndRegionLocked(); ++i) {
|
||||
callback(&*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tcmalloc
|
||||
|
@ -129,6 +129,7 @@
|
||||
#include "thread_cache_ptr.h"
|
||||
|
||||
#include "maybe_emergency_malloc.h"
|
||||
#include "testing_portal.h"
|
||||
|
||||
#if (defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)) && !defined(WIN32_OVERRIDE_ALLOCATORS)
|
||||
# define WIN32_DO_PATCHING 1
|
||||
@ -156,9 +157,15 @@ using tcmalloc::Static;
|
||||
using tcmalloc::ThreadCache;
|
||||
using tcmalloc::ThreadCachePtr;
|
||||
|
||||
using tcmalloc::TestingPortal;
|
||||
|
||||
DECLARE_double(tcmalloc_release_rate);
|
||||
DECLARE_int64(tcmalloc_heap_limit_mb);
|
||||
|
||||
#ifndef NO_HEAP_CHECK
|
||||
DECLARE_string(heap_check);
|
||||
#endif
|
||||
|
||||
// Those common architectures are known to be safe w.r.t. aliasing function
|
||||
// with "extra" unused args to function with fewer arguments (e.g.
|
||||
// tc_delete_nothrow being aliased to tc_delete).
|
||||
@ -543,6 +550,75 @@ static void IterateOverRanges(void* arg, MallocExtension::RangeFunction func) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace tcmalloc {
|
||||
|
||||
TestingPortal::~TestingPortal() = default;
|
||||
|
||||
// implemented in heap-checker.cc
|
||||
extern void DoIterateMemoryRegionMap(tcmalloc::FunctionRef<void(const void*)> callback);
|
||||
|
||||
class ATTRIBUTE_HIDDEN TestingPortalImpl : public TestingPortal {
|
||||
public:
|
||||
~TestingPortalImpl() override = default;
|
||||
|
||||
bool HaveSystemRelease() override {
|
||||
static bool have = ([] () {
|
||||
size_t actual;
|
||||
auto ptr = TCMalloc_SystemAlloc(kPageSize, &actual, 0);
|
||||
return TCMalloc_SystemRelease(ptr, actual);
|
||||
})();
|
||||
return have;
|
||||
}
|
||||
bool IsDebuggingMalloc() override {
|
||||
return false;
|
||||
}
|
||||
size_t GetPageSize() override {
|
||||
return kPageSize;
|
||||
}
|
||||
size_t GetMinAlign() override {
|
||||
return kMinAlign;
|
||||
}
|
||||
size_t GetMaxSize() override {
|
||||
return kMaxSize;
|
||||
}
|
||||
int64_t& GetSampleParameter() override {
|
||||
return FLAGS_tcmalloc_sample_parameter;
|
||||
}
|
||||
double& GetReleaseRate() override {
|
||||
return FLAGS_tcmalloc_release_rate;
|
||||
}
|
||||
int32_t& GetMaxFreeQueueSize() override {
|
||||
abort();
|
||||
}
|
||||
|
||||
std::string_view GetHeapCheckFlag() {
|
||||
#ifndef NO_HEAP_CHECK
|
||||
return FLAGS_heap_check;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
void IterateMemoryRegionMap(tcmalloc::FunctionRef<void(const void*)> callback) {
|
||||
#ifndef NO_HEAP_CHECK
|
||||
DoIterateMemoryRegionMap(callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
static TestingPortalImpl* Get() {
|
||||
static TestingPortalImpl* ptr = ([] () {
|
||||
static struct {
|
||||
alignas(TestingPortalImpl) char memory[sizeof(TestingPortalImpl)];
|
||||
} storage;
|
||||
return new (storage.memory) TestingPortalImpl;
|
||||
}());
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tcmalloc
|
||||
|
||||
using tcmalloc::TestingPortalImpl;
|
||||
|
||||
// TCMalloc's support for extra malloc interfaces
|
||||
class TCMallocImplementation : public MallocExtension {
|
||||
private:
|
||||
@ -775,6 +851,12 @@ class TCMallocImplementation : public MallocExtension {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TestingPortal** portal = TestingPortal::CheckGetPortal(name, value); portal) {
|
||||
*portal = TestingPortalImpl::Get();
|
||||
*value = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
89
src/testing_portal.h
Normal file
89
src/testing_portal.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
|
||||
* Copyright (c) 2024, gperftools Contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef TESTING_PORTAL_H_
|
||||
#define TESTING_PORTAL_H_
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gperftools/malloc_extension.h>
|
||||
|
||||
#include "base/function_ref.h"
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace tcmalloc {
|
||||
|
||||
class ATTRIBUTE_HIDDEN TestingPortal {
|
||||
public:
|
||||
static inline constexpr char kMagic[] = "tcmalloc.impl.testing-portal";
|
||||
static TestingPortal* Get() {
|
||||
static TestingPortal* instance = ([] () {
|
||||
struct {
|
||||
TestingPortal* ptr = nullptr;
|
||||
size_t v = 0;
|
||||
} s;
|
||||
bool ok = MallocExtension::instance()->GetNumericProperty(kMagic, &s.v);
|
||||
if (!ok || s.ptr == nullptr) {
|
||||
abort();
|
||||
}
|
||||
return s.ptr;
|
||||
})();
|
||||
|
||||
return instance;
|
||||
}
|
||||
static TestingPortal** CheckGetPortal(const char* property_name, size_t* value) {
|
||||
if (strcmp(property_name, kMagic) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<TestingPortal**>(value) - 1;
|
||||
}
|
||||
|
||||
virtual bool HaveSystemRelease() = 0;
|
||||
virtual bool IsDebuggingMalloc() = 0;
|
||||
virtual size_t GetPageSize() = 0;
|
||||
virtual size_t GetMinAlign() = 0;
|
||||
virtual size_t GetMaxSize() = 0;
|
||||
virtual int64_t& GetSampleParameter() = 0;
|
||||
virtual double& GetReleaseRate() = 0;
|
||||
virtual int32_t& GetMaxFreeQueueSize() = 0;
|
||||
|
||||
// For heap checker unit test
|
||||
virtual std::string_view GetHeapCheckFlag() = 0;
|
||||
virtual void IterateMemoryRegionMap(tcmalloc::FunctionRef<void(const void*)> callback) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~TestingPortal();
|
||||
};
|
||||
|
||||
} // namespace tcmalloc
|
||||
|
||||
#endif // TESTING_PORTAL_H_
|
Loading…
Reference in New Issue
Block a user