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