Index: third_party/tcmalloc/malloc_extension.cc |
=================================================================== |
--- third_party/tcmalloc/malloc_extension.cc (revision 0) |
+++ third_party/tcmalloc/malloc_extension.cc (revision 0) |
@@ -0,0 +1,326 @@ |
+// Copyright (c) 2005, Google Inc. |
+// 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. |
+ |
+// --- |
+// Author: Sanjay Ghemawat <opensource@google.com> |
+ |
+#include <config.h> |
+#include <assert.h> |
+#include <stdio.h> |
+#include <string.h> |
+#include <stdio.h> |
+#if defined HAVE_STDINT_H |
+#include <stdint.h> |
+#elif defined HAVE_INTTYPES_H |
+#include <inttypes.h> |
+#else |
+#include <sys/types.h> |
+#endif |
+#include <string> |
+#include "base/dynamic_annotations.h" |
+#include "base/sysinfo.h" // for FillProcSelfMaps |
+#include "google/malloc_extension.h" |
+#include "maybe_threads.h" |
+ |
+using STL_NAMESPACE::string; |
+ |
+static void DumpAddressMap(string* result) { |
+ *result += "\nMAPPED_LIBRARIES:\n"; |
+ // We keep doubling until we get a fit |
+ const size_t old_resultlen = result->size(); |
+ for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) { |
+ result->resize(old_resultlen + amap_size); |
+ const int bytes_written = |
+ tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size); |
+ if (bytes_written < amap_size - 1) { // we fit! |
+ (*result)[old_resultlen + bytes_written] = '\0'; |
+ result->resize(old_resultlen + bytes_written); |
+ return; |
+ } |
+ } |
+ result->reserve(old_resultlen); // just don't print anything |
+} |
+ |
+// Note: this routine is meant to be called before threads are spawned. |
+void MallocExtension::Initialize() { |
+ static bool initialize_called = false; |
+ |
+ if (initialize_called) return; |
+ initialize_called = true; |
+ |
+#ifdef __GLIBC__ |
+ // GNU libc++ versions 3.3 and 3.4 obey the environment variables |
+ // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting |
+ // one of these variables forces the STL default allocator to call |
+ // new() or delete() for each allocation or deletion. Otherwise |
+ // the STL allocator tries to avoid the high cost of doing |
+ // allocations by pooling memory internally. However, tcmalloc |
+ // does allocations really fast, especially for the types of small |
+ // items one sees in STL, so it's better off just using us. |
+ // TODO: control whether we do this via an environment variable? |
+ setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/); |
+ setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/); |
+ |
+ // Now we need to make the setenv 'stick', which it may not do since |
+ // the env is flakey before main() is called. But luckily stl only |
+ // looks at this env var the first time it tries to do an alloc, and |
+ // caches what it finds. So we just cause an stl alloc here. |
+ string dummy("I need to be allocated"); |
+ dummy += "!"; // so the definition of dummy isn't optimized out |
+#endif /* __GLIBC__ */ |
+} |
+ |
+// Default implementation -- does nothing |
+MallocExtension::~MallocExtension() { } |
+bool MallocExtension::VerifyAllMemory() { return true; } |
+bool MallocExtension::VerifyNewMemory(void* p) { return true; } |
+bool MallocExtension::VerifyArrayNewMemory(void* p) { return true; } |
+bool MallocExtension::VerifyMallocMemory(void* p) { return true; } |
+ |
+bool MallocExtension::GetNumericProperty(const char* property, size_t* value) { |
+ return false; |
+} |
+ |
+bool MallocExtension::SetNumericProperty(const char* property, size_t value) { |
+ return false; |
+} |
+ |
+void MallocExtension::GetStats(char* buffer, int length) { |
+ assert(length > 0); |
+ buffer[0] = '\0'; |
+} |
+ |
+bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total, |
+ int histogram[kMallocHistogramSize]) { |
+ *blocks = 0; |
+ *total = 0; |
+ memset(histogram, 0, sizeof(histogram)); |
+ return true; |
+} |
+ |
+void** MallocExtension::ReadStackTraces(int* sample_period) { |
+ return NULL; |
+} |
+ |
+void** MallocExtension::ReadHeapGrowthStackTraces() { |
+ return NULL; |
+} |
+ |
+void MallocExtension::MarkThreadIdle() { |
+ // Default implementation does nothing |
+} |
+ |
+void MallocExtension::ReleaseFreeMemory() { |
+ // Default implementation does nothing |
+} |
+ |
+void MallocExtension::Scavenge() { |
+ // Default implementation does nothing |
+} |
+ |
+void MallocExtension::SetMemoryReleaseRate(double rate) { |
+ // Default implementation does nothing |
+} |
+ |
+double MallocExtension::GetMemoryReleaseRate() { |
+ return -1.0; |
+} |
+ |
+size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) { |
+ return size; |
+} |
+ |
+size_t MallocExtension::GetAllocatedSize(void* p) { |
+ return 0; |
+} |
+ |
+// The current malloc extension object. We also keep a pointer to |
+// the default implementation so that the heap-leak checker does not |
+// complain about a memory leak. |
+ |
+static pthread_once_t module_init = PTHREAD_ONCE_INIT; |
+static MallocExtension* default_instance = NULL; |
+static MallocExtension* current_instance = NULL; |
+ |
+static void InitModule() { |
+ default_instance = new MallocExtension; |
+ current_instance = default_instance; |
+} |
+ |
+MallocExtension* MallocExtension::instance() { |
+ perftools_pthread_once(&module_init, InitModule); |
+ return current_instance; |
+} |
+ |
+void MallocExtension::Register(MallocExtension* implementation) { |
+ perftools_pthread_once(&module_init, InitModule); |
+ // When running under valgrind, our custom malloc is replaced with |
+ // valgrind's one and malloc extensions will not work. |
+ if (!RunningOnValgrind()) { |
+ current_instance = implementation; |
+ } |
+} |
+ |
+// ----------------------------------------------------------------------- |
+// Heap sampling support |
+// ----------------------------------------------------------------------- |
+ |
+namespace { |
+ |
+// Accessors |
+uintptr_t Count(void** entry) { |
+ return reinterpret_cast<uintptr_t>(entry[0]); |
+} |
+uintptr_t Size(void** entry) { |
+ return reinterpret_cast<uintptr_t>(entry[1]); |
+} |
+uintptr_t Depth(void** entry) { |
+ return reinterpret_cast<uintptr_t>(entry[2]); |
+} |
+void* PC(void** entry, int i) { |
+ return entry[3+i]; |
+} |
+ |
+void PrintCountAndSize(MallocExtensionWriter* writer, |
+ uintptr_t count, uintptr_t size) { |
+ char buf[100]; |
+ snprintf(buf, sizeof(buf), |
+ "%6lld: %8lld [%6lld: %8lld] @", |
+ static_cast<long long>(count), |
+ static_cast<long long>(size), |
+ static_cast<long long>(count), |
+ static_cast<long long>(size)); |
+ writer->append(buf, strlen(buf)); |
+} |
+ |
+void PrintHeader(MallocExtensionWriter* writer, |
+ const char* label, void** entries) { |
+ // Compute the total count and total size |
+ uintptr_t total_count = 0; |
+ uintptr_t total_size = 0; |
+ for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { |
+ total_count += Count(entry); |
+ total_size += Size(entry); |
+ } |
+ |
+ const char* const kTitle = "heap profile: "; |
+ writer->append(kTitle, strlen(kTitle)); |
+ PrintCountAndSize(writer, total_count, total_size); |
+ writer->append(" ", 1); |
+ writer->append(label, strlen(label)); |
+ writer->append("\n", 1); |
+} |
+ |
+void PrintStackEntry(MallocExtensionWriter* writer, void** entry) { |
+ PrintCountAndSize(writer, Count(entry), Size(entry)); |
+ |
+ for (int i = 0; i < Depth(entry); i++) { |
+ char buf[32]; |
+ snprintf(buf, sizeof(buf), " %p", PC(entry, i)); |
+ writer->append(buf, strlen(buf)); |
+ } |
+ writer->append("\n", 1); |
+} |
+ |
+} |
+ |
+void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) { |
+ int sample_period = 0; |
+ void** entries = ReadStackTraces(&sample_period); |
+ if (entries == NULL) { |
+ const char* const kErrorMsg = |
+ "This malloc implementation does not support sampling.\n" |
+ "As of 2005/01/26, only tcmalloc supports sampling, and\n" |
+ "you are probably running a binary that does not use\n" |
+ "tcmalloc.\n"; |
+ writer->append(kErrorMsg, strlen(kErrorMsg)); |
+ return; |
+ } |
+ |
+ char label[32]; |
+ sprintf(label, "heap_v2/%d", sample_period); |
+ PrintHeader(writer, label, entries); |
+ for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { |
+ PrintStackEntry(writer, entry); |
+ } |
+ delete[] entries; |
+ |
+ DumpAddressMap(writer); |
+} |
+ |
+void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) { |
+ void** entries = ReadHeapGrowthStackTraces(); |
+ if (entries == NULL) { |
+ const char* const kErrorMsg = |
+ "This malloc implementation does not support " |
+ "ReadHeapGrowthStackTraces().\n" |
+ "As of 2005/09/27, only tcmalloc supports this, and you\n" |
+ "are probably running a binary that does not use tcmalloc.\n"; |
+ writer->append(kErrorMsg, strlen(kErrorMsg)); |
+ return; |
+ } |
+ |
+ // Do not canonicalize the stack entries, so that we get a |
+ // time-ordered list of stack traces, which may be useful if the |
+ // client wants to focus on the latest stack traces. |
+ PrintHeader(writer, "growth", entries); |
+ for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { |
+ PrintStackEntry(writer, entry); |
+ } |
+ delete[] entries; |
+ |
+ DumpAddressMap(writer); |
+} |
+ |
+// These are C shims that work on the current instance. |
+ |
+#define C_SHIM(fn, retval, paramlist, arglist) \ |
+ extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist { \ |
+ return MallocExtension::instance()->fn arglist; \ |
+ } |
+ |
+C_SHIM(VerifyAllMemory, bool, (), ()); |
+C_SHIM(VerifyNewMemory, bool, (void* p), (p)); |
+C_SHIM(VerifyArrayNewMemory, bool, (void* p), (p)); |
+C_SHIM(VerifyMallocMemory, bool, (void* p), (p)); |
+C_SHIM(MallocMemoryStats, bool, |
+ (int* blocks, size_t* total, int histogram[kMallocHistogramSize]), |
+ (blocks, total, histogram)); |
+ |
+C_SHIM(GetStats, void, |
+ (char* buffer, int buffer_length), (buffer, buffer_length)); |
+C_SHIM(GetNumericProperty, bool, |
+ (const char* property, size_t* value), (property, value)); |
+C_SHIM(SetNumericProperty, bool, |
+ (const char* property, size_t value), (property, value)); |
+ |
+C_SHIM(MarkThreadIdle, void, (), ()); |
+C_SHIM(ReleaseFreeMemory, void, (), ()); |
+C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size)); |
+C_SHIM(GetAllocatedSize, size_t, (void* p), (p)); |