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