|
|
|
// Copyright 2017 The Abseil Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include "absl/base/internal/malloc_extension.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <atomic>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "absl/base/dynamic_annotations.h"
|
|
|
|
#include "absl/base/internal/malloc_extension_c.h"
|
|
|
|
|
|
|
|
namespace absl {
|
|
|
|
namespace base_internal {
|
|
|
|
|
|
|
|
// SysAllocator implementation
|
|
|
|
SysAllocator::~SysAllocator() {}
|
|
|
|
void SysAllocator::GetStats(char* buffer, int) { buffer[0] = 0; }
|
|
|
|
|
|
|
|
// Dummy key method to avoid weak vtable.
|
|
|
|
void MallocExtensionWriter::UnusedKeyMethod() {}
|
|
|
|
|
|
|
|
void StringMallocExtensionWriter::Write(const char* buf, int len) {
|
|
|
|
out_->append(buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default implementation -- does nothing
|
|
|
|
MallocExtension::~MallocExtension() { }
|
|
|
|
bool MallocExtension::VerifyAllMemory() { return true; }
|
|
|
|
bool MallocExtension::VerifyNewMemory(const void*) { return true; }
|
|
|
|
bool MallocExtension::VerifyArrayNewMemory(const void*) { return true; }
|
|
|
|
bool MallocExtension::VerifyMallocMemory(const void*) { return true; }
|
|
|
|
|
|
|
|
bool MallocExtension::GetNumericProperty(const char*, size_t*) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MallocExtension::SetNumericProperty(const char*, size_t) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::GetStats(char* buffer, int length) {
|
|
|
|
assert(length > 0);
|
|
|
|
static_cast<void>(length);
|
|
|
|
buffer[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total,
|
|
|
|
int histogram[kMallocHistogramSize]) {
|
|
|
|
*blocks = 0;
|
|
|
|
*total = 0;
|
|
|
|
memset(histogram, 0, sizeof(*histogram) * kMallocHistogramSize);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::MarkThreadIdle() {
|
|
|
|
// Default implementation does nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::MarkThreadBusy() {
|
|
|
|
// Default implementation does nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
SysAllocator* MallocExtension::GetSystemAllocator() {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::SetSystemAllocator(SysAllocator*) {
|
|
|
|
// Default implementation does nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::ReleaseToSystem(size_t) {
|
|
|
|
// Default implementation does nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::ReleaseFreeMemory() {
|
|
|
|
ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::SetMemoryReleaseRate(double) {
|
|
|
|
// Default implementation does nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
double MallocExtension::GetMemoryReleaseRate() {
|
|
|
|
return -1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MallocExtension::GetAllocatedSize(const void* p) {
|
|
|
|
assert(GetOwnership(p) != kNotOwned);
|
|
|
|
static_cast<void>(p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
MallocExtension::Ownership MallocExtension::GetOwnership(const void*) {
|
|
|
|
return kUnknownOwnership;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::GetProperties(MallocExtension::StatLevel,
|
|
|
|
std::map<std::string, Property>* result) {
|
|
|
|
result->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MallocExtension::ReleaseCPUMemory(int) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The current malloc extension object.
|
|
|
|
|
|
|
|
std::atomic<MallocExtension*> MallocExtension::current_instance_;
|
|
|
|
|
|
|
|
MallocExtension* MallocExtension::InitModule() {
|
|
|
|
MallocExtension* ext = new MallocExtension;
|
|
|
|
current_instance_.store(ext, std::memory_order_release);
|
|
|
|
return ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MallocExtension::Register(MallocExtension* implementation) {
|
|
|
|
InitModuleOnce();
|
|
|
|
// When running under valgrind, our custom malloc is replaced with
|
|
|
|
// valgrind's one and malloc extensions will not work. (Note:
|
|
|
|
// callers should be responsible for checking that they are the
|
|
|
|
// malloc that is really being run, before calling Register. This
|
|
|
|
// is just here as an extra sanity check.)
|
|
|
|
// Under compiler-based ThreadSanitizer RunningOnValgrind() returns true,
|
|
|
|
// but we still want to use malloc extensions.
|
|
|
|
#ifndef THREAD_SANITIZER
|
|
|
|
if (RunningOnValgrind()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif // #ifndef THREAD_SANITIZER
|
|
|
|
current_instance_.store(implementation, std::memory_order_release);
|
|
|
|
}
|
|
|
|
void MallocExtension::GetHeapSample(MallocExtensionWriter*) {}
|
|
|
|
|
|
|
|
void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter*) {}
|
|
|
|
|
|
|
|
void MallocExtension::GetFragmentationProfile(MallocExtensionWriter*) {}
|
|
|
|
|
|
|
|
} // namespace base_internal
|
|
|
|
} // namespace absl
|
|
|
|
|
|
|
|
// These are C shims that work on the current instance.
|
|
|
|
|
|
|
|
#define C_SHIM(fn, retval, paramlist, arglist) \
|
|
|
|
extern "C" retval MallocExtension_##fn paramlist { \
|
|
|
|
return absl::base_internal::MallocExtension::instance()->fn arglist; \
|
|
|
|
}
|
|
|
|
|
|
|
|
C_SHIM(VerifyAllMemory, int, (void), ());
|
|
|
|
C_SHIM(VerifyNewMemory, int, (const void* p), (p));
|
|
|
|
C_SHIM(VerifyArrayNewMemory, int, (const void* p), (p));
|
|
|
|
C_SHIM(VerifyMallocMemory, int, (const void* p), (p));
|
|
|
|
C_SHIM(
|
|
|
|
MallocMemoryStats, int,
|
|
|
|
(int* blocks, size_t* total,
|
|
|
|
int histogram[absl::base_internal::MallocExtension::kMallocHistogramSize]),
|
|
|
|
(blocks, total, histogram));
|
|
|
|
|
|
|
|
C_SHIM(GetStats, void,
|
|
|
|
(char* buffer, int buffer_length), (buffer, buffer_length));
|
|
|
|
C_SHIM(GetNumericProperty, int,
|
|
|
|
(const char* property, size_t* value), (property, value));
|
|
|
|
C_SHIM(SetNumericProperty, int,
|
|
|
|
(const char* property, size_t value), (property, value));
|
|
|
|
|
|
|
|
C_SHIM(MarkThreadIdle, void, (void), ());
|
|
|
|
C_SHIM(MarkThreadBusy, void, (void), ());
|
|
|
|
C_SHIM(ReleaseFreeMemory, void, (void), ());
|
|
|
|
C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes));
|
|
|
|
C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size));
|
|
|
|
C_SHIM(GetAllocatedSize, size_t, (const void* p), (p));
|
|
|
|
|
|
|
|
// Can't use the shim here because of the need to translate the enums.
|
|
|
|
extern "C"
|
|
|
|
MallocExtension_Ownership MallocExtension_GetOwnership(const void* p) {
|
|
|
|
return static_cast<MallocExtension_Ownership>(
|
|
|
|
absl::base_internal::MallocExtension::instance()->GetOwnership(p));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default implementation just returns size. The expectation is that
|
|
|
|
// the linked-in malloc implementation might provide an override of
|
|
|
|
// this weak function with a better implementation.
|
|
|
|
ABSL_ATTRIBUTE_WEAK ABSL_ATTRIBUTE_NOINLINE size_t nallocx(size_t size, int) {
|
|
|
|
return size;
|
|
|
|
}
|