1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <dlfcn.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <stdbool.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/mman.h>
12#include <unistd.h>
13#include <unwind.h>
14
15#include "tools/android/heap_profiler/heap_profiler.h"
16
17#define HEAP_PROFILER_EXPORT __attribute__((visibility("default")))
18
19
20static inline __attribute__((always_inline))
21uint32_t get_backtrace(uintptr_t* frames, uint32_t max_depth);
22
23// Function pointers typedefs for the hooked symbols.
24typedef void* (*mmap_t)(void*, size_t, int, int, int, off_t);
25typedef void* (*mmap2_t)(void*, size_t, int, int, int, off_t);
26typedef void* (*mmap64_t)(void*, size_t, int, int, int, off64_t);
27typedef void* (*mremap_t)(void*, size_t, size_t, unsigned long);
28typedef int (*munmap_t)(void*, size_t);
29typedef void* (*malloc_t)(size_t);
30typedef void* (*calloc_t)(size_t, size_t);
31typedef void* (*realloc_t)(void*, size_t);
32typedef void (*free_t)(void*);
33
34// And their actual definitions.
35static mmap_t real_mmap;
36static mmap2_t real_mmap2;
37static mmap64_t real_mmap64;
38static mremap_t real_mremap;
39static munmap_t real_munmap;
40static malloc_t real_malloc;
41static calloc_t real_calloc;
42static realloc_t real_realloc;
43static free_t real_free;
44static int* has_forked_off_zygote;
45
46HEAP_PROFILER_EXPORT const HeapStats* heap_profiler_stats_for_tests;
47
48// +---------------------------------------------------------------------------+
49// + Initialization of heap_profiler and lookup of hooks' addresses            +
50// +---------------------------------------------------------------------------+
51__attribute__((constructor))
52static void initialize() {
53  real_mmap = (mmap_t) dlsym(RTLD_NEXT, "mmap");
54  real_mmap2 = (mmap_t) dlsym(RTLD_NEXT, "mmap2");
55  real_mmap64 = (mmap64_t) dlsym(RTLD_NEXT, "mmap64");
56  real_mremap = (mremap_t) dlsym(RTLD_NEXT, "mremap");
57  real_munmap = (munmap_t) dlsym(RTLD_NEXT, "munmap");
58  real_malloc = (malloc_t) dlsym(RTLD_NEXT, "malloc");
59  real_calloc = (calloc_t) dlsym(RTLD_NEXT, "calloc");
60  real_realloc = (realloc_t) dlsym(RTLD_NEXT, "realloc");
61  real_free = (free_t) dlsym(RTLD_NEXT, "free");
62
63  // gMallocLeakZygoteChild is an extra useful piece of information to have.
64  // When available, it tells whether we're in the zygote (=0) or forked (=1)
65  // a child off it. In the worst case it will be NULL and we'll just ignore it.
66  has_forked_off_zygote = (int*) dlsym(RTLD_NEXT, "gMallocLeakZygoteChild");
67
68  // Allocate room for the HeapStats area and initialize the heap profiler.
69  // Make an explicit map of /dev/zero (instead of MAP_ANONYMOUS), so that the
70  // heap_dump tool can easily spot the mapping in the target process.
71  int fd = open("/dev/zero", O_RDONLY);
72  if (fd < 0) {
73    abort();  // This world has gone wrong. Good night Vienna.
74  }
75
76  HeapStats* stats = (HeapStats*) real_mmap(
77      0, sizeof(HeapStats), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
78  heap_profiler_stats_for_tests = stats;
79  heap_profiler_init(stats);
80}
81
82static inline __attribute__((always_inline)) void unwind_and_record_alloc(
83    void* start, size_t size, uint32_t flags) {
84  const int errno_save = errno;
85  uintptr_t frames[HEAP_PROFILER_MAX_DEPTH];
86  const uint32_t depth = get_backtrace(frames, HEAP_PROFILER_MAX_DEPTH);
87  if (has_forked_off_zygote != NULL && *has_forked_off_zygote == 0)
88    flags |= HEAP_PROFILER_FLAGS_IN_ZYGOTE;
89  heap_profiler_alloc(start, size, frames, depth, flags);
90  errno = errno_save;
91}
92
93static inline __attribute__((always_inline)) void discard_alloc(
94    void* start, size_t size, uint32_t* old_flags) {
95  const int errno_save = errno;
96  heap_profiler_free(start, size, old_flags);
97  errno = errno_save;
98}
99
100// Flags are non-functional extra decorators that are made available to the
101// final heap_dump tool, to get more details about the source of the allocation.
102static uint32_t get_flags_for_mmap(int fd) {
103  return HEAP_PROFILER_FLAGS_MMAP | (fd ? HEAP_PROFILER_FLAGS_MMAP_FILE : 0);
104}
105
106// +---------------------------------------------------------------------------+
107// + Actual mmap/malloc hooks                                                  +
108// +---------------------------------------------------------------------------+
109HEAP_PROFILER_EXPORT void* mmap(
110    void* addr, size_t size, int prot, int flags, int fd, off_t offset)  {
111  void* ret = real_mmap(addr, size, prot, flags, fd, offset);
112  if (ret != MAP_FAILED)
113    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
114  return ret;
115}
116
117HEAP_PROFILER_EXPORT void* mmap2(
118    void* addr, size_t size, int prot, int flags, int fd, off_t pgoffset)  {
119  void* ret = real_mmap2(addr, size, prot, flags, fd, pgoffset);
120  if (ret != MAP_FAILED)
121    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
122  return ret;
123}
124
125HEAP_PROFILER_EXPORT void* mmap64(
126    void* addr, size_t size, int prot, int flags, int fd, off64_t offset) {
127  void* ret = real_mmap64(addr, size, prot, flags, fd, offset);
128  if (ret != MAP_FAILED)
129    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
130  return ret;
131}
132
133HEAP_PROFILER_EXPORT void* mremap(
134    void* addr, size_t oldlen, size_t newlen, unsigned long flags) {
135  void* ret = real_mremap(addr, oldlen, newlen, flags);
136  if (ret != MAP_FAILED) {
137    uint32_t flags = 0;
138    if (addr)
139      discard_alloc(addr, oldlen, &flags);
140    if (newlen > 0)
141      unwind_and_record_alloc(ret, newlen, flags);
142  }
143  return ret;
144}
145
146HEAP_PROFILER_EXPORT int munmap(void* ptr, size_t size) {
147  int ret = real_munmap(ptr, size);
148  discard_alloc(ptr, size, /*old_flags=*/NULL);
149  return ret;
150}
151
152HEAP_PROFILER_EXPORT void* malloc(size_t byte_count) {
153  void* ret = real_malloc(byte_count);
154  if (ret != NULL)
155    unwind_and_record_alloc(ret, byte_count, HEAP_PROFILER_FLAGS_MALLOC);
156  return ret;
157}
158
159HEAP_PROFILER_EXPORT void* calloc(size_t nmemb, size_t size) {
160  void* ret = real_calloc(nmemb, size);
161  if (ret != NULL)
162    unwind_and_record_alloc(ret, nmemb * size, HEAP_PROFILER_FLAGS_MALLOC);
163  return ret;
164}
165
166HEAP_PROFILER_EXPORT void* realloc(void* ptr, size_t size) {
167  void* ret = real_realloc(ptr, size);
168  uint32_t flags = 0;
169  if (ptr)
170    discard_alloc(ptr, 0, &flags);
171  if (ret != NULL)
172    unwind_and_record_alloc(ret, size, flags | HEAP_PROFILER_FLAGS_MALLOC);
173  return ret;
174}
175
176HEAP_PROFILER_EXPORT void free(void* ptr) {
177  real_free(ptr);
178  discard_alloc(ptr, 0, /*old_flags=*/NULL);
179}
180
181// +---------------------------------------------------------------------------+
182// + Stack unwinder                                                            +
183// +---------------------------------------------------------------------------+
184typedef struct {
185  uintptr_t* frames;
186  uint32_t frame_count;
187  uint32_t max_depth;
188  bool have_skipped_self;
189} stack_crawl_state_t;
190
191static _Unwind_Reason_Code unwind_fn(struct _Unwind_Context* ctx, void* arg) {
192  stack_crawl_state_t* state = (stack_crawl_state_t*) arg;
193  uintptr_t ip = _Unwind_GetIP(ctx);
194
195  if (ip != 0 && !state->have_skipped_self) {
196    state->have_skipped_self = true;
197    return _URC_NO_REASON;
198  }
199
200  state->frames[state->frame_count++] = ip;
201  return (state->frame_count >= state->max_depth) ?
202          _URC_END_OF_STACK : _URC_NO_REASON;
203}
204
205static uint32_t get_backtrace(uintptr_t* frames, uint32_t max_depth) {
206  stack_crawl_state_t state = {.frames = frames, .max_depth = max_depth};
207  _Unwind_Backtrace(unwind_fn, &state);
208  return state.frame_count;
209}
210