| OLD | NEW |
| 1 // Copyright (c) 2005, Google Inc. | 1 // Copyright (c) 2005, Google Inc. |
| 2 // All rights reserved. | 2 // All rights reserved. |
| 3 // | 3 // |
| 4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
| 6 // met: | 6 // met: |
| 7 // | 7 // |
| 8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 48 #ifdef HAVE_MMAP | 48 #ifdef HAVE_MMAP |
| 49 #include <sys/mman.h> | 49 #include <sys/mman.h> |
| 50 #endif | 50 #endif |
| 51 #include <errno.h> | 51 #include <errno.h> |
| 52 #include <assert.h> | 52 #include <assert.h> |
| 53 #include <sys/types.h> | 53 #include <sys/types.h> |
| 54 | 54 |
| 55 #include <algorithm> | 55 #include <algorithm> |
| 56 #include <string> | 56 #include <string> |
| 57 | 57 |
| 58 #include <google/heap-profiler.h> | 58 #include <gperftools/heap-profiler.h> |
| 59 | 59 |
| 60 #include "base/logging.h" | 60 #include "base/logging.h" |
| 61 #include "base/basictypes.h" // for PRId64, among other things | 61 #include "base/basictypes.h" // for PRId64, among other things |
| 62 #include "base/googleinit.h" | 62 #include "base/googleinit.h" |
| 63 #include "base/commandlineflags.h" | 63 #include "base/commandlineflags.h" |
| 64 #include "malloc_hook-inl.h" | 64 #include "malloc_hook-inl.h" |
| 65 #include "tcmalloc_guard.h" | 65 #include "tcmalloc_guard.h" |
| 66 #include <google/malloc_hook.h> | 66 #include <gperftools/malloc_hook.h> |
| 67 #include <google/malloc_extension.h> | 67 #include <gperftools/malloc_extension.h> |
| 68 #include "base/spinlock.h" | 68 #include "base/spinlock.h" |
| 69 #include "base/low_level_alloc.h" | 69 #include "base/low_level_alloc.h" |
| 70 #include "base/sysinfo.h" // for GetUniquePathFromEnv() | 70 #include "base/sysinfo.h" // for GetUniquePathFromEnv() |
| 71 #include "heap-profile-table.h" | 71 #include "heap-profile-table.h" |
| 72 #include "memory_region_map.h" | 72 #include "memory_region_map.h" |
| 73 | 73 |
| 74 | 74 |
| 75 #ifndef PATH_MAX | 75 #ifndef PATH_MAX |
| 76 #ifdef MAXPATHLEN | 76 #ifdef MAXPATHLEN |
| 77 #define PATH_MAX MAXPATHLEN | 77 #define PATH_MAX MAXPATHLEN |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 static int64 last_dump_alloc = 0; // alloc_size when did we last dump | 168 static int64 last_dump_alloc = 0; // alloc_size when did we last dump |
| 169 static int64 last_dump_free = 0; // free_size when did we last dump | 169 static int64 last_dump_free = 0; // free_size when did we last dump |
| 170 static int64 high_water_mark = 0; // In-use-bytes at last high-water dump | 170 static int64 high_water_mark = 0; // In-use-bytes at last high-water dump |
| 171 | 171 |
| 172 static HeapProfileTable* heap_profile = NULL; // the heap profile table | 172 static HeapProfileTable* heap_profile = NULL; // the heap profile table |
| 173 | 173 |
| 174 //---------------------------------------------------------------------- | 174 //---------------------------------------------------------------------- |
| 175 // Profile generation | 175 // Profile generation |
| 176 //---------------------------------------------------------------------- | 176 //---------------------------------------------------------------------- |
| 177 | 177 |
| 178 enum AddOrRemove { ADD, REMOVE }; | |
| 179 | |
| 180 // Add or remove all MMap-allocated regions to/from *heap_profile. | |
| 181 // Assumes heap_lock is held. | |
| 182 static void AddRemoveMMapDataLocked(AddOrRemove mode) { | |
| 183 RAW_DCHECK(heap_lock.IsHeld(), ""); | |
| 184 if (!FLAGS_mmap_profile || !is_on) return; | |
| 185 // MemoryRegionMap maintained all the data we need for all | |
| 186 // mmap-like allocations, so we just use it here: | |
| 187 MemoryRegionMap::LockHolder l; | |
| 188 for (MemoryRegionMap::RegionIterator r = MemoryRegionMap::BeginRegionLocked(); | |
| 189 r != MemoryRegionMap::EndRegionLocked(); ++r) { | |
| 190 if (mode == ADD) { | |
| 191 heap_profile->RecordAllocWithStack( | |
| 192 reinterpret_cast<const void*>(r->start_addr), | |
| 193 r->end_addr - r->start_addr, | |
| 194 r->call_stack_depth, r->call_stack); | |
| 195 } else { | |
| 196 heap_profile->RecordFree(reinterpret_cast<void*>(r->start_addr)); | |
| 197 } | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // Input must be a buffer of size at least 1MB. | 178 // Input must be a buffer of size at least 1MB. |
| 202 static char* DoGetHeapProfileLocked(char* buf, int buflen) { | 179 static char* DoGetHeapProfileLocked(char* buf, int buflen) { |
| 203 // We used to be smarter about estimating the required memory and | 180 // We used to be smarter about estimating the required memory and |
| 204 // then capping it to 1MB and generating the profile into that. | 181 // then capping it to 1MB and generating the profile into that. |
| 205 if (buf == NULL || buflen < 1) | 182 if (buf == NULL || buflen < 1) |
| 206 return NULL; | 183 return NULL; |
| 207 | 184 |
| 208 RAW_DCHECK(heap_lock.IsHeld(), ""); | 185 RAW_DCHECK(heap_lock.IsHeld(), ""); |
| 209 int bytes_written = 0; | 186 int bytes_written = 0; |
| 210 if (is_on) { | 187 if (is_on) { |
| 211 HeapProfileTable::Stats const stats = heap_profile->total(); | 188 if (FLAGS_mmap_profile) { |
| 212 (void)stats; // avoid an unused-variable warning in non-debug mode. | 189 heap_profile->RefreshMMapData(); |
| 213 AddRemoveMMapDataLocked(ADD); | 190 } |
| 214 bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1); | 191 bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1); |
| 215 // FillOrderedProfile should not reduce the set of active mmap-ed regions, | 192 if (FLAGS_mmap_profile) { |
| 216 // hence MemoryRegionMap will let us remove everything we've added above: | 193 heap_profile->ClearMMapData(); |
| 217 AddRemoveMMapDataLocked(REMOVE); | 194 } |
| 218 RAW_DCHECK(stats.Equivalent(heap_profile->total()), ""); | |
| 219 // if this fails, we somehow removed by AddRemoveMMapDataLocked | |
| 220 // more than we have added. | |
| 221 } | 195 } |
| 222 buf[bytes_written] = '\0'; | 196 buf[bytes_written] = '\0'; |
| 223 RAW_DCHECK(bytes_written == strlen(buf), ""); | 197 RAW_DCHECK(bytes_written == strlen(buf), ""); |
| 224 | 198 |
| 225 return buf; | 199 return buf; |
| 226 } | 200 } |
| 227 | 201 |
| 228 extern "C" char* GetHeapProfile() { | 202 extern "C" char* GetHeapProfile() { |
| 229 // Use normal malloc: we return the profile to the user to free it: | 203 // Use normal malloc: we return the profile to the user to free it: |
| 230 char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize)); | 204 char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize)); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 last_dump_alloc = total.alloc_size; | 291 last_dump_alloc = total.alloc_size; |
| 318 last_dump_free = total.free_size; | 292 last_dump_free = total.free_size; |
| 319 if (inuse_bytes > high_water_mark) | 293 if (inuse_bytes > high_water_mark) |
| 320 high_water_mark = inuse_bytes; | 294 high_water_mark = inuse_bytes; |
| 321 } | 295 } |
| 322 } | 296 } |
| 323 } | 297 } |
| 324 | 298 |
| 325 // Record an allocation in the profile. | 299 // Record an allocation in the profile. |
| 326 static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) { | 300 static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) { |
| 301 // Take the stack trace outside the critical section. |
| 302 void* stack[HeapProfileTable::kMaxStackDepth]; |
| 303 int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack); |
| 327 SpinLockHolder l(&heap_lock); | 304 SpinLockHolder l(&heap_lock); |
| 328 if (is_on) { | 305 if (is_on) { |
| 329 heap_profile->RecordAlloc(ptr, bytes, skip_count + 1); | 306 heap_profile->RecordAlloc(ptr, bytes, depth, stack); |
| 330 MaybeDumpProfileLocked(); | 307 MaybeDumpProfileLocked(); |
| 331 } | 308 } |
| 332 } | 309 } |
| 333 | 310 |
| 334 // Record a deallocation in the profile. | 311 // Record a deallocation in the profile. |
| 335 static void RecordFree(const void* ptr) { | 312 static void RecordFree(const void* ptr) { |
| 336 SpinLockHolder l(&heap_lock); | 313 SpinLockHolder l(&heap_lock); |
| 337 if (is_on) { | 314 if (is_on) { |
| 338 heap_profile->RecordFree(ptr); | 315 heap_profile->RecordFree(ptr); |
| 339 MaybeDumpProfileLocked(); | 316 MaybeDumpProfileLocked(); |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 | 546 |
| 570 // class used for finalization -- dumps the heap-profile at program exit | 547 // class used for finalization -- dumps the heap-profile at program exit |
| 571 struct HeapProfileEndWriter { | 548 struct HeapProfileEndWriter { |
| 572 ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); } | 549 ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); } |
| 573 }; | 550 }; |
| 574 | 551 |
| 575 // We want to make sure tcmalloc is up and running before starting the profiler | 552 // We want to make sure tcmalloc is up and running before starting the profiler |
| 576 static const TCMallocGuard tcmalloc_initializer; | 553 static const TCMallocGuard tcmalloc_initializer; |
| 577 REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit()); | 554 REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit()); |
| 578 static HeapProfileEndWriter heap_profile_end_writer; | 555 static HeapProfileEndWriter heap_profile_end_writer; |
| OLD | NEW |