1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <bionic/malloc.h>
18 #include <inttypes.h>
19 #include <malloc.h>
20 #include <private/bionic_malloc_dispatch.h>
21
22 #include <atomic>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/utils.h"
26 #include "perfetto/heap_profile.h"
27 #include "src/profiling/memory/heap_profile_internal.h"
28
29 #include "src/profiling/memory/wrap_allocators.h"
30
31 #pragma GCC visibility push(default)
32 extern "C" {
33
34 bool heapprofd_initialize(const MallocDispatch* malloc_dispatch,
35 bool* zygote_child,
36 const char* options);
37 void heapprofd_finalize();
38 void heapprofd_dump_heap(const char* file_name);
39 void heapprofd_get_malloc_leak_info(uint8_t** info,
40 size_t* overall_size,
41 size_t* info_size,
42 size_t* total_memory,
43 size_t* backtrace_size);
44 bool heapprofd_write_malloc_leak_info(FILE* fp);
45 ssize_t heapprofd_malloc_backtrace(void* pointer,
46 uintptr_t* frames,
47 size_t frame_count);
48 void heapprofd_free_malloc_leak_info(uint8_t* info);
49 size_t heapprofd_malloc_usable_size(void* pointer);
50 void* heapprofd_malloc(size_t size);
51 void heapprofd_free(void* pointer);
52 void* heapprofd_aligned_alloc(size_t alignment, size_t size);
53 void* heapprofd_memalign(size_t alignment, size_t bytes);
54 void* heapprofd_realloc(void* pointer, size_t bytes);
55 void* heapprofd_calloc(size_t nmemb, size_t bytes);
56 struct mallinfo heapprofd_mallinfo();
57 int heapprofd_mallopt(int param, int value);
58 int heapprofd_malloc_info(int options, FILE* fp);
59 int heapprofd_posix_memalign(void** memptr, size_t alignment, size_t size);
60 int heapprofd_malloc_iterate(uintptr_t base,
61 size_t size,
62 void (*callback)(uintptr_t base,
63 size_t size,
64 void* arg),
65 void* arg);
66 void heapprofd_malloc_disable();
67 void heapprofd_malloc_enable();
68
69 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
70 void* heapprofd_pvalloc(size_t bytes);
71 void* heapprofd_valloc(size_t size);
72 #endif
73 }
74 #pragma GCC visibility pop
75
76 namespace {
77
78 // The real malloc function pointers we get in initialize. Set once in the first
79 // initialize invocation, and never changed afterwards. Because bionic does a
80 // release write after initialization and an acquire read to retrieve the hooked
81 // malloc functions, we can use relaxed memory mode for both writing and
82 // reading.
83 std::atomic<const MallocDispatch*> g_dispatch{nullptr};
84
GetDispatch()85 const MallocDispatch* GetDispatch() {
86 return g_dispatch.load(std::memory_order_relaxed);
87 }
88
89 // Note: android_mallopt(M_RESET_HOOKS) is mutually exclusive with
90 // heapprofd_initialize. Concurrent calls get discarded, which might be our
91 // unpatching attempt if there is a concurrent re-initialization running due to
92 // a new signal.
ProfileDisabledCallback(void *,const AHeapProfileDisableCallbackInfo *)93 void ProfileDisabledCallback(void*, const AHeapProfileDisableCallbackInfo*) {
94 if (!android_mallopt(M_RESET_HOOKS, nullptr, 0))
95 PERFETTO_PLOG("Unpatching heapprofd hooks failed.");
96 }
97
98 uint32_t g_heap_id = AHeapProfile_registerHeap(
99 AHeapInfo_setDisabledCallback(AHeapInfo_create("libc.malloc"),
100 ProfileDisabledCallback,
101 nullptr));
102
103 } // namespace
104
105 // Setup for the rest of profiling. The first time profiling is triggered in a
106 // process, this is called after this client library is dlopened, but before the
107 // rest of the hooks are patched in. However, as we support multiple profiling
108 // sessions within a process' lifetime, this function can also be legitimately
109 // called any number of times afterwards (note: bionic guarantees that at most
110 // one initialize call is active at a time).
111 //
112 // Note: if profiling is triggered at runtime, this runs on a dedicated pthread
113 // (which is safe to block). If profiling is triggered at startup, then this
114 // code runs synchronously.
heapprofd_initialize(const MallocDispatch * malloc_dispatch,bool *,const char *)115 bool heapprofd_initialize(const MallocDispatch* malloc_dispatch,
116 bool*,
117 const char*) {
118 // Table of pointers to backing implementation.
119 g_dispatch.store(malloc_dispatch);
120 return AHeapProfile_initSession(malloc_dispatch->malloc,
121 malloc_dispatch->free);
122 }
123
heapprofd_finalize()124 void heapprofd_finalize() {
125 // At the time of writing, invoked only as an atexit handler. We don't have
126 // any specific action to take, and cleanup can be left to the OS.
127 }
128
heapprofd_malloc(size_t size)129 void* heapprofd_malloc(size_t size) {
130 return perfetto::profiling::wrap_malloc(g_heap_id, GetDispatch()->malloc,
131 size);
132 }
133
heapprofd_calloc(size_t nmemb,size_t size)134 void* heapprofd_calloc(size_t nmemb, size_t size) {
135 return perfetto::profiling::wrap_calloc(g_heap_id, GetDispatch()->calloc,
136 nmemb, size);
137 }
138
heapprofd_aligned_alloc(size_t alignment,size_t size)139 void* heapprofd_aligned_alloc(size_t alignment, size_t size) {
140 // aligned_alloc is the same as memalign.
141 return perfetto::profiling::wrap_memalign(
142 g_heap_id, GetDispatch()->aligned_alloc, alignment, size);
143 }
144
heapprofd_memalign(size_t alignment,size_t size)145 void* heapprofd_memalign(size_t alignment, size_t size) {
146 return perfetto::profiling::wrap_memalign(g_heap_id, GetDispatch()->memalign,
147 alignment, size);
148 }
149
heapprofd_posix_memalign(void ** memptr,size_t alignment,size_t size)150 int heapprofd_posix_memalign(void** memptr, size_t alignment, size_t size) {
151 return perfetto::profiling::wrap_posix_memalign(
152 g_heap_id, GetDispatch()->posix_memalign, memptr, alignment, size);
153 }
154
heapprofd_free(void * pointer)155 void heapprofd_free(void* pointer) {
156 return perfetto::profiling::wrap_free(g_heap_id, GetDispatch()->free,
157 pointer);
158 }
159
160 // Approach to recording realloc: under the initial lock, get a safe copy of the
161 // client, and make the sampling decision in advance. Then record the
162 // deallocation, call the real realloc, and finally record the sample if one is
163 // necessary.
164 //
165 // As with the free, we record the deallocation before calling the backing
166 // implementation to make sure the address is still exclusive while we're
167 // processing it.
heapprofd_realloc(void * pointer,size_t size)168 void* heapprofd_realloc(void* pointer, size_t size) {
169 return perfetto::profiling::wrap_realloc(g_heap_id, GetDispatch()->realloc,
170 pointer, size);
171 }
172
heapprofd_dump_heap(const char *)173 void heapprofd_dump_heap(const char*) {}
174
heapprofd_get_malloc_leak_info(uint8_t **,size_t *,size_t *,size_t *,size_t *)175 void heapprofd_get_malloc_leak_info(uint8_t**,
176 size_t*,
177 size_t*,
178 size_t*,
179 size_t*) {}
180
heapprofd_write_malloc_leak_info(FILE *)181 bool heapprofd_write_malloc_leak_info(FILE*) {
182 return false;
183 }
184
heapprofd_malloc_backtrace(void *,uintptr_t *,size_t)185 ssize_t heapprofd_malloc_backtrace(void*, uintptr_t*, size_t) {
186 return -1;
187 }
188
heapprofd_free_malloc_leak_info(uint8_t *)189 void heapprofd_free_malloc_leak_info(uint8_t*) {}
190
heapprofd_malloc_usable_size(void * pointer)191 size_t heapprofd_malloc_usable_size(void* pointer) {
192 const MallocDispatch* dispatch = GetDispatch();
193 return dispatch->malloc_usable_size(pointer);
194 }
195
heapprofd_mallinfo()196 struct mallinfo heapprofd_mallinfo() {
197 const MallocDispatch* dispatch = GetDispatch();
198 return dispatch->mallinfo();
199 }
200
heapprofd_mallopt(int param,int value)201 int heapprofd_mallopt(int param, int value) {
202 const MallocDispatch* dispatch = GetDispatch();
203 return dispatch->mallopt(param, value);
204 }
205
heapprofd_malloc_info(int options,FILE * fp)206 int heapprofd_malloc_info(int options, FILE* fp) {
207 const MallocDispatch* dispatch = GetDispatch();
208 return dispatch->malloc_info(options, fp);
209 }
210
heapprofd_malloc_iterate(uintptr_t base,size_t size,void (* callback)(uintptr_t base,size_t size,void * arg),void * arg)211 int heapprofd_malloc_iterate(uintptr_t base,
212 size_t size,
213 void (*callback)(uintptr_t base,
214 size_t size,
215 void* arg),
216 void* arg) {
217 const MallocDispatch* dispatch = GetDispatch();
218 return dispatch->malloc_iterate(base, size, callback, arg);
219 }
220
heapprofd_malloc_disable()221 void heapprofd_malloc_disable() {
222 const MallocDispatch* dispatch = GetDispatch();
223 return dispatch->malloc_disable();
224 }
225
heapprofd_malloc_enable()226 void heapprofd_malloc_enable() {
227 const MallocDispatch* dispatch = GetDispatch();
228 return dispatch->malloc_enable();
229 }
230
231 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
heapprofd_pvalloc(size_t size)232 void* heapprofd_pvalloc(size_t size) {
233 return perfetto::profiling::wrap_pvalloc(g_heap_id, GetDispatch()->pvalloc,
234 size);
235 }
236
heapprofd_valloc(size_t size)237 void* heapprofd_valloc(size_t size) {
238 return perfetto::profiling::wrap_valloc(g_heap_id, GetDispatch()->valloc,
239 size);
240 }
241
242 #endif
243