• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
32 //
33 // TODO: Log large allocations
34 
35 #include <config.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #ifdef HAVE_INTTYPES_H
43 #include <inttypes.h>
44 #endif
45 #ifdef HAVE_FCNTL_H
46 #include <fcntl.h>    // for open()
47 #endif
48 #ifdef HAVE_MMAP
49 #include <sys/mman.h>
50 #endif
51 #include <errno.h>
52 #include <assert.h>
53 #include <sys/types.h>
54 
55 #include <algorithm>
56 #include <string>
57 
58 #include <gperftools/heap-profiler.h>
59 
60 #include "base/logging.h"
61 #include "base/basictypes.h"   // for PRId64, among other things
62 #include "base/googleinit.h"
63 #include "base/commandlineflags.h"
64 #include "malloc_hook-inl.h"
65 #include "tcmalloc_guard.h"
66 #include <gperftools/malloc_hook.h>
67 #include <gperftools/malloc_extension.h>
68 #include "base/spinlock.h"
69 #include "base/low_level_alloc.h"
70 #include "base/sysinfo.h"      // for GetUniquePathFromEnv()
71 #include "deep-heap-profile.h"
72 #include "heap-profile-table.h"
73 #include "memory_region_map.h"
74 
75 
76 #ifndef	PATH_MAX
77 #ifdef MAXPATHLEN
78 #define	PATH_MAX	MAXPATHLEN
79 #else
80 #define	PATH_MAX	4096         // seems conservative for max filename len!
81 #endif
82 #endif
83 
84 #if defined(__ANDROID__) || defined(ANDROID)
85 // On android, there are no environment variables.
86 // Instead, we use system properties, set via:
87 //   adb shell setprop prop_name prop_value
88 // From <sys/system_properties.h>,
89 //   PROP_NAME_MAX   32
90 //   PROP_VALUE_MAX  92
91 #define HEAPPROFILE "heapprof"
92 #define HEAP_PROFILE_ALLOCATION_INTERVAL "heapprof.allocation_interval"
93 #define HEAP_PROFILE_DEALLOCATION_INTERVAL "heapprof.deallocation_interval"
94 #define HEAP_PROFILE_INUSE_INTERVAL "heapprof.inuse_interval"
95 #define HEAP_PROFILE_TIME_INTERVAL "heapprof.time_interval"
96 #define HEAP_PROFILE_MMAP_LOG "heapprof.mmap_log"
97 #define HEAP_PROFILE_MMAP "heapprof.mmap"
98 #define HEAP_PROFILE_ONLY_MMAP "heapprof.only_mmap"
99 #define DEEP_HEAP_PROFILE "heapprof.deep_heap_profile"
100 #define DEEP_HEAP_PROFILE_PAGEFRAME "heapprof.deep.pageframe"
101 #define HEAP_PROFILE_TYPE_STATISTICS "heapprof.type_statistics"
102 #else  // defined(__ANDROID__) || defined(ANDROID)
103 #define HEAPPROFILE "HEAPPROFILE"
104 #define HEAP_PROFILE_ALLOCATION_INTERVAL "HEAP_PROFILE_ALLOCATION_INTERVAL"
105 #define HEAP_PROFILE_DEALLOCATION_INTERVAL "HEAP_PROFILE_DEALLOCATION_INTERVAL"
106 #define HEAP_PROFILE_INUSE_INTERVAL "HEAP_PROFILE_INUSE_INTERVAL"
107 #define HEAP_PROFILE_TIME_INTERVAL "HEAP_PROFILE_TIME_INTERVAL"
108 #define HEAP_PROFILE_MMAP_LOG "HEAP_PROFILE_MMAP_LOG"
109 #define HEAP_PROFILE_MMAP "HEAP_PROFILE_MMAP"
110 #define HEAP_PROFILE_ONLY_MMAP "HEAP_PROFILE_ONLY_MMAP"
111 #define DEEP_HEAP_PROFILE "DEEP_HEAP_PROFILE"
112 #define DEEP_HEAP_PROFILE_PAGEFRAME "DEEP_HEAP_PROFILE_PAGEFRAME"
113 #define HEAP_PROFILE_TYPE_STATISTICS "HEAP_PROFILE_TYPE_STATISTICS"
114 #endif  // defined(__ANDROID__) || defined(ANDROID)
115 
116 using STL_NAMESPACE::string;
117 using STL_NAMESPACE::sort;
118 
119 //----------------------------------------------------------------------
120 // Flags that control heap-profiling
121 //
122 // The thread-safety of the profiler depends on these being immutable
123 // after main starts, so don't change them.
124 //----------------------------------------------------------------------
125 
126 DEFINE_int64(heap_profile_allocation_interval,
127              EnvToInt64(HEAP_PROFILE_ALLOCATION_INTERVAL, 1 << 30 /*1GB*/),
128              "If non-zero, dump heap profiling information once every "
129              "specified number of bytes allocated by the program since "
130              "the last dump.");
131 DEFINE_int64(heap_profile_deallocation_interval,
132              EnvToInt64(HEAP_PROFILE_DEALLOCATION_INTERVAL, 0),
133              "If non-zero, dump heap profiling information once every "
134              "specified number of bytes deallocated by the program "
135              "since the last dump.");
136 // We could also add flags that report whenever inuse_bytes changes by
137 // X or -X, but there hasn't been a need for that yet, so we haven't.
138 DEFINE_int64(heap_profile_inuse_interval,
139              EnvToInt64(HEAP_PROFILE_INUSE_INTERVAL, 100 << 20 /*100MB*/),
140              "If non-zero, dump heap profiling information whenever "
141              "the high-water memory usage mark increases by the specified "
142              "number of bytes.");
143 DEFINE_int64(heap_profile_time_interval,
144              EnvToInt64(HEAP_PROFILE_TIME_INTERVAL, 0),
145              "If non-zero, dump heap profiling information once every "
146              "specified number of seconds since the last dump.");
147 DEFINE_bool(mmap_log,
148             EnvToBool(HEAP_PROFILE_MMAP_LOG, false),
149             "Should mmap/munmap calls be logged?");
150 DEFINE_bool(mmap_profile,
151             EnvToBool(HEAP_PROFILE_MMAP, false),
152             "If heap-profiling is on, also profile mmap, mremap, and sbrk)");
153 DEFINE_bool(only_mmap_profile,
154             EnvToBool(HEAP_PROFILE_ONLY_MMAP, false),
155             "If heap-profiling is on, only profile mmap, mremap, and sbrk; "
156             "do not profile malloc/new/etc");
157 DEFINE_bool(deep_heap_profile,
158             EnvToBool(DEEP_HEAP_PROFILE, false),
159             "If heap-profiling is on, profile deeper (Linux and Android)");
160 DEFINE_int32(deep_heap_profile_pageframe,
161              EnvToInt(DEEP_HEAP_PROFILE_PAGEFRAME, 0),
162              "Needs deeper profile. If 1, dump page frame numbers (PFNs). "
163              "If 2, dump page counts (/proc/kpagecount) with PFNs.");
164 #if defined(TYPE_PROFILING)
165 DEFINE_bool(heap_profile_type_statistics,
166             EnvToBool(HEAP_PROFILE_TYPE_STATISTICS, false),
167             "If heap-profiling is on, dump type statistics.");
168 #endif  // defined(TYPE_PROFILING)
169 
170 
171 //----------------------------------------------------------------------
172 // Locking
173 //----------------------------------------------------------------------
174 
175 // A pthread_mutex has way too much lock contention to be used here.
176 //
177 // I would like to use Mutex, but it can call malloc(),
178 // which can cause us to fall into an infinite recursion.
179 //
180 // So we use a simple spinlock.
181 static SpinLock heap_lock(SpinLock::LINKER_INITIALIZED);
182 
183 //----------------------------------------------------------------------
184 // Simple allocator for heap profiler's internal memory
185 //----------------------------------------------------------------------
186 
187 static LowLevelAlloc::Arena *heap_profiler_memory;
188 
ProfilerMalloc(size_t bytes)189 static void* ProfilerMalloc(size_t bytes) {
190   return LowLevelAlloc::AllocWithArena(bytes, heap_profiler_memory);
191 }
ProfilerFree(void * p)192 static void ProfilerFree(void* p) {
193   LowLevelAlloc::Free(p);
194 }
195 
196 // We use buffers of this size in DoGetHeapProfile.
197 static const int kProfileBufferSize = 1 << 20;
198 
199 // This is a last-ditch buffer we use in DumpProfileLocked in case we
200 // can't allocate more memory from ProfilerMalloc.  We expect this
201 // will be used by HeapProfileEndWriter when the application has to
202 // exit due to out-of-memory.  This buffer is allocated in
203 // HeapProfilerStart.  Access to this must be protected by heap_lock.
204 static char* global_profiler_buffer = NULL;
205 
206 
207 //----------------------------------------------------------------------
208 // Profiling control/state data
209 //----------------------------------------------------------------------
210 
211 // Access to all of these is protected by heap_lock.
212 static bool  is_on = false;           // If are on as a subsytem.
213 static bool  dumping = false;         // Dumping status to prevent recursion
214 static char* filename_prefix = NULL;  // Prefix used for profile file names
215                                       // (NULL if no need for dumping yet)
216 static int   dump_count = 0;          // How many dumps so far
217 static int64 last_dump_alloc = 0;     // alloc_size when did we last dump
218 static int64 last_dump_free = 0;      // free_size when did we last dump
219 static int64 high_water_mark = 0;     // In-use-bytes at last high-water dump
220 static int64 last_dump_time = 0;      // The time of the last dump
221 
222 static HeapProfileTable* heap_profile = NULL;  // the heap profile table
223 static DeepHeapProfile* deep_profile = NULL;  // deep memory profiler
224 
225 // Callback to generate a stack trace for an allocation. May be overriden
226 // by an application to provide its own pseudo-stacks.
227 static StackGeneratorFunction stack_generator_function =
228     HeapProfileTable::GetCallerStackTrace;
229 
230 //----------------------------------------------------------------------
231 // Profile generation
232 //----------------------------------------------------------------------
233 
234 // Input must be a buffer of size at least 1MB.
DoGetHeapProfileLocked(char * buf,int buflen)235 static char* DoGetHeapProfileLocked(char* buf, int buflen) {
236   // We used to be smarter about estimating the required memory and
237   // then capping it to 1MB and generating the profile into that.
238   if (buf == NULL || buflen < 1)
239     return NULL;
240 
241   RAW_DCHECK(heap_lock.IsHeld(), "");
242   int bytes_written = 0;
243   if (is_on) {
244     HeapProfileTable::Stats const stats = heap_profile->total();
245     (void)stats;   // avoid an unused-variable warning in non-debug mode.
246     bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1);
247     // FillOrderedProfile should not reduce the set of active mmap-ed regions,
248     // hence MemoryRegionMap will let us remove everything we've added above:
249     RAW_DCHECK(stats.Equivalent(heap_profile->total()), "");
250     // if this fails, we somehow removed by FillOrderedProfile
251     // more than we have added.
252   }
253   buf[bytes_written] = '\0';
254   RAW_DCHECK(bytes_written == strlen(buf), "");
255 
256   return buf;
257 }
258 
GetHeapProfile()259 extern "C" char* GetHeapProfile() {
260   // Use normal malloc: we return the profile to the user to free it:
261   char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize));
262   SpinLockHolder l(&heap_lock);
263   return DoGetHeapProfileLocked(buffer, kProfileBufferSize);
264 }
265 
266 // defined below
267 static void NewHook(const void* ptr, size_t size);
268 static void DeleteHook(const void* ptr);
269 
270 // Helper for HeapProfilerDump.
DumpProfileLocked(const char * reason)271 static void DumpProfileLocked(const char* reason) {
272   RAW_DCHECK(heap_lock.IsHeld(), "");
273   RAW_DCHECK(is_on, "");
274   RAW_DCHECK(!dumping, "");
275 
276   if (filename_prefix == NULL) return;  // we do not yet need dumping
277 
278   dumping = true;
279 
280   // Make file name
281   char file_name[1000];
282   dump_count++;
283   snprintf(file_name, sizeof(file_name), "%s.%05d.%04d%s",
284            filename_prefix, getpid(), dump_count, HeapProfileTable::kFileExt);
285 
286   // Dump the profile
287   RAW_VLOG(0, "Dumping heap profile to %s (%s)", file_name, reason);
288   // We must use file routines that don't access memory, since we hold
289   // a memory lock now.
290   RawFD fd = RawOpenForWriting(file_name);
291   if (fd == kIllegalRawFD) {
292     RAW_LOG(ERROR, "Failed dumping heap profile to %s", file_name);
293     dumping = false;
294     return;
295   }
296 
297   // This case may be impossible, but it's best to be safe.
298   // It's safe to use the global buffer: we're protected by heap_lock.
299   if (global_profiler_buffer == NULL) {
300     global_profiler_buffer =
301         reinterpret_cast<char*>(ProfilerMalloc(kProfileBufferSize));
302   }
303 
304   if (deep_profile) {
305     deep_profile->DumpOrderedProfile(reason, global_profiler_buffer,
306                                      kProfileBufferSize, fd);
307   } else {
308     char* profile = DoGetHeapProfileLocked(global_profiler_buffer,
309                                            kProfileBufferSize);
310     RawWrite(fd, profile, strlen(profile));
311   }
312   RawClose(fd);
313 
314 #if defined(TYPE_PROFILING)
315   if (FLAGS_heap_profile_type_statistics) {
316     snprintf(file_name, sizeof(file_name), "%s.%05d.%04d.type",
317              filename_prefix, getpid(), dump_count);
318     RAW_VLOG(0, "Dumping type statistics to %s", file_name);
319     heap_profile->DumpTypeStatistics(file_name);
320   }
321 #endif  // defined(TYPE_PROFILING)
322 
323   dumping = false;
324 }
325 
326 //----------------------------------------------------------------------
327 // Profile collection
328 //----------------------------------------------------------------------
329 
330 // Dump a profile after either an allocation or deallocation, if
331 // the memory use has changed enough since the last dump.
MaybeDumpProfileLocked()332 static void MaybeDumpProfileLocked() {
333   if (!dumping) {
334     const HeapProfileTable::Stats& total = heap_profile->total();
335     const int64 inuse_bytes = total.alloc_size - total.free_size;
336     bool need_to_dump = false;
337     char buf[128];
338     int64 current_time = time(NULL);
339     if (FLAGS_heap_profile_allocation_interval > 0 &&
340         total.alloc_size >=
341         last_dump_alloc + FLAGS_heap_profile_allocation_interval) {
342       snprintf(buf, sizeof(buf), ("%" PRId64 " MB allocated cumulatively, "
343                                   "%" PRId64 " MB currently in use"),
344                total.alloc_size >> 20, inuse_bytes >> 20);
345       need_to_dump = true;
346     } else if (FLAGS_heap_profile_deallocation_interval > 0 &&
347                total.free_size >=
348                last_dump_free + FLAGS_heap_profile_deallocation_interval) {
349       snprintf(buf, sizeof(buf), ("%" PRId64 " MB freed cumulatively, "
350                                   "%" PRId64 " MB currently in use"),
351                total.free_size >> 20, inuse_bytes >> 20);
352       need_to_dump = true;
353     } else if (FLAGS_heap_profile_inuse_interval > 0 &&
354                inuse_bytes >
355                high_water_mark + FLAGS_heap_profile_inuse_interval) {
356       snprintf(buf, sizeof(buf), "%" PRId64 " MB currently in use",
357                inuse_bytes >> 20);
358       need_to_dump = true;
359     } else if (FLAGS_heap_profile_time_interval > 0 &&
360                current_time - last_dump_time >=
361                FLAGS_heap_profile_time_interval) {
362       snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump",
363                current_time - last_dump_time);
364       need_to_dump = true;
365       last_dump_time = current_time;
366     }
367     if (need_to_dump) {
368       DumpProfileLocked(buf);
369 
370       last_dump_alloc = total.alloc_size;
371       last_dump_free = total.free_size;
372       if (inuse_bytes > high_water_mark)
373         high_water_mark = inuse_bytes;
374     }
375   }
376 }
377 
378 // Record an allocation in the profile.
RecordAlloc(const void * ptr,size_t bytes,int skip_count)379 static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) {
380   // Take the stack trace outside the critical section.
381   void* stack[HeapProfileTable::kMaxStackDepth];
382   int depth = stack_generator_function(skip_count + 1, stack);
383   SpinLockHolder l(&heap_lock);
384   if (is_on) {
385     heap_profile->RecordAlloc(ptr, bytes, depth, stack);
386     MaybeDumpProfileLocked();
387   }
388 }
389 
390 // Record a deallocation in the profile.
RecordFree(const void * ptr)391 static void RecordFree(const void* ptr) {
392   SpinLockHolder l(&heap_lock);
393   if (is_on) {
394     heap_profile->RecordFree(ptr);
395     MaybeDumpProfileLocked();
396   }
397 }
398 
399 //----------------------------------------------------------------------
400 // Allocation/deallocation hooks for MallocHook
401 //----------------------------------------------------------------------
402 
403 // static
NewHook(const void * ptr,size_t size)404 void NewHook(const void* ptr, size_t size) {
405   if (ptr != NULL) RecordAlloc(ptr, size, 0);
406 }
407 
408 // static
DeleteHook(const void * ptr)409 void DeleteHook(const void* ptr) {
410   if (ptr != NULL) RecordFree(ptr);
411 }
412 
413 // TODO(jandrews): Re-enable stack tracing
414 #ifdef TODO_REENABLE_STACK_TRACING
RawInfoStackDumper(const char * message,void *)415 static void RawInfoStackDumper(const char* message, void*) {
416   RAW_LOG(INFO, "%.*s", static_cast<int>(strlen(message) - 1), message);
417   // -1 is to chop the \n which will be added by RAW_LOG
418 }
419 #endif
420 
MmapHook(const void * result,const void * start,size_t size,int prot,int flags,int fd,off_t offset)421 static void MmapHook(const void* result, const void* start, size_t size,
422                      int prot, int flags, int fd, off_t offset) {
423   if (FLAGS_mmap_log) {  // log it
424     // We use PRIxS not just '%p' to avoid deadlocks
425     // in pretty-printing of NULL as "nil".
426     // TODO(maxim): instead should use a safe snprintf reimplementation
427     RAW_LOG(INFO,
428             "mmap(start=0x%" PRIxPTR ", len=%" PRIuS ", prot=0x%x, flags=0x%x, "
429             "fd=%d, offset=0x%x) = 0x%" PRIxPTR,
430             (uintptr_t) start, size, prot, flags, fd, (unsigned int) offset,
431             (uintptr_t) result);
432 #ifdef TODO_REENABLE_STACK_TRACING
433     DumpStackTrace(1, RawInfoStackDumper, NULL);
434 #endif
435   }
436 }
437 
MremapHook(const void * result,const void * old_addr,size_t old_size,size_t new_size,int flags,const void * new_addr)438 static void MremapHook(const void* result, const void* old_addr,
439                        size_t old_size, size_t new_size,
440                        int flags, const void* new_addr) {
441   if (FLAGS_mmap_log) {  // log it
442     // We use PRIxS not just '%p' to avoid deadlocks
443     // in pretty-printing of NULL as "nil".
444     // TODO(maxim): instead should use a safe snprintf reimplementation
445     RAW_LOG(INFO,
446             "mremap(old_addr=0x%" PRIxPTR ", old_size=%" PRIuS ", "
447             "new_size=%" PRIuS ", flags=0x%x, new_addr=0x%" PRIxPTR ") = "
448             "0x%" PRIxPTR,
449             (uintptr_t) old_addr, old_size, new_size, flags,
450             (uintptr_t) new_addr, (uintptr_t) result);
451 #ifdef TODO_REENABLE_STACK_TRACING
452     DumpStackTrace(1, RawInfoStackDumper, NULL);
453 #endif
454   }
455 }
456 
MunmapHook(const void * ptr,size_t size)457 static void MunmapHook(const void* ptr, size_t size) {
458   if (FLAGS_mmap_log) {  // log it
459     // We use PRIxS not just '%p' to avoid deadlocks
460     // in pretty-printing of NULL as "nil".
461     // TODO(maxim): instead should use a safe snprintf reimplementation
462     RAW_LOG(INFO, "munmap(start=0x%" PRIxPTR ", len=%" PRIuS ")",
463                   (uintptr_t) ptr, size);
464 #ifdef TODO_REENABLE_STACK_TRACING
465     DumpStackTrace(1, RawInfoStackDumper, NULL);
466 #endif
467   }
468 }
469 
SbrkHook(const void * result,ptrdiff_t increment)470 static void SbrkHook(const void* result, ptrdiff_t increment) {
471   if (FLAGS_mmap_log) {  // log it
472     RAW_LOG(INFO, "sbrk(inc=%" PRIdS ") = 0x%" PRIxPTR,
473                   increment, (uintptr_t) result);
474 #ifdef TODO_REENABLE_STACK_TRACING
475     DumpStackTrace(1, RawInfoStackDumper, NULL);
476 #endif
477   }
478 }
479 
480 //----------------------------------------------------------------------
481 // Starting/stopping/dumping
482 //----------------------------------------------------------------------
483 
HeapProfilerStart(const char * prefix)484 extern "C" void HeapProfilerStart(const char* prefix) {
485   SpinLockHolder l(&heap_lock);
486 
487   if (is_on) return;
488 
489   is_on = true;
490 
491   RAW_VLOG(0, "Starting tracking the heap");
492 
493   // This should be done before the hooks are set up, since it should
494   // call new, and we want that to be accounted for correctly.
495   MallocExtension::Initialize();
496 
497   if (FLAGS_only_mmap_profile) {
498     FLAGS_mmap_profile = true;
499   }
500 
501   if (FLAGS_mmap_profile) {
502     // Ask MemoryRegionMap to record all mmap, mremap, and sbrk
503     // call stack traces of at least size kMaxStackDepth:
504     MemoryRegionMap::Init(HeapProfileTable::kMaxStackDepth,
505                           /* use_buckets */ true);
506   }
507 
508   if (FLAGS_mmap_log) {
509     // Install our hooks to do the logging:
510     RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
511     RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
512     RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
513     RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
514   }
515 
516   heap_profiler_memory =
517     LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
518 
519   // Reserve space now for the heap profiler, so we can still write a
520   // heap profile even if the application runs out of memory.
521   global_profiler_buffer =
522       reinterpret_cast<char*>(ProfilerMalloc(kProfileBufferSize));
523 
524   heap_profile = new(ProfilerMalloc(sizeof(HeapProfileTable)))
525       HeapProfileTable(ProfilerMalloc, ProfilerFree, FLAGS_mmap_profile);
526 
527   last_dump_alloc = 0;
528   last_dump_free = 0;
529   high_water_mark = 0;
530   last_dump_time = 0;
531 
532   if (FLAGS_deep_heap_profile) {
533     // Initialize deep memory profiler
534     RAW_VLOG(0, "[%d] Starting a deep memory profiler", getpid());
535     deep_profile = new(ProfilerMalloc(sizeof(DeepHeapProfile)))
536         DeepHeapProfile(heap_profile, prefix, DeepHeapProfile::PageFrameType(
537             FLAGS_deep_heap_profile_pageframe));
538   }
539 
540   // We do not reset dump_count so if the user does a sequence of
541   // HeapProfilerStart/HeapProfileStop, we will get a continuous
542   // sequence of profiles.
543 
544   if (FLAGS_only_mmap_profile == false) {
545     // Now set the hooks that capture new/delete and malloc/free.
546     RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
547     RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
548   }
549 
550   // Copy filename prefix only if provided.
551   if (!prefix)
552     return;
553   RAW_DCHECK(filename_prefix == NULL, "");
554   const int prefix_length = strlen(prefix);
555   filename_prefix = reinterpret_cast<char*>(ProfilerMalloc(prefix_length + 1));
556   memcpy(filename_prefix, prefix, prefix_length);
557   filename_prefix[prefix_length] = '\0';
558 }
559 
HeapProfilerWithPseudoStackStart(StackGeneratorFunction callback)560 extern "C" void HeapProfilerWithPseudoStackStart(
561     StackGeneratorFunction callback) {
562   {
563     // Ensure the callback is set before allocations can be recorded.
564     SpinLockHolder l(&heap_lock);
565     stack_generator_function = callback;
566   }
567   HeapProfilerStart(NULL);
568 }
569 
IterateAllocatedObjects(AddressVisitor visitor,void * data)570 extern "C" void IterateAllocatedObjects(AddressVisitor visitor, void* data) {
571   SpinLockHolder l(&heap_lock);
572 
573   if (!is_on) return;
574 
575   heap_profile->IterateAllocationAddresses(visitor, data);
576 }
577 
IsHeapProfilerRunning()578 extern "C" int IsHeapProfilerRunning() {
579   SpinLockHolder l(&heap_lock);
580   return is_on ? 1 : 0;   // return an int, because C code doesn't have bool
581 }
582 
HeapProfilerStop()583 extern "C" void HeapProfilerStop() {
584   SpinLockHolder l(&heap_lock);
585 
586   if (!is_on) return;
587 
588   if (FLAGS_only_mmap_profile == false) {
589     // Unset our new/delete hooks, checking they were set:
590     RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
591     RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
592   }
593   if (FLAGS_mmap_log) {
594     // Restore mmap/sbrk hooks, checking that our hooks were set:
595     RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
596     RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
597     RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
598     RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
599   }
600 
601   if (deep_profile) {
602     // free deep memory profiler
603     deep_profile->~DeepHeapProfile();
604     ProfilerFree(deep_profile);
605     deep_profile = NULL;
606   }
607 
608   // free profile
609   heap_profile->~HeapProfileTable();
610   ProfilerFree(heap_profile);
611   heap_profile = NULL;
612 
613   // free output-buffer memory
614   ProfilerFree(global_profiler_buffer);
615 
616   // free prefix
617   ProfilerFree(filename_prefix);
618   filename_prefix = NULL;
619 
620   if (!LowLevelAlloc::DeleteArena(heap_profiler_memory)) {
621     RAW_LOG(FATAL, "Memory leak in HeapProfiler:");
622   }
623 
624   if (FLAGS_mmap_profile) {
625     MemoryRegionMap::Shutdown();
626   }
627 
628   is_on = false;
629 }
630 
HeapProfilerDump(const char * reason)631 extern "C" void HeapProfilerDump(const char* reason) {
632   SpinLockHolder l(&heap_lock);
633   if (is_on && !dumping) {
634     DumpProfileLocked(reason);
635   }
636 }
637 
HeapProfilerMarkBaseline()638 extern "C" void HeapProfilerMarkBaseline() {
639   SpinLockHolder l(&heap_lock);
640 
641   if (!is_on) return;
642 
643   heap_profile->MarkCurrentAllocations(HeapProfileTable::MARK_ONE);
644 }
645 
HeapProfilerMarkInteresting()646 extern "C" void HeapProfilerMarkInteresting() {
647   SpinLockHolder l(&heap_lock);
648 
649   if (!is_on) return;
650 
651   heap_profile->MarkUnmarkedAllocations(HeapProfileTable::MARK_TWO);
652 }
653 
HeapProfilerDumpAliveObjects(const char * filename)654 extern "C" void HeapProfilerDumpAliveObjects(const char* filename) {
655   SpinLockHolder l(&heap_lock);
656 
657   if (!is_on) return;
658 
659   heap_profile->DumpMarkedObjects(HeapProfileTable::MARK_TWO, filename);
660 }
661 
662 //----------------------------------------------------------------------
663 // Initialization/finalization code
664 //----------------------------------------------------------------------
665 #if defined(ENABLE_PROFILING)
666 // Initialization code
HeapProfilerInit()667 static void HeapProfilerInit() {
668   // Everything after this point is for setting up the profiler based on envvar
669   char fname[PATH_MAX];
670   if (!GetUniquePathFromEnv(HEAPPROFILE, fname)) {
671     return;
672   }
673   // We do a uid check so we don't write out files in a setuid executable.
674 #ifdef HAVE_GETEUID
675   if (getuid() != geteuid()) {
676     RAW_LOG(WARNING, ("HeapProfiler: ignoring " HEAPPROFILE " because "
677                       "program seems to be setuid\n"));
678     return;
679   }
680 #endif
681 
682   HeapProfileTable::CleanupOldProfiles(fname);
683 
684   HeapProfilerStart(fname);
685 }
686 
687 // class used for finalization -- dumps the heap-profile at program exit
688 struct HeapProfileEndWriter {
~HeapProfileEndWriterHeapProfileEndWriter689   ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); }
690 };
691 
692 // We want to make sure tcmalloc is up and running before starting the profiler
693 static const TCMallocGuard tcmalloc_initializer;
694 REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit());
695 static HeapProfileEndWriter heap_profile_end_writer;
696 #endif   // defined(ENABLE_PROFILING)
697