1// Copyright (c) 2005, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30// --- 31// Author: Sanjay Ghemawat <opensource@google.com> 32 33#include <config.h> 34#include <assert.h> 35#include <string.h> 36#include <stdio.h> 37#if defined HAVE_STDINT_H 38#include <stdint.h> 39#elif defined HAVE_INTTYPES_H 40#include <inttypes.h> 41#else 42#include <sys/types.h> 43#endif 44#include <string> 45#include "base/dynamic_annotations.h" 46#include "base/sysinfo.h" // for FillProcSelfMaps 47#ifndef NO_HEAP_CHECK 48#include "gperftools/heap-checker.h" 49#endif 50#include "gperftools/malloc_extension.h" 51#include "gperftools/malloc_extension_c.h" 52#include "maybe_threads.h" 53 54using STL_NAMESPACE::string; 55using STL_NAMESPACE::vector; 56 57static void DumpAddressMap(string* result) { 58 *result += "\nMAPPED_LIBRARIES:\n"; 59 // We keep doubling until we get a fit 60 const size_t old_resultlen = result->size(); 61 for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) { 62 result->resize(old_resultlen + amap_size); 63 bool wrote_all = false; 64 const int bytes_written = 65 tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size, 66 &wrote_all); 67 if (wrote_all) { // we fit! 68 (*result)[old_resultlen + bytes_written] = '\0'; 69 result->resize(old_resultlen + bytes_written); 70 return; 71 } 72 } 73 result->reserve(old_resultlen); // just don't print anything 74} 75 76// Note: this routine is meant to be called before threads are spawned. 77void MallocExtension::Initialize() { 78 static bool initialize_called = false; 79 80 if (initialize_called) return; 81 initialize_called = true; 82 83#ifdef __GLIBC__ 84 // GNU libc++ versions 3.3 and 3.4 obey the environment variables 85 // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting 86 // one of these variables forces the STL default allocator to call 87 // new() or delete() for each allocation or deletion. Otherwise 88 // the STL allocator tries to avoid the high cost of doing 89 // allocations by pooling memory internally. However, tcmalloc 90 // does allocations really fast, especially for the types of small 91 // items one sees in STL, so it's better off just using us. 92 // TODO: control whether we do this via an environment variable? 93 setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/); 94 setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/); 95 96 // Now we need to make the setenv 'stick', which it may not do since 97 // the env is flakey before main() is called. But luckily stl only 98 // looks at this env var the first time it tries to do an alloc, and 99 // caches what it finds. So we just cause an stl alloc here. 100 string dummy("I need to be allocated"); 101 dummy += "!"; // so the definition of dummy isn't optimized out 102#endif /* __GLIBC__ */ 103} 104 105// SysAllocator implementation 106SysAllocator::~SysAllocator() {} 107 108// Default implementation -- does nothing 109MallocExtension::~MallocExtension() { } 110bool MallocExtension::VerifyAllMemory() { return true; } 111bool MallocExtension::VerifyNewMemory(const void* p) { return true; } 112bool MallocExtension::VerifyArrayNewMemory(const void* p) { return true; } 113bool MallocExtension::VerifyMallocMemory(const void* p) { return true; } 114 115bool MallocExtension::GetNumericProperty(const char* property, size_t* value) { 116 return false; 117} 118 119bool MallocExtension::SetNumericProperty(const char* property, size_t value) { 120 return false; 121} 122 123void MallocExtension::GetStats(char* buffer, int length) { 124 assert(length > 0); 125 buffer[0] = '\0'; 126} 127 128bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total, 129 int histogram[kMallocHistogramSize]) { 130 *blocks = 0; 131 *total = 0; 132 memset(histogram, 0, sizeof(*histogram) * kMallocHistogramSize); 133 return true; 134} 135 136void** MallocExtension::ReadStackTraces(int* sample_period) { 137 return NULL; 138} 139 140void** MallocExtension::ReadHeapGrowthStackTraces() { 141 return NULL; 142} 143 144void MallocExtension::MarkThreadIdle() { 145 // Default implementation does nothing 146} 147 148void MallocExtension::MarkThreadBusy() { 149 // Default implementation does nothing 150} 151 152SysAllocator* MallocExtension::GetSystemAllocator() { 153 return NULL; 154} 155 156void MallocExtension::SetSystemAllocator(SysAllocator *a) { 157 // Default implementation does nothing 158} 159 160void MallocExtension::ReleaseToSystem(size_t num_bytes) { 161 // Default implementation does nothing 162} 163 164void MallocExtension::ReleaseFreeMemory() { 165 ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX 166} 167 168void MallocExtension::SetMemoryReleaseRate(double rate) { 169 // Default implementation does nothing 170} 171 172double MallocExtension::GetMemoryReleaseRate() { 173 return -1.0; 174} 175 176size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) { 177 return size; 178} 179 180size_t MallocExtension::GetAllocatedSize(const void* p) { 181 assert(GetOwnership(p) != kNotOwned); 182 return 0; 183} 184 185MallocExtension::Ownership MallocExtension::GetOwnership(const void* p) { 186 return kUnknownOwnership; 187} 188 189void MallocExtension::GetFreeListSizes( 190 vector<MallocExtension::FreeListInfo>* v) { 191 v->clear(); 192} 193 194// The current malloc extension object. 195 196static pthread_once_t module_init = PTHREAD_ONCE_INIT; 197static MallocExtension* current_instance = NULL; 198 199static void InitModule() { 200 current_instance = new MallocExtension; 201#ifndef NO_HEAP_CHECK 202 HeapLeakChecker::IgnoreObject(current_instance); 203#endif 204} 205 206MallocExtension* MallocExtension::instance() { 207 perftools_pthread_once(&module_init, InitModule); 208 return current_instance; 209} 210 211void MallocExtension::Register(MallocExtension* implementation) { 212 perftools_pthread_once(&module_init, InitModule); 213 // When running under valgrind, our custom malloc is replaced with 214 // valgrind's one and malloc extensions will not work. (Note: 215 // callers should be responsible for checking that they are the 216 // malloc that is really being run, before calling Register. This 217 // is just here as an extra sanity check.) 218 if (!RunningOnValgrind()) { 219 current_instance = implementation; 220 } 221} 222 223// ----------------------------------------------------------------------- 224// Heap sampling support 225// ----------------------------------------------------------------------- 226 227namespace { 228 229// Accessors 230uintptr_t Count(void** entry) { 231 return reinterpret_cast<uintptr_t>(entry[0]); 232} 233uintptr_t Size(void** entry) { 234 return reinterpret_cast<uintptr_t>(entry[1]); 235} 236uintptr_t Depth(void** entry) { 237 return reinterpret_cast<uintptr_t>(entry[2]); 238} 239void* PC(void** entry, int i) { 240 return entry[3+i]; 241} 242 243void PrintCountAndSize(MallocExtensionWriter* writer, 244 uintptr_t count, uintptr_t size) { 245 char buf[100]; 246 snprintf(buf, sizeof(buf), 247 "%6"PRIu64": %8"PRIu64" [%6"PRIu64": %8"PRIu64"] @", 248 static_cast<uint64>(count), 249 static_cast<uint64>(size), 250 static_cast<uint64>(count), 251 static_cast<uint64>(size)); 252 writer->append(buf, strlen(buf)); 253} 254 255void PrintHeader(MallocExtensionWriter* writer, 256 const char* label, void** entries) { 257 // Compute the total count and total size 258 uintptr_t total_count = 0; 259 uintptr_t total_size = 0; 260 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 261 total_count += Count(entry); 262 total_size += Size(entry); 263 } 264 265 const char* const kTitle = "heap profile: "; 266 writer->append(kTitle, strlen(kTitle)); 267 PrintCountAndSize(writer, total_count, total_size); 268 writer->append(" ", 1); 269 writer->append(label, strlen(label)); 270 writer->append("\n", 1); 271} 272 273void PrintStackEntry(MallocExtensionWriter* writer, void** entry) { 274 PrintCountAndSize(writer, Count(entry), Size(entry)); 275 276 for (int i = 0; i < Depth(entry); i++) { 277 char buf[32]; 278 snprintf(buf, sizeof(buf), " %p", PC(entry, i)); 279 writer->append(buf, strlen(buf)); 280 } 281 writer->append("\n", 1); 282} 283 284} 285 286void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) { 287 int sample_period = 0; 288 void** entries = ReadStackTraces(&sample_period); 289 if (entries == NULL) { 290 const char* const kErrorMsg = 291 "This malloc implementation does not support sampling.\n" 292 "As of 2005/01/26, only tcmalloc supports sampling, and\n" 293 "you are probably running a binary that does not use\n" 294 "tcmalloc.\n"; 295 writer->append(kErrorMsg, strlen(kErrorMsg)); 296 return; 297 } 298 299 char label[32]; 300 sprintf(label, "heap_v2/%d", sample_period); 301 PrintHeader(writer, label, entries); 302 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 303 PrintStackEntry(writer, entry); 304 } 305 delete[] entries; 306 307 DumpAddressMap(writer); 308} 309 310void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) { 311 void** entries = ReadHeapGrowthStackTraces(); 312 if (entries == NULL) { 313 const char* const kErrorMsg = 314 "This malloc implementation does not support " 315 "ReadHeapGrowthStackTraces().\n" 316 "As of 2005/09/27, only tcmalloc supports this, and you\n" 317 "are probably running a binary that does not use tcmalloc.\n"; 318 writer->append(kErrorMsg, strlen(kErrorMsg)); 319 return; 320 } 321 322 // Do not canonicalize the stack entries, so that we get a 323 // time-ordered list of stack traces, which may be useful if the 324 // client wants to focus on the latest stack traces. 325 PrintHeader(writer, "growth", entries); 326 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 327 PrintStackEntry(writer, entry); 328 } 329 delete[] entries; 330 331 DumpAddressMap(writer); 332} 333 334void MallocExtension::Ranges(void* arg, RangeFunction func) { 335 // No callbacks by default 336} 337 338// These are C shims that work on the current instance. 339 340#define C_SHIM(fn, retval, paramlist, arglist) \ 341 extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist { \ 342 return MallocExtension::instance()->fn arglist; \ 343 } 344 345C_SHIM(VerifyAllMemory, int, (void), ()); 346C_SHIM(VerifyNewMemory, int, (const void* p), (p)); 347C_SHIM(VerifyArrayNewMemory, int, (const void* p), (p)); 348C_SHIM(VerifyMallocMemory, int, (const void* p), (p)); 349C_SHIM(MallocMemoryStats, int, 350 (int* blocks, size_t* total, int histogram[kMallocHistogramSize]), 351 (blocks, total, histogram)); 352 353C_SHIM(GetStats, void, 354 (char* buffer, int buffer_length), (buffer, buffer_length)); 355C_SHIM(GetNumericProperty, int, 356 (const char* property, size_t* value), (property, value)); 357C_SHIM(SetNumericProperty, int, 358 (const char* property, size_t value), (property, value)); 359 360C_SHIM(MarkThreadIdle, void, (void), ()); 361C_SHIM(MarkThreadBusy, void, (void), ()); 362C_SHIM(ReleaseFreeMemory, void, (void), ()); 363C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes)); 364C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size)); 365C_SHIM(GetAllocatedSize, size_t, (const void* p), (p)); 366 367// Can't use the shim here because of the need to translate the enums. 368extern "C" 369MallocExtension_Ownership MallocExtension_GetOwnership(const void* p) { 370 return static_cast<MallocExtension_Ownership>( 371 MallocExtension::instance()->GetOwnership(p)); 372} 373