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
116using STL_NAMESPACE::string;
117using 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
126DEFINE_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.");
131DEFINE_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.
138DEFINE_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.");
143DEFINE_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.");
147DEFINE_bool(mmap_log,
148            EnvToBool(HEAP_PROFILE_MMAP_LOG, false),
149            "Should mmap/munmap calls be logged?");
150DEFINE_bool(mmap_profile,
151            EnvToBool(HEAP_PROFILE_MMAP, false),
152            "If heap-profiling is on, also profile mmap, mremap, and sbrk)");
153DEFINE_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");
157DEFINE_bool(deep_heap_profile,
158            EnvToBool(DEEP_HEAP_PROFILE, false),
159            "If heap-profiling is on, profile deeper (Linux and Android)");
160DEFINE_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)
165DEFINE_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.
181static SpinLock heap_lock(SpinLock::LINKER_INITIALIZED);
182
183//----------------------------------------------------------------------
184// Simple allocator for heap profiler's internal memory
185//----------------------------------------------------------------------
186
187static LowLevelAlloc::Arena *heap_profiler_memory;
188
189static void* ProfilerMalloc(size_t bytes) {
190  return LowLevelAlloc::AllocWithArena(bytes, heap_profiler_memory);
191}
192static void ProfilerFree(void* p) {
193  LowLevelAlloc::Free(p);
194}
195
196// We use buffers of this size in DoGetHeapProfile.
197static 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.
204static 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.
212static bool  is_on = false;           // If are on as a subsytem.
213static bool  dumping = false;         // Dumping status to prevent recursion
214static char* filename_prefix = NULL;  // Prefix used for profile file names
215                                      // (NULL if no need for dumping yet)
216static int   dump_count = 0;          // How many dumps so far
217static int64 last_dump_alloc = 0;     // alloc_size when did we last dump
218static int64 last_dump_free = 0;      // free_size when did we last dump
219static int64 high_water_mark = 0;     // In-use-bytes at last high-water dump
220static int64 last_dump_time = 0;      // The time of the last dump
221
222static HeapProfileTable* heap_profile = NULL;  // the heap profile table
223static 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.
227static 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.
235static 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
259extern "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
267static void NewHook(const void* ptr, size_t size);
268static void DeleteHook(const void* ptr);
269
270// Helper for HeapProfilerDump.
271static 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.
332static 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.
379static 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.
391static 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
404void NewHook(const void* ptr, size_t size) {
405  if (ptr != NULL) RecordAlloc(ptr, size, 0);
406}
407
408// static
409void 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
415static 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
421static 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
438static 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
457static 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
470static 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
484extern "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
560extern "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
570extern "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
578extern "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
583extern "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
631extern "C" void HeapProfilerDump(const char* reason) {
632  SpinLockHolder l(&heap_lock);
633  if (is_on && !dumping) {
634    DumpProfileLocked(reason);
635  }
636}
637
638extern "C" void HeapProfilerMarkBaseline() {
639  SpinLockHolder l(&heap_lock);
640
641  if (!is_on) return;
642
643  heap_profile->MarkCurrentAllocations(HeapProfileTable::MARK_ONE);
644}
645
646extern "C" void HeapProfilerMarkInteresting() {
647  SpinLockHolder l(&heap_lock);
648
649  if (!is_on) return;
650
651  heap_profile->MarkUnmarkedAllocations(HeapProfileTable::MARK_TWO);
652}
653
654extern "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
667static 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
688struct HeapProfileEndWriter {
689  ~HeapProfileEndWriter() { HeapProfilerDump("Exiting"); }
690};
691
692// We want to make sure tcmalloc is up and running before starting the profiler
693static const TCMallocGuard tcmalloc_initializer;
694REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit());
695static HeapProfileEndWriter heap_profile_end_writer;
696#endif   // defined(ENABLE_PROFILING)
697