1// Copyright (c) 2012, 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 54#ifdef USE_TCMALLOC 55// Note that malloc_extension can be used without tcmalloc if gperftools' 56// heap-profiler is enabled without the tcmalloc memory allocator. 57#include "thread_cache.h" 58#endif 59 60using STL_NAMESPACE::string; 61using STL_NAMESPACE::vector; 62 63static void DumpAddressMap(string* result) { 64 *result += "\nMAPPED_LIBRARIES:\n"; 65 // We keep doubling until we get a fit 66 const size_t old_resultlen = result->size(); 67 for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) { 68 result->resize(old_resultlen + amap_size); 69 bool wrote_all = false; 70 const int bytes_written = 71 tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size, 72 &wrote_all); 73 if (wrote_all) { // we fit! 74 (*result)[old_resultlen + bytes_written] = '\0'; 75 result->resize(old_resultlen + bytes_written); 76 return; 77 } 78 } 79 result->reserve(old_resultlen); // just don't print anything 80} 81 82// Note: this routine is meant to be called before threads are spawned. 83void MallocExtension::Initialize() { 84 static bool initialize_called = false; 85 86 if (initialize_called) return; 87 initialize_called = true; 88 89#ifdef __GLIBC__ 90 // GNU libc++ versions 3.3 and 3.4 obey the environment variables 91 // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting 92 // one of these variables forces the STL default allocator to call 93 // new() or delete() for each allocation or deletion. Otherwise 94 // the STL allocator tries to avoid the high cost of doing 95 // allocations by pooling memory internally. However, tcmalloc 96 // does allocations really fast, especially for the types of small 97 // items one sees in STL, so it's better off just using us. 98 // TODO: control whether we do this via an environment variable? 99 setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/); 100 setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/); 101 102 // Now we need to make the setenv 'stick', which it may not do since 103 // the env is flakey before main() is called. But luckily stl only 104 // looks at this env var the first time it tries to do an alloc, and 105 // caches what it finds. So we just cause an stl alloc here. 106 string dummy("I need to be allocated"); 107 dummy += "!"; // so the definition of dummy isn't optimized out 108#endif /* __GLIBC__ */ 109} 110 111// SysAllocator implementation 112SysAllocator::~SysAllocator() {} 113 114// Default implementation -- does nothing 115MallocExtension::~MallocExtension() { } 116bool MallocExtension::VerifyAllMemory() { return true; } 117bool MallocExtension::VerifyNewMemory(const void* p) { return true; } 118bool MallocExtension::VerifyArrayNewMemory(const void* p) { return true; } 119bool MallocExtension::VerifyMallocMemory(const void* p) { return true; } 120 121bool MallocExtension::GetNumericProperty(const char* property, size_t* value) { 122 return false; 123} 124 125bool MallocExtension::SetNumericProperty(const char* property, size_t value) { 126 return false; 127} 128 129void MallocExtension::GetStats(char* buffer, int length) { 130 assert(length > 0); 131 buffer[0] = '\0'; 132} 133 134bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total, 135 int histogram[kMallocHistogramSize]) { 136 *blocks = 0; 137 *total = 0; 138 memset(histogram, 0, sizeof(*histogram) * kMallocHistogramSize); 139 return true; 140} 141 142void** MallocExtension::ReadStackTraces(int* sample_period) { 143 return NULL; 144} 145 146void** MallocExtension::ReadHeapGrowthStackTraces() { 147 return NULL; 148} 149 150void MallocExtension::MarkThreadIdle() { 151 // Default implementation does nothing 152} 153 154void MallocExtension::MarkThreadBusy() { 155 // Default implementation does nothing 156} 157 158SysAllocator* MallocExtension::GetSystemAllocator() { 159 return NULL; 160} 161 162void MallocExtension::SetSystemAllocator(SysAllocator *a) { 163 // Default implementation does nothing 164} 165 166void MallocExtension::ReleaseToSystem(size_t num_bytes) { 167 // Default implementation does nothing 168} 169 170void MallocExtension::ReleaseFreeMemory() { 171 ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX 172} 173 174void MallocExtension::SetMemoryReleaseRate(double rate) { 175 // Default implementation does nothing 176} 177 178double MallocExtension::GetMemoryReleaseRate() { 179 return -1.0; 180} 181 182size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) { 183 return size; 184} 185 186size_t MallocExtension::GetAllocatedSize(const void* p) { 187 assert(GetOwnership(p) != kNotOwned); 188 return 0; 189} 190 191MallocExtension::Ownership MallocExtension::GetOwnership(const void* p) { 192 return kUnknownOwnership; 193} 194 195void MallocExtension::GetFreeListSizes( 196 vector<MallocExtension::FreeListInfo>* v) { 197 v->clear(); 198} 199 200// The current malloc extension object. 201 202static pthread_once_t module_init = PTHREAD_ONCE_INIT; 203static MallocExtension* current_instance = NULL; 204 205static void InitModule() { 206 current_instance = new MallocExtension; 207#ifndef NO_HEAP_CHECK 208 HeapLeakChecker::IgnoreObject(current_instance); 209#endif 210} 211 212MallocExtension* MallocExtension::instance() { 213 perftools_pthread_once(&module_init, InitModule); 214 return current_instance; 215} 216 217void MallocExtension::Register(MallocExtension* implementation) { 218 perftools_pthread_once(&module_init, InitModule); 219 // When running under valgrind, our custom malloc is replaced with 220 // valgrind's one and malloc extensions will not work. (Note: 221 // callers should be responsible for checking that they are the 222 // malloc that is really being run, before calling Register. This 223 // is just here as an extra sanity check.) 224 if (!RunningOnValgrind()) { 225 current_instance = implementation; 226 } 227} 228 229unsigned int MallocExtension::GetBytesAllocatedOnCurrentThread() { 230 // This function is added in Chromium for profiling. 231#ifdef USE_TCMALLOC 232 // Note that malloc_extension can be used without tcmalloc if gperftools' 233 // heap-profiler is enabled without the tcmalloc memory allocator. 234 return tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread(); 235#else 236 return 0; 237#endif 238} 239 240// ----------------------------------------------------------------------- 241// Heap sampling support 242// ----------------------------------------------------------------------- 243 244namespace { 245 246// Accessors 247uintptr_t Count(void** entry) { 248 return reinterpret_cast<uintptr_t>(entry[0]); 249} 250uintptr_t Size(void** entry) { 251 return reinterpret_cast<uintptr_t>(entry[1]); 252} 253uintptr_t Depth(void** entry) { 254 return reinterpret_cast<uintptr_t>(entry[2]); 255} 256void* PC(void** entry, int i) { 257 return entry[3+i]; 258} 259 260void PrintCountAndSize(MallocExtensionWriter* writer, 261 uintptr_t count, uintptr_t size) { 262 char buf[100]; 263 snprintf(buf, sizeof(buf), 264 "%6" PRIu64 ": %8" PRIu64 " [%6" PRIu64 ": %8" PRIu64 "] @", 265 static_cast<uint64>(count), 266 static_cast<uint64>(size), 267 static_cast<uint64>(count), 268 static_cast<uint64>(size)); 269 writer->append(buf, strlen(buf)); 270} 271 272void PrintHeader(MallocExtensionWriter* writer, 273 const char* label, void** entries) { 274 // Compute the total count and total size 275 uintptr_t total_count = 0; 276 uintptr_t total_size = 0; 277 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 278 total_count += Count(entry); 279 total_size += Size(entry); 280 } 281 282 const char* const kTitle = "heap profile: "; 283 writer->append(kTitle, strlen(kTitle)); 284 PrintCountAndSize(writer, total_count, total_size); 285 writer->append(" ", 1); 286 writer->append(label, strlen(label)); 287 writer->append("\n", 1); 288} 289 290void PrintStackEntry(MallocExtensionWriter* writer, void** entry) { 291 PrintCountAndSize(writer, Count(entry), Size(entry)); 292 293 for (int i = 0; i < Depth(entry); i++) { 294 char buf[32]; 295 snprintf(buf, sizeof(buf), " %p", PC(entry, i)); 296 writer->append(buf, strlen(buf)); 297 } 298 writer->append("\n", 1); 299} 300 301} 302 303void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) { 304 int sample_period = 0; 305 void** entries = ReadStackTraces(&sample_period); 306 if (entries == NULL) { 307 const char* const kErrorMsg = 308 "This malloc implementation does not support sampling.\n" 309 "As of 2005/01/26, only tcmalloc supports sampling, and\n" 310 "you are probably running a binary that does not use\n" 311 "tcmalloc.\n"; 312 writer->append(kErrorMsg, strlen(kErrorMsg)); 313 return; 314 } 315 316 char label[32]; 317 sprintf(label, "heap_v2/%d", sample_period); 318 PrintHeader(writer, label, entries); 319 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 320 PrintStackEntry(writer, entry); 321 } 322 delete[] entries; 323 324 DumpAddressMap(writer); 325} 326 327void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) { 328 void** entries = ReadHeapGrowthStackTraces(); 329 if (entries == NULL) { 330 const char* const kErrorMsg = 331 "This malloc implementation does not support " 332 "ReadHeapGrowthStackTraces().\n" 333 "As of 2005/09/27, only tcmalloc supports this, and you\n" 334 "are probably running a binary that does not use tcmalloc.\n"; 335 writer->append(kErrorMsg, strlen(kErrorMsg)); 336 return; 337 } 338 339 // Do not canonicalize the stack entries, so that we get a 340 // time-ordered list of stack traces, which may be useful if the 341 // client wants to focus on the latest stack traces. 342 PrintHeader(writer, "growth", entries); 343 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { 344 PrintStackEntry(writer, entry); 345 } 346 delete[] entries; 347 348 DumpAddressMap(writer); 349} 350 351void MallocExtension::Ranges(void* arg, RangeFunction func) { 352 // No callbacks by default 353} 354 355// These are C shims that work on the current instance. 356 357#define C_SHIM(fn, retval, paramlist, arglist) \ 358 extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist { \ 359 return MallocExtension::instance()->fn arglist; \ 360 } 361 362C_SHIM(VerifyAllMemory, int, (void), ()); 363C_SHIM(VerifyNewMemory, int, (const void* p), (p)); 364C_SHIM(VerifyArrayNewMemory, int, (const void* p), (p)); 365C_SHIM(VerifyMallocMemory, int, (const void* p), (p)); 366C_SHIM(MallocMemoryStats, int, 367 (int* blocks, size_t* total, int histogram[kMallocHistogramSize]), 368 (blocks, total, histogram)); 369 370C_SHIM(GetStats, void, 371 (char* buffer, int buffer_length), (buffer, buffer_length)); 372C_SHIM(GetNumericProperty, int, 373 (const char* property, size_t* value), (property, value)); 374C_SHIM(SetNumericProperty, int, 375 (const char* property, size_t value), (property, value)); 376 377C_SHIM(MarkThreadIdle, void, (void), ()); 378C_SHIM(MarkThreadBusy, void, (void), ()); 379C_SHIM(ReleaseFreeMemory, void, (void), ()); 380C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes)); 381C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size)); 382C_SHIM(GetAllocatedSize, size_t, (const void* p), (p)); 383 384// Can't use the shim here because of the need to translate the enums. 385extern "C" 386MallocExtension_Ownership MallocExtension_GetOwnership(const void* p) { 387 return static_cast<MallocExtension_Ownership>( 388 MallocExtension::instance()->GetOwnership(p)); 389} 390