| OLD | NEW |
| (Empty) |
| 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 <stdio.h> | |
| 36 #include <string.h> | |
| 37 #include <stdio.h> | |
| 38 #if defined HAVE_STDINT_H | |
| 39 #include <stdint.h> | |
| 40 #elif defined HAVE_INTTYPES_H | |
| 41 #include <inttypes.h> | |
| 42 #else | |
| 43 #include <sys/types.h> | |
| 44 #endif | |
| 45 #include <string> | |
| 46 #include "base/dynamic_annotations.h" | |
| 47 #include "base/sysinfo.h" // for FillProcSelfMaps | |
| 48 #include "google/malloc_extension.h" | |
| 49 #include "maybe_threads.h" | |
| 50 | |
| 51 using STL_NAMESPACE::string; | |
| 52 | |
| 53 static void DumpAddressMap(string* result) { | |
| 54 *result += "\nMAPPED_LIBRARIES:\n"; | |
| 55 // We keep doubling until we get a fit | |
| 56 const size_t old_resultlen = result->size(); | |
| 57 for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) { | |
| 58 result->resize(old_resultlen + amap_size); | |
| 59 const int bytes_written = | |
| 60 tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size); | |
| 61 if (bytes_written < amap_size - 1) { // we fit! | |
| 62 (*result)[old_resultlen + bytes_written] = '\0'; | |
| 63 result->resize(old_resultlen + bytes_written); | |
| 64 return; | |
| 65 } | |
| 66 } | |
| 67 result->reserve(old_resultlen); // just don't print anything | |
| 68 } | |
| 69 | |
| 70 // Note: this routine is meant to be called before threads are spawned. | |
| 71 void MallocExtension::Initialize() { | |
| 72 static bool initialize_called = false; | |
| 73 | |
| 74 if (initialize_called) return; | |
| 75 initialize_called = true; | |
| 76 | |
| 77 #ifdef __GLIBC__ | |
| 78 // GNU libc++ versions 3.3 and 3.4 obey the environment variables | |
| 79 // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting | |
| 80 // one of these variables forces the STL default allocator to call | |
| 81 // new() or delete() for each allocation or deletion. Otherwise | |
| 82 // the STL allocator tries to avoid the high cost of doing | |
| 83 // allocations by pooling memory internally. However, tcmalloc | |
| 84 // does allocations really fast, especially for the types of small | |
| 85 // items one sees in STL, so it's better off just using us. | |
| 86 // TODO: control whether we do this via an environment variable? | |
| 87 setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/); | |
| 88 setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/); | |
| 89 | |
| 90 // Now we need to make the setenv 'stick', which it may not do since | |
| 91 // the env is flakey before main() is called. But luckily stl only | |
| 92 // looks at this env var the first time it tries to do an alloc, and | |
| 93 // caches what it finds. So we just cause an stl alloc here. | |
| 94 string dummy("I need to be allocated"); | |
| 95 dummy += "!"; // so the definition of dummy isn't optimized out | |
| 96 #endif /* __GLIBC__ */ | |
| 97 } | |
| 98 | |
| 99 // Default implementation -- does nothing | |
| 100 MallocExtension::~MallocExtension() { } | |
| 101 bool MallocExtension::VerifyAllMemory() { return true; } | |
| 102 bool MallocExtension::VerifyNewMemory(void* p) { return true; } | |
| 103 bool MallocExtension::VerifyArrayNewMemory(void* p) { return true; } | |
| 104 bool MallocExtension::VerifyMallocMemory(void* p) { return true; } | |
| 105 | |
| 106 bool MallocExtension::GetNumericProperty(const char* property, size_t* value) { | |
| 107 return false; | |
| 108 } | |
| 109 | |
| 110 bool MallocExtension::SetNumericProperty(const char* property, size_t value) { | |
| 111 return false; | |
| 112 } | |
| 113 | |
| 114 void MallocExtension::GetStats(char* buffer, int length) { | |
| 115 assert(length > 0); | |
| 116 buffer[0] = '\0'; | |
| 117 } | |
| 118 | |
| 119 bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total, | |
| 120 int histogram[kMallocHistogramSize]) { | |
| 121 *blocks = 0; | |
| 122 *total = 0; | |
| 123 memset(histogram, 0, sizeof(histogram)); | |
| 124 return true; | |
| 125 } | |
| 126 | |
| 127 void** MallocExtension::ReadStackTraces(int* sample_period) { | |
| 128 return NULL; | |
| 129 } | |
| 130 | |
| 131 void** MallocExtension::ReadHeapGrowthStackTraces() { | |
| 132 return NULL; | |
| 133 } | |
| 134 | |
| 135 void MallocExtension::MarkThreadIdle() { | |
| 136 // Default implementation does nothing | |
| 137 } | |
| 138 | |
| 139 void MallocExtension::ReleaseFreeMemory() { | |
| 140 // Default implementation does nothing | |
| 141 } | |
| 142 | |
| 143 void MallocExtension::Scavenge() { | |
| 144 // Default implementation does nothing | |
| 145 } | |
| 146 | |
| 147 void MallocExtension::SetMemoryReleaseRate(double rate) { | |
| 148 // Default implementation does nothing | |
| 149 } | |
| 150 | |
| 151 double MallocExtension::GetMemoryReleaseRate() { | |
| 152 return -1.0; | |
| 153 } | |
| 154 | |
| 155 size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) { | |
| 156 return size; | |
| 157 } | |
| 158 | |
| 159 size_t MallocExtension::GetAllocatedSize(void* p) { | |
| 160 return 0; | |
| 161 } | |
| 162 | |
| 163 // The current malloc extension object. We also keep a pointer to | |
| 164 // the default implementation so that the heap-leak checker does not | |
| 165 // complain about a memory leak. | |
| 166 | |
| 167 static pthread_once_t module_init = PTHREAD_ONCE_INIT; | |
| 168 static MallocExtension* default_instance = NULL; | |
| 169 static MallocExtension* current_instance = NULL; | |
| 170 | |
| 171 static void InitModule() { | |
| 172 default_instance = new MallocExtension; | |
| 173 current_instance = default_instance; | |
| 174 } | |
| 175 | |
| 176 MallocExtension* MallocExtension::instance() { | |
| 177 perftools_pthread_once(&module_init, InitModule); | |
| 178 return current_instance; | |
| 179 } | |
| 180 | |
| 181 void MallocExtension::Register(MallocExtension* implementation) { | |
| 182 perftools_pthread_once(&module_init, InitModule); | |
| 183 // When running under valgrind, our custom malloc is replaced with | |
| 184 // valgrind's one and malloc extensions will not work. | |
| 185 if (!RunningOnValgrind()) { | |
| 186 current_instance = implementation; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 // ----------------------------------------------------------------------- | |
| 191 // Heap sampling support | |
| 192 // ----------------------------------------------------------------------- | |
| 193 | |
| 194 namespace { | |
| 195 | |
| 196 // Accessors | |
| 197 uintptr_t Count(void** entry) { | |
| 198 return reinterpret_cast<uintptr_t>(entry[0]); | |
| 199 } | |
| 200 uintptr_t Size(void** entry) { | |
| 201 return reinterpret_cast<uintptr_t>(entry[1]); | |
| 202 } | |
| 203 uintptr_t Depth(void** entry) { | |
| 204 return reinterpret_cast<uintptr_t>(entry[2]); | |
| 205 } | |
| 206 void* PC(void** entry, int i) { | |
| 207 return entry[3+i]; | |
| 208 } | |
| 209 | |
| 210 void PrintCountAndSize(MallocExtensionWriter* writer, | |
| 211 uintptr_t count, uintptr_t size) { | |
| 212 char buf[100]; | |
| 213 snprintf(buf, sizeof(buf), | |
| 214 "%6lld: %8lld [%6lld: %8lld] @", | |
| 215 static_cast<long long>(count), | |
| 216 static_cast<long long>(size), | |
| 217 static_cast<long long>(count), | |
| 218 static_cast<long long>(size)); | |
| 219 writer->append(buf, strlen(buf)); | |
| 220 } | |
| 221 | |
| 222 void PrintHeader(MallocExtensionWriter* writer, | |
| 223 const char* label, void** entries) { | |
| 224 // Compute the total count and total size | |
| 225 uintptr_t total_count = 0; | |
| 226 uintptr_t total_size = 0; | |
| 227 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { | |
| 228 total_count += Count(entry); | |
| 229 total_size += Size(entry); | |
| 230 } | |
| 231 | |
| 232 const char* const kTitle = "heap profile: "; | |
| 233 writer->append(kTitle, strlen(kTitle)); | |
| 234 PrintCountAndSize(writer, total_count, total_size); | |
| 235 writer->append(" ", 1); | |
| 236 writer->append(label, strlen(label)); | |
| 237 writer->append("\n", 1); | |
| 238 } | |
| 239 | |
| 240 void PrintStackEntry(MallocExtensionWriter* writer, void** entry) { | |
| 241 PrintCountAndSize(writer, Count(entry), Size(entry)); | |
| 242 | |
| 243 for (int i = 0; i < Depth(entry); i++) { | |
| 244 char buf[32]; | |
| 245 snprintf(buf, sizeof(buf), " %p", PC(entry, i)); | |
| 246 writer->append(buf, strlen(buf)); | |
| 247 } | |
| 248 writer->append("\n", 1); | |
| 249 } | |
| 250 | |
| 251 } | |
| 252 | |
| 253 void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) { | |
| 254 int sample_period = 0; | |
| 255 void** entries = ReadStackTraces(&sample_period); | |
| 256 if (entries == NULL) { | |
| 257 const char* const kErrorMsg = | |
| 258 "This malloc implementation does not support sampling.\n" | |
| 259 "As of 2005/01/26, only tcmalloc supports sampling, and\n" | |
| 260 "you are probably running a binary that does not use\n" | |
| 261 "tcmalloc.\n"; | |
| 262 writer->append(kErrorMsg, strlen(kErrorMsg)); | |
| 263 return; | |
| 264 } | |
| 265 | |
| 266 char label[32]; | |
| 267 sprintf(label, "heap_v2/%d", sample_period); | |
| 268 PrintHeader(writer, label, entries); | |
| 269 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { | |
| 270 PrintStackEntry(writer, entry); | |
| 271 } | |
| 272 delete[] entries; | |
| 273 | |
| 274 DumpAddressMap(writer); | |
| 275 } | |
| 276 | |
| 277 void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) { | |
| 278 void** entries = ReadHeapGrowthStackTraces(); | |
| 279 if (entries == NULL) { | |
| 280 const char* const kErrorMsg = | |
| 281 "This malloc implementation does not support " | |
| 282 "ReadHeapGrowthStackTraces().\n" | |
| 283 "As of 2005/09/27, only tcmalloc supports this, and you\n" | |
| 284 "are probably running a binary that does not use tcmalloc.\n"; | |
| 285 writer->append(kErrorMsg, strlen(kErrorMsg)); | |
| 286 return; | |
| 287 } | |
| 288 | |
| 289 // Do not canonicalize the stack entries, so that we get a | |
| 290 // time-ordered list of stack traces, which may be useful if the | |
| 291 // client wants to focus on the latest stack traces. | |
| 292 PrintHeader(writer, "growth", entries); | |
| 293 for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) { | |
| 294 PrintStackEntry(writer, entry); | |
| 295 } | |
| 296 delete[] entries; | |
| 297 | |
| 298 DumpAddressMap(writer); | |
| 299 } | |
| 300 | |
| 301 // These are C shims that work on the current instance. | |
| 302 | |
| 303 #define C_SHIM(fn, retval, paramlist, arglist) \ | |
| 304 extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist { \ | |
| 305 return MallocExtension::instance()->fn arglist; \ | |
| 306 } | |
| 307 | |
| 308 C_SHIM(VerifyAllMemory, bool, (), ()); | |
| 309 C_SHIM(VerifyNewMemory, bool, (void* p), (p)); | |
| 310 C_SHIM(VerifyArrayNewMemory, bool, (void* p), (p)); | |
| 311 C_SHIM(VerifyMallocMemory, bool, (void* p), (p)); | |
| 312 C_SHIM(MallocMemoryStats, bool, | |
| 313 (int* blocks, size_t* total, int histogram[kMallocHistogramSize]), | |
| 314 (blocks, total, histogram)); | |
| 315 | |
| 316 C_SHIM(GetStats, void, | |
| 317 (char* buffer, int buffer_length), (buffer, buffer_length)); | |
| 318 C_SHIM(GetNumericProperty, bool, | |
| 319 (const char* property, size_t* value), (property, value)); | |
| 320 C_SHIM(SetNumericProperty, bool, | |
| 321 (const char* property, size_t value), (property, value)); | |
| 322 | |
| 323 C_SHIM(MarkThreadIdle, void, (), ()); | |
| 324 C_SHIM(ReleaseFreeMemory, void, (), ()); | |
| 325 C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size)); | |
| 326 C_SHIM(GetAllocatedSize, size_t, (void* p), (p)); | |
| OLD | NEW |