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 |