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 <gperftools/heap-profiler.h> | 58 #include <google/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 <gperftools/malloc_hook.h> | 66 #include <google/malloc_hook.h> |
67 #include <gperftools/malloc_extension.h> | 67 #include <google/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 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 static int64 last_dump_free = 0; // free_size when did we last dump | 177 static int64 last_dump_free = 0; // free_size when did we last dump |
178 static int64 high_water_mark = 0; // In-use-bytes at last high-water dump | 178 static int64 high_water_mark = 0; // In-use-bytes at last high-water dump |
179 static int64 last_dump_time = 0; // The time of the last dump | 179 static int64 last_dump_time = 0; // The time of the last dump |
180 | 180 |
181 static HeapProfileTable* heap_profile = NULL; // the heap profile table | 181 static HeapProfileTable* heap_profile = NULL; // the heap profile table |
182 | 182 |
183 //---------------------------------------------------------------------- | 183 //---------------------------------------------------------------------- |
184 // Profile generation | 184 // Profile generation |
185 //---------------------------------------------------------------------- | 185 //---------------------------------------------------------------------- |
186 | 186 |
| 187 enum AddOrRemove { ADD, REMOVE }; |
| 188 |
| 189 // Add or remove all MMap-allocated regions to/from *heap_profile. |
| 190 // Assumes heap_lock is held. |
| 191 static void AddRemoveMMapDataLocked(AddOrRemove mode) { |
| 192 RAW_DCHECK(heap_lock.IsHeld(), ""); |
| 193 if (!FLAGS_mmap_profile || !is_on) return; |
| 194 // MemoryRegionMap maintained all the data we need for all |
| 195 // mmap-like allocations, so we just use it here: |
| 196 MemoryRegionMap::LockHolder l; |
| 197 for (MemoryRegionMap::RegionIterator r = MemoryRegionMap::BeginRegionLocked(); |
| 198 r != MemoryRegionMap::EndRegionLocked(); ++r) { |
| 199 if (mode == ADD) { |
| 200 heap_profile->RecordAllocWithStack( |
| 201 reinterpret_cast<const void*>(r->start_addr), |
| 202 r->end_addr - r->start_addr, |
| 203 r->call_stack_depth, r->call_stack); |
| 204 } else { |
| 205 heap_profile->RecordFree(reinterpret_cast<void*>(r->start_addr)); |
| 206 } |
| 207 } |
| 208 } |
| 209 |
187 // Input must be a buffer of size at least 1MB. | 210 // Input must be a buffer of size at least 1MB. |
188 static char* DoGetHeapProfileLocked(char* buf, int buflen) { | 211 static char* DoGetHeapProfileLocked(char* buf, int buflen) { |
189 // We used to be smarter about estimating the required memory and | 212 // We used to be smarter about estimating the required memory and |
190 // then capping it to 1MB and generating the profile into that. | 213 // then capping it to 1MB and generating the profile into that. |
191 if (buf == NULL || buflen < 1) | 214 if (buf == NULL || buflen < 1) |
192 return NULL; | 215 return NULL; |
193 | 216 |
194 RAW_DCHECK(heap_lock.IsHeld(), ""); | 217 RAW_DCHECK(heap_lock.IsHeld(), ""); |
195 int bytes_written = 0; | 218 int bytes_written = 0; |
196 if (is_on) { | 219 if (is_on) { |
197 if (FLAGS_mmap_profile) { | 220 HeapProfileTable::Stats const stats = heap_profile->total(); |
198 heap_profile->RefreshMMapData(); | 221 (void)stats; // avoid an unused-variable warning in non-debug mode. |
199 } | 222 AddRemoveMMapDataLocked(ADD); |
200 bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1); | 223 bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1); |
201 if (FLAGS_mmap_profile) { | 224 // FillOrderedProfile should not reduce the set of active mmap-ed regions, |
202 heap_profile->ClearMMapData(); | 225 // hence MemoryRegionMap will let us remove everything we've added above: |
203 } | 226 AddRemoveMMapDataLocked(REMOVE); |
| 227 RAW_DCHECK(stats.Equivalent(heap_profile->total()), ""); |
| 228 // if this fails, we somehow removed by AddRemoveMMapDataLocked |
| 229 // more than we have added. |
204 } | 230 } |
205 buf[bytes_written] = '\0'; | 231 buf[bytes_written] = '\0'; |
206 RAW_DCHECK(bytes_written == strlen(buf), ""); | 232 RAW_DCHECK(bytes_written == strlen(buf), ""); |
207 | 233 |
208 return buf; | 234 return buf; |
209 } | 235 } |
210 | 236 |
211 extern "C" char* GetHeapProfile() { | 237 extern "C" char* GetHeapProfile() { |
212 // Use normal malloc: we return the profile to the user to free it: | 238 // Use normal malloc: we return the profile to the user to free it: |
213 char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize)); | 239 char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize)); |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 last_dump_alloc = total.alloc_size; | 334 last_dump_alloc = total.alloc_size; |
309 last_dump_free = total.free_size; | 335 last_dump_free = total.free_size; |
310 if (inuse_bytes > high_water_mark) | 336 if (inuse_bytes > high_water_mark) |
311 high_water_mark = inuse_bytes; | 337 high_water_mark = inuse_bytes; |
312 } | 338 } |
313 } | 339 } |
314 } | 340 } |
315 | 341 |
316 // Record an allocation in the profile. | 342 // Record an allocation in the profile. |
317 static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) { | 343 static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) { |
318 // Take the stack trace outside the critical section. | |
319 void* stack[HeapProfileTable::kMaxStackDepth]; | |
320 int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack); | |
321 SpinLockHolder l(&heap_lock); | 344 SpinLockHolder l(&heap_lock); |
322 if (is_on) { | 345 if (is_on) { |
323 heap_profile->RecordAlloc(ptr, bytes, depth, stack); | 346 heap_profile->RecordAlloc(ptr, bytes, skip_count + 1); |
324 MaybeDumpProfileLocked(); | 347 MaybeDumpProfileLocked(); |
325 } | 348 } |
326 } | 349 } |
327 | 350 |
328 // Record a deallocation in the profile. | 351 // Record a deallocation in the profile. |
329 static void RecordFree(const void* ptr) { | 352 static void RecordFree(const void* ptr) { |
330 SpinLockHolder l(&heap_lock); | 353 SpinLockHolder l(&heap_lock); |
331 if (is_on) { | 354 if (is_on) { |
332 heap_profile->RecordFree(ptr); | 355 heap_profile->RecordFree(ptr); |
333 MaybeDumpProfileLocked(); | 356 MaybeDumpProfileLocked(); |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
564 | 587 |
565 // class used for finalization -- dumps the heap-profile at program exit | 588 // class used for finalization -- dumps the heap-profile at program exit |
566 struct HeapProfileEndWriter { | 589 struct HeapProfileEndWriter { |
567 ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); } | 590 ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); } |
568 }; | 591 }; |
569 | 592 |
570 // We want to make sure tcmalloc is up and running before starting the profiler | 593 // We want to make sure tcmalloc is up and running before starting the profiler |
571 static const TCMallocGuard tcmalloc_initializer; | 594 static const TCMallocGuard tcmalloc_initializer; |
572 REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit()); | 595 REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit()); |
573 static HeapProfileEndWriter heap_profile_end_writer; | 596 static HeapProfileEndWriter heap_profile_end_writer; |
OLD | NEW |