blob: fc65e15a9e607a98f4c69efd4865633af30ad1d5 [file] [log] [blame]
Christopher Ferris63860cb2015-11-16 17:30:32 -08001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29// Contains a thin layer that calls whatever real native allocator
30// has been defined. For the libc shared library, this allows the
31// implementation of a debug malloc that can intercept all of the allocation
32// calls and add special debugging code to attempt to catch allocation
33// errors. All of the debugging code is implemented in a separate shared
34// library that is only loaded when the property "libc.debug.malloc.options"
35// is set to a non-zero value. There are two functions exported to
36// allow ddms, or other external users to get information from the debug
37// allocation.
38// get_malloc_leak_info: Returns information about all of the known native
39// allocations that are currently in use.
40// free_malloc_leak_info: Frees the data allocated by the call to
41// get_malloc_leak_info.
Christopher Ferris2e1a40a2018-06-13 10:46:34 -070042// write_malloc_leak_info: Writes the leak info data to a file.
Christopher Ferris63860cb2015-11-16 17:30:32 -080043
Colin Cross869691c2016-01-29 12:48:18 -080044#include <pthread.h>
Florian Mayerf7f71e32018-08-31 15:36:48 -070045#include <stdatomic.h>
Colin Cross869691c2016-01-29 12:48:18 -080046
dimitry5332af62018-12-04 14:03:13 +010047#include <private/bionic_defs.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -080048#include <private/bionic_config.h>
49#include <private/bionic_globals.h>
Ryan Savitskiecc37e32018-12-14 15:57:21 +000050#include <private/bionic_malloc.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -080051#include <private/bionic_malloc_dispatch.h>
52
Evgenii Stepanovbe551f52018-08-13 16:46:15 -070053#if __has_feature(hwaddress_sanitizer)
54// FIXME: implement these in HWASan allocator.
55extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
56 void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
57 void* arg __unused) {
58 return 0;
59}
60
61extern "C" void __sanitizer_malloc_disable() {
62}
63
64extern "C" void __sanitizer_malloc_enable() {
65}
66#include <sanitizer/hwasan_interface.h>
67#define Malloc(function) __sanitizer_ ## function
68
69#else // __has_feature(hwaddress_sanitizer)
Christopher Ferris63860cb2015-11-16 17:30:32 -080070#include "jemalloc.h"
71#define Malloc(function) je_ ## function
Evgenii Stepanovbe551f52018-08-13 16:46:15 -070072#endif
Christopher Ferris63860cb2015-11-16 17:30:32 -080073
Florian Mayerf7f71e32018-08-31 15:36:48 -070074template <typename T>
75static T* RemoveConst(const T* x) {
76 return const_cast<T*>(x);
77}
78
79// RemoveConst is a workaround for bug in current libcxx. Fix in
80// https://reviews.llvm.org/D47613
81#define atomic_load_explicit_const(obj, order) atomic_load_explicit(RemoveConst(obj), order)
82
83static constexpr memory_order default_read_memory_order = memory_order_acquire;
84
Christopher Ferris63860cb2015-11-16 17:30:32 -080085static constexpr MallocDispatch __libc_malloc_default_dispatch
86 __attribute__((unused)) = {
87 Malloc(calloc),
88 Malloc(free),
89 Malloc(mallinfo),
90 Malloc(malloc),
91 Malloc(malloc_usable_size),
92 Malloc(memalign),
93 Malloc(posix_memalign),
94#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
95 Malloc(pvalloc),
96#endif
97 Malloc(realloc),
98#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
99 Malloc(valloc),
100#endif
Colin Cross869691c2016-01-29 12:48:18 -0800101 Malloc(iterate),
102 Malloc(malloc_disable),
103 Malloc(malloc_enable),
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700104 Malloc(mallopt),
Christopher Ferriscae21a92018-02-05 18:14:55 -0800105 Malloc(aligned_alloc),
Christopher Ferris63860cb2015-11-16 17:30:32 -0800106 };
107
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800108// Malloc hooks.
109void* (*volatile __malloc_hook)(size_t, const void*);
110void* (*volatile __realloc_hook)(void*, size_t, const void*);
111void (*volatile __free_hook)(void*, const void*);
112void* (*volatile __memalign_hook)(size_t, size_t, const void*);
113
Christopher Ferris63860cb2015-11-16 17:30:32 -0800114// In a VM process, this is set to 1 after fork()ing out of zygote.
115int gMallocLeakZygoteChild = 0;
116
117// =============================================================================
118// Allocation functions
119// =============================================================================
120extern "C" void* calloc(size_t n_elements, size_t elem_size) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700121 auto _calloc = atomic_load_explicit_const(
122 &__libc_globals->malloc_dispatch.calloc,
123 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800124 if (__predict_false(_calloc != nullptr)) {
125 return _calloc(n_elements, elem_size);
126 }
127 return Malloc(calloc)(n_elements, elem_size);
128}
129
130extern "C" void free(void* mem) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700131 auto _free = atomic_load_explicit_const(
132 &__libc_globals->malloc_dispatch.free,
133 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800134 if (__predict_false(_free != nullptr)) {
135 _free(mem);
136 } else {
137 Malloc(free)(mem);
138 }
139}
140
141extern "C" struct mallinfo mallinfo() {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700142 auto _mallinfo = atomic_load_explicit_const(
143 &__libc_globals->malloc_dispatch.mallinfo,
144 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800145 if (__predict_false(_mallinfo != nullptr)) {
146 return _mallinfo();
147 }
148 return Malloc(mallinfo)();
149}
150
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700151extern "C" int mallopt(int param, int value) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700152 auto _mallopt = atomic_load_explicit_const(
153 &__libc_globals->malloc_dispatch.mallopt,
154 default_read_memory_order);
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700155 if (__predict_false(_mallopt != nullptr)) {
156 return _mallopt(param, value);
157 }
158 return Malloc(mallopt)(param, value);
159}
160
Christopher Ferris63860cb2015-11-16 17:30:32 -0800161extern "C" void* malloc(size_t bytes) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700162 auto _malloc = atomic_load_explicit_const(
163 &__libc_globals->malloc_dispatch.malloc,
164 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800165 if (__predict_false(_malloc != nullptr)) {
166 return _malloc(bytes);
167 }
168 return Malloc(malloc)(bytes);
169}
170
171extern "C" size_t malloc_usable_size(const void* mem) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700172 auto _malloc_usable_size = atomic_load_explicit_const(
173 &__libc_globals->malloc_dispatch.malloc_usable_size,
174 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800175 if (__predict_false(_malloc_usable_size != nullptr)) {
176 return _malloc_usable_size(mem);
177 }
178 return Malloc(malloc_usable_size)(mem);
179}
180
181extern "C" void* memalign(size_t alignment, size_t bytes) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700182 auto _memalign = atomic_load_explicit_const(
183 &__libc_globals->malloc_dispatch.memalign,
184 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800185 if (__predict_false(_memalign != nullptr)) {
186 return _memalign(alignment, bytes);
187 }
188 return Malloc(memalign)(alignment, bytes);
189}
190
191extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700192 auto _posix_memalign = atomic_load_explicit_const(
193 &__libc_globals->malloc_dispatch.posix_memalign,
194 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800195 if (__predict_false(_posix_memalign != nullptr)) {
196 return _posix_memalign(memptr, alignment, size);
197 }
198 return Malloc(posix_memalign)(memptr, alignment, size);
199}
200
Christopher Ferriscae21a92018-02-05 18:14:55 -0800201extern "C" void* aligned_alloc(size_t alignment, size_t size) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700202 auto _aligned_alloc = atomic_load_explicit_const(
203 &__libc_globals->malloc_dispatch.aligned_alloc,
204 default_read_memory_order);
Christopher Ferriscae21a92018-02-05 18:14:55 -0800205 if (__predict_false(_aligned_alloc != nullptr)) {
206 return _aligned_alloc(alignment, size);
207 }
208 return Malloc(aligned_alloc)(alignment, size);
209}
210
Christopher Ferris63860cb2015-11-16 17:30:32 -0800211extern "C" void* realloc(void* old_mem, size_t bytes) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700212 auto _realloc = atomic_load_explicit_const(
213 &__libc_globals->malloc_dispatch.realloc,
214 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800215 if (__predict_false(_realloc != nullptr)) {
216 return _realloc(old_mem, bytes);
217 }
218 return Malloc(realloc)(old_mem, bytes);
219}
220
Elliott Hughesb1770852018-09-18 12:52:42 -0700221extern "C" void* reallocarray(void* old_mem, size_t item_count, size_t item_size) {
222 size_t new_size;
223 if (__builtin_mul_overflow(item_count, item_size, &new_size)) {
224 errno = ENOMEM;
225 return nullptr;
226 }
227 return realloc(old_mem, new_size);
228}
229
Christopher Ferris63860cb2015-11-16 17:30:32 -0800230#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
231extern "C" void* pvalloc(size_t bytes) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700232 auto _pvalloc = atomic_load_explicit_const(
233 &__libc_globals->malloc_dispatch.pvalloc,
234 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800235 if (__predict_false(_pvalloc != nullptr)) {
236 return _pvalloc(bytes);
237 }
238 return Malloc(pvalloc)(bytes);
239}
240
241extern "C" void* valloc(size_t bytes) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700242 auto _valloc = atomic_load_explicit_const(
243 &__libc_globals->malloc_dispatch.valloc,
244 default_read_memory_order);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800245 if (__predict_false(_valloc != nullptr)) {
246 return _valloc(bytes);
247 }
248 return Malloc(valloc)(bytes);
249}
250#endif
251
252// We implement malloc debugging only in libc.so, so the code below
253// must be excluded if we compile this file for static libc.a
254#if !defined(LIBC_STATIC)
255
256#include <dlfcn.h>
Florian Mayer4e28ea12018-11-22 17:34:34 +0000257#include <fcntl.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -0800258#include <stdio.h>
259#include <stdlib.h>
Florian Mayer4e28ea12018-11-22 17:34:34 +0000260#include <unistd.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -0800261
Christopher Ferris7a3681e2017-04-24 17:48:32 -0700262#include <async_safe/log.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -0800263#include <sys/system_properties.h>
264
265extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
266
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800267static const char* HOOKS_SHARED_LIB = "libc_malloc_hooks.so";
268static const char* HOOKS_PROPERTY_ENABLE = "libc.debug.hooks.enable";
269static const char* HOOKS_ENV_ENABLE = "LIBC_HOOKS_ENABLE";
270
Christopher Ferris63860cb2015-11-16 17:30:32 -0800271static const char* DEBUG_SHARED_LIB = "libc_malloc_debug.so";
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800272static const char* DEBUG_PROPERTY_OPTIONS = "libc.debug.malloc.options";
273static const char* DEBUG_PROPERTY_PROGRAM = "libc.debug.malloc.program";
274static const char* DEBUG_ENV_OPTIONS = "LIBC_DEBUG_MALLOC_OPTIONS";
Christopher Ferris63860cb2015-11-16 17:30:32 -0800275
Florian Mayerf7f71e32018-08-31 15:36:48 -0700276static const char* HEAPPROFD_SHARED_LIB = "heapprofd_client.so";
277static const char* HEAPPROFD_PREFIX = "heapprofd";
Florian Mayer0dbe6d12018-11-08 11:25:49 +0000278static const char* HEAPPROFD_PROPERTY_ENABLE = "heapprofd.enable";
Florian Mayerf7f71e32018-08-31 15:36:48 -0700279static const int HEAPPROFD_SIGNAL = __SIGRTMIN + 4;
280
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800281enum FunctionEnum : uint8_t {
282 FUNC_INITIALIZE,
283 FUNC_FINALIZE,
284 FUNC_GET_MALLOC_LEAK_INFO,
285 FUNC_FREE_MALLOC_LEAK_INFO,
286 FUNC_MALLOC_BACKTRACE,
Christopher Ferris2e1a40a2018-06-13 10:46:34 -0700287 FUNC_WRITE_LEAK_INFO,
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800288 FUNC_LAST,
289};
290static void* g_functions[FUNC_LAST];
Christopher Ferris63860cb2015-11-16 17:30:32 -0800291
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800292typedef void (*finalize_func_t)();
293typedef bool (*init_func_t)(const MallocDispatch*, int*, const char*);
294typedef void (*get_malloc_leak_info_func_t)(uint8_t**, size_t*, size_t*, size_t*, size_t*);
295typedef void (*free_malloc_leak_info_func_t)(uint8_t*);
Christopher Ferris2e1a40a2018-06-13 10:46:34 -0700296typedef bool (*write_malloc_leak_info_func_t)(FILE*);
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800297typedef ssize_t (*malloc_backtrace_func_t)(void*, uintptr_t*, size_t);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800298
299// =============================================================================
300// Log functions
301// =============================================================================
302#define error_log(format, ...) \
Christopher Ferris7a3681e2017-04-24 17:48:32 -0700303 async_safe_format_log(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
Christopher Ferris63860cb2015-11-16 17:30:32 -0800304#define info_log(format, ...) \
Christopher Ferris7a3681e2017-04-24 17:48:32 -0700305 async_safe_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
Christopher Ferris63860cb2015-11-16 17:30:32 -0800306// =============================================================================
307
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000308// In a Zygote child process, this is set to true if profiling of this process
309// is allowed. Note that this set at a later time than the above
310// gMallocLeakZygoteChild. The latter is set during the fork (while still in
311// zygote's SELinux domain). While this bit is set after the child is
312// specialized (and has transferred SELinux domains if applicable).
313static _Atomic bool gMallocZygoteChildProfileable = false;
314
Christopher Ferris63860cb2015-11-16 17:30:32 -0800315// =============================================================================
316// Exported for use by ddms.
317// =============================================================================
318
319// Retrieve native heap information.
320//
321// "*info" is set to a buffer we allocate
322// "*overall_size" is set to the size of the "info" buffer
323// "*info_size" is set to the size of a single entry
324// "*total_memory" is set to the sum of all allocations we're tracking; does
325// not include heap overhead
326// "*backtrace_size" is set to the maximum number of entries in the back trace
327extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size,
328 size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800329 void* func = g_functions[FUNC_GET_MALLOC_LEAK_INFO];
330 if (func == nullptr) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800331 return;
332 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800333 reinterpret_cast<get_malloc_leak_info_func_t>(func)(info, overall_size, info_size, total_memory,
334 backtrace_size);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800335}
336
337extern "C" void free_malloc_leak_info(uint8_t* info) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800338 void* func = g_functions[FUNC_FREE_MALLOC_LEAK_INFO];
339 if (func == nullptr) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800340 return;
341 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800342 reinterpret_cast<free_malloc_leak_info_func_t>(func)(info);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800343}
Colin Cross869691c2016-01-29 12:48:18 -0800344
Christopher Ferris2e1a40a2018-06-13 10:46:34 -0700345extern "C" void write_malloc_leak_info(FILE* fp) {
346 if (fp == nullptr) {
347 error_log("write_malloc_leak_info called with a nullptr");
348 return;
349 }
350
351 void* func = g_functions[FUNC_WRITE_LEAK_INFO];
352 bool written = false;
353 if (func != nullptr) {
354 written = reinterpret_cast<write_malloc_leak_info_func_t>(func)(fp);
355 }
356
357 if (!written) {
358 fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
359 fprintf(fp, "# adb shell stop\n");
360 fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
361 fprintf(fp, "# adb shell start\n");
362 }
363}
364
Christopher Ferris63860cb2015-11-16 17:30:32 -0800365// =============================================================================
366
367template<typename FunctionType>
Florian Mayerf7f71e32018-08-31 15:36:48 -0700368static bool InitMallocFunction(void* malloc_impl_handler, _Atomic(FunctionType)* func, const char* prefix, const char* suffix) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800369 char symbol[128];
370 snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
371 *func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
372 if (*func == nullptr) {
373 error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
374 return false;
375 }
376 return true;
377}
378
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800379static bool InitMallocFunctions(void* impl_handler, MallocDispatch* table, const char* prefix) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700380 if (!InitMallocFunction<MallocFree>(impl_handler, &table->free, prefix, "free")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800381 return false;
382 }
Florian Mayerf7f71e32018-08-31 15:36:48 -0700383 if (!InitMallocFunction<MallocCalloc>(impl_handler, &table->calloc, prefix, "calloc")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800384 return false;
385 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800386 if (!InitMallocFunction<MallocMallinfo>(impl_handler, &table->mallinfo, prefix, "mallinfo")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800387 return false;
388 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800389 if (!InitMallocFunction<MallocMallopt>(impl_handler, &table->mallopt, prefix, "mallopt")) {
Christopher Ferrisa1c0d2f2017-05-15 15:50:19 -0700390 return false;
391 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800392 if (!InitMallocFunction<MallocMalloc>(impl_handler, &table->malloc, prefix, "malloc")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800393 return false;
394 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800395 if (!InitMallocFunction<MallocMallocUsableSize>(impl_handler, &table->malloc_usable_size, prefix,
396 "malloc_usable_size")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800397 return false;
398 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800399 if (!InitMallocFunction<MallocMemalign>(impl_handler, &table->memalign, prefix, "memalign")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800400 return false;
401 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800402 if (!InitMallocFunction<MallocPosixMemalign>(impl_handler, &table->posix_memalign, prefix,
403 "posix_memalign")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800404 return false;
405 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800406 if (!InitMallocFunction<MallocAlignedAlloc>(impl_handler, &table->aligned_alloc,
Christopher Ferriscae21a92018-02-05 18:14:55 -0800407 prefix, "aligned_alloc")) {
408 return false;
409 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800410 if (!InitMallocFunction<MallocRealloc>(impl_handler, &table->realloc, prefix, "realloc")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800411 return false;
412 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800413 if (!InitMallocFunction<MallocIterate>(impl_handler, &table->iterate, prefix, "iterate")) {
Colin Cross869691c2016-01-29 12:48:18 -0800414 return false;
415 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800416 if (!InitMallocFunction<MallocMallocDisable>(impl_handler, &table->malloc_disable, prefix,
417 "malloc_disable")) {
Colin Cross869691c2016-01-29 12:48:18 -0800418 return false;
419 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800420 if (!InitMallocFunction<MallocMallocEnable>(impl_handler, &table->malloc_enable, prefix,
421 "malloc_enable")) {
Colin Cross869691c2016-01-29 12:48:18 -0800422 return false;
423 }
Christopher Ferris63860cb2015-11-16 17:30:32 -0800424#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800425 if (!InitMallocFunction<MallocPvalloc>(impl_handler, &table->pvalloc, prefix, "pvalloc")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800426 return false;
427 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800428 if (!InitMallocFunction<MallocValloc>(impl_handler, &table->valloc, prefix, "valloc")) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800429 return false;
430 }
431#endif
432
433 return true;
434}
435
436static void malloc_fini_impl(void*) {
437 // Our BSD stdio implementation doesn't close the standard streams,
438 // it only flushes them. Other unclosed FILE*s will show up as
439 // malloc leaks, but to avoid the standard streams showing up in
440 // leak reports, close them here.
441 fclose(stdin);
442 fclose(stdout);
443 fclose(stderr);
444
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800445 reinterpret_cast<finalize_func_t>(g_functions[FUNC_FINALIZE])();
446}
447
448static bool CheckLoadMallocHooks(char** options) {
449 char* env = getenv(HOOKS_ENV_ENABLE);
450 if ((env == nullptr || env[0] == '\0' || env[0] == '0') &&
451 (__system_property_get(HOOKS_PROPERTY_ENABLE, *options) == 0 || *options[0] == '\0' || *options[0] == '0')) {
452 return false;
453 }
454 *options = nullptr;
455 return true;
456}
457
458static bool CheckLoadMallocDebug(char** options) {
459 // If DEBUG_MALLOC_ENV_OPTIONS is set then it overrides the system properties.
460 char* env = getenv(DEBUG_ENV_OPTIONS);
461 if (env == nullptr || env[0] == '\0') {
462 if (__system_property_get(DEBUG_PROPERTY_OPTIONS, *options) == 0 || *options[0] == '\0') {
463 return false;
464 }
465
466 // Check to see if only a specific program should have debug malloc enabled.
467 char program[PROP_VALUE_MAX];
468 if (__system_property_get(DEBUG_PROPERTY_PROGRAM, program) != 0 &&
469 strstr(getprogname(), program) == nullptr) {
470 return false;
471 }
472 } else {
473 *options = env;
474 }
475 return true;
476}
477
Florian Mayer4e28ea12018-11-22 17:34:34 +0000478static bool GetHeapprofdProgramProperty(char* data, size_t size) {
479 constexpr char prefix[] = "heapprofd.enable.";
480 // - 1 to skip nullbyte, which we will write later.
481 constexpr size_t prefix_size = sizeof(prefix) - 1;
482 if (size < prefix_size) {
483 error_log("%s: Overflow constructing heapprofd property", getprogname());
484 return false;
485 }
486 memcpy(data, prefix, prefix_size);
487
488 int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
489 if (fd == -1) {
490 error_log("%s: Failed to open /proc/self/cmdline", getprogname());
491 return false;
492 }
493 char cmdline[128];
494 ssize_t rd = read(fd, cmdline, sizeof(cmdline) - 1);
495 close(fd);
496 if (rd == -1) {
497 error_log("%s: Failed to read /proc/self/cmdline", getprogname());
498 return false;
499 }
500 cmdline[rd] = '\0';
501 char* first_arg = static_cast<char*>(memchr(cmdline, '\0', rd));
502 if (first_arg == nullptr || first_arg == cmdline + size - 1) {
503 error_log("%s: Overflow reading cmdline", getprogname());
504 return false;
505 }
506 // For consistency with what we do with Java app cmdlines, trim everything
507 // after the @ sign of the first arg.
508 char* first_at = static_cast<char*>(memchr(cmdline, '@', rd));
509 if (first_at != nullptr && first_at < first_arg) {
510 *first_at = '\0';
511 first_arg = first_at;
512 }
513
514 char* start = static_cast<char*>(memrchr(cmdline, '/', first_arg - cmdline));
515 if (start == first_arg) {
516 // The first argument ended in a slash.
517 error_log("%s: cmdline ends in /", getprogname());
518 return false;
519 } else if (start == nullptr) {
520 start = cmdline;
521 } else {
522 // Skip the /.
523 start++;
524 }
525
526 size_t name_size = static_cast<size_t>(first_arg - start);
527 if (name_size >= size - prefix_size) {
528 error_log("%s: overflow constructing heapprofd property.", getprogname());
529 return false;
530 }
531 // + 1 to also copy the trailing null byte.
532 memcpy(data + prefix_size, start, name_size + 1);
533 return true;
534}
535
Florian Mayer0dbe6d12018-11-08 11:25:49 +0000536static bool CheckLoadHeapprofd() {
537 // First check for heapprofd.enable. If it is set to "all", enable
538 // heapprofd for all processes. Otherwise, check heapprofd.enable.${prog},
539 // if it is set and not 0, enable heap profiling for this process.
540 char property_value[PROP_VALUE_MAX];
541 if (__system_property_get(HEAPPROFD_PROPERTY_ENABLE, property_value) == 0) {
542 return false;
543 }
544 if (strcmp(property_value, "all") == 0) {
545 return true;
546 }
547
548 char program_property[128];
Florian Mayer4e28ea12018-11-22 17:34:34 +0000549 if (!GetHeapprofdProgramProperty(program_property,
550 sizeof(program_property))) {
Florian Mayer0dbe6d12018-11-08 11:25:49 +0000551 return false;
552 }
Florian Mayer0dbe6d12018-11-08 11:25:49 +0000553 if (__system_property_get(program_property, property_value) == 0) {
554 return false;
555 }
Florian Mayer0dbe6d12018-11-08 11:25:49 +0000556 return program_property[0] != '\0';
557}
558
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800559static void ClearGlobalFunctions() {
560 for (size_t i = 0; i < FUNC_LAST; i++) {
561 g_functions[i] = nullptr;
562 }
563}
564
Florian Mayerdb59b892018-11-27 17:06:54 +0000565static bool InitSharedLibrary(void* impl_handle, const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800566 static constexpr const char* names[] = {
567 "initialize",
568 "finalize",
569 "get_malloc_leak_info",
570 "free_malloc_leak_info",
571 "malloc_backtrace",
Christopher Ferris2e1a40a2018-06-13 10:46:34 -0700572 "write_malloc_leak_info",
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800573 };
574 for (size_t i = 0; i < FUNC_LAST; i++) {
575 char symbol[128];
576 snprintf(symbol, sizeof(symbol), "%s_%s", prefix, names[i]);
577 g_functions[i] = dlsym(impl_handle, symbol);
578 if (g_functions[i] == nullptr) {
579 error_log("%s: %s routine not found in %s", getprogname(), symbol, shared_lib);
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800580 ClearGlobalFunctions();
Florian Mayerdb59b892018-11-27 17:06:54 +0000581 return false;
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800582 }
583 }
584
585 if (!InitMallocFunctions(impl_handle, dispatch_table, prefix)) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800586 ClearGlobalFunctions();
Florian Mayerdb59b892018-11-27 17:06:54 +0000587 return false;
588 }
589 return true;
590}
591
592static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
593 void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
594 if (impl_handle == nullptr) {
595 error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800596 return nullptr;
597 }
598
Florian Mayerdb59b892018-11-27 17:06:54 +0000599 if (!InitSharedLibrary(impl_handle, shared_lib, prefix, dispatch_table)) {
600 dlclose(impl_handle);
601 impl_handle = nullptr;
602 }
603
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800604 return impl_handle;
Christopher Ferris63860cb2015-11-16 17:30:32 -0800605}
606
Florian Mayer176a4752018-10-23 11:48:34 +0100607// A function pointer to heapprofds init function. Used to re-initialize
608// heapprofd. This will start a new profiling session and tear down the old
609// one in case it is still active.
Florian Mayerdb59b892018-11-27 17:06:54 +0000610static _Atomic (void*) g_heapprofd_handle = nullptr;
Florian Mayer176a4752018-10-23 11:48:34 +0100611
Florian Mayerf7f71e32018-08-31 15:36:48 -0700612static void install_hooks(libc_globals* globals, const char* options,
613 const char* prefix, const char* shared_lib) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800614 MallocDispatch dispatch_table;
Florian Mayerdb59b892018-11-27 17:06:54 +0000615
616 void* impl_handle = atomic_load(&g_heapprofd_handle);
617 if (impl_handle != nullptr) {
618 if (!InitSharedLibrary(impl_handle, shared_lib, prefix, &dispatch_table)) {
619 return;
620 }
621 } else {
622 impl_handle = LoadSharedLibrary(shared_lib, prefix, &dispatch_table);
623 if (impl_handle == nullptr) {
624 return;
625 }
Christopher Ferris63860cb2015-11-16 17:30:32 -0800626 }
Florian Mayerdb59b892018-11-27 17:06:54 +0000627 init_func_t init_func = reinterpret_cast<init_func_t>(g_functions[FUNC_INITIALIZE]);
Tamas Berghammerac81fe82016-08-26 15:54:59 +0100628 if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) {
Florian Mayerdb59b892018-11-27 17:06:54 +0000629 error_log("%s: failed to enable malloc %s", getprogname(), prefix);
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800630 dlclose(impl_handle);
631 ClearGlobalFunctions();
Christopher Ferris63860cb2015-11-16 17:30:32 -0800632 return;
633 }
634
Florian Mayere965bcd2018-11-23 15:35:42 +0000635 // We assign free first explicitly to prevent the case where we observe a
636 // alloc, but miss the corresponding free because of initialization order.
637 //
638 // This is safer than relying on the declaration order inside
639 // MallocDispatch at the cost of an extra atomic pointer write on
640 // initialization.
641 atomic_store(&globals->malloc_dispatch.free, dispatch_table.free);
642 // The struct gets assigned elementwise and each of the elements is an
643 // _Atomic. Assigning to an _Atomic is an atomic_store operation.
644 // The assignment is done in declaration order.
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800645 globals->malloc_dispatch = dispatch_table;
Florian Mayerdb59b892018-11-27 17:06:54 +0000646 atomic_store(&g_heapprofd_handle, impl_handle);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800647
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800648 info_log("%s: malloc %s enabled", getprogname(), prefix);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800649
650 // Use atexit to trigger the cleanup function. This avoids a problem
651 // where another atexit function is used to cleanup allocated memory,
652 // but the finalize function was already called. This particular error
653 // seems to be triggered by a zygote spawned process calling exit.
654 int ret_value = __cxa_atexit(malloc_fini_impl, nullptr, nullptr);
655 if (ret_value != 0) {
656 error_log("failed to set atexit cleanup function: %d", ret_value);
657 }
658}
659
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000660// The logic for triggering heapprofd (at runtime) is as follows:
661// 1. HEAPPROFD_SIGNAL is received by the process, entering the
662// MaybeInstallInitHeapprofdHook signal handler.
663// 2. If the initialization is not already in flight
664// (g_heapprofd_init_in_progress is false), the malloc hook is set to
665// point at InitHeapprofdHook, and g_heapprofd_init_in_progress is set to
666// true.
667// 3. The next malloc call enters InitHeapprofdHook, which removes the malloc
668// hook, and spawns a detached pthread to run the InitHeapprofd task.
669// (g_heapprofd_init_hook_installed atomic is used to perform this once.)
670// 4. InitHeapprofd, on a dedicated pthread, loads the heapprofd client library,
671// installs the full set of heapprofd hooks, and invokes the client's
672// initializer. The dedicated pthread then terminates.
Florian Mayer176a4752018-10-23 11:48:34 +0100673// 5. g_heapprofd_init_in_progress and g_heapprofd_init_hook_installed are
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000674// reset to false such that heapprofd can be reinitialized. Reinitialization
675// means that a new profiling session is started, and any still active is
Florian Mayer176a4752018-10-23 11:48:34 +0100676// torn down.
Florian Mayerf7f71e32018-08-31 15:36:48 -0700677//
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000678// The incremental hooking and a dedicated task thread are used since we cannot
679// do heavy work within a signal handler, or when blocking a malloc invocation.
Florian Mayerf7f71e32018-08-31 15:36:48 -0700680
681static _Atomic bool g_heapprofd_init_in_progress = false;
Florian Mayer176a4752018-10-23 11:48:34 +0100682static _Atomic bool g_heapprofd_init_hook_installed = false;
Florian Mayerf7f71e32018-08-31 15:36:48 -0700683
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000684extern "C" void MaybeInstallInitHeapprofdHook(int);
Florian Mayer3a538a42018-12-20 11:23:50 +0000685
686// Initializes memory allocation framework once per process.
687static void malloc_init_impl(libc_globals* globals) {
688 struct sigaction action = {};
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000689 action.sa_handler = MaybeInstallInitHeapprofdHook;
Florian Mayer3a538a42018-12-20 11:23:50 +0000690 sigaction(HEAPPROFD_SIGNAL, &action, nullptr);
691
692 const char* prefix;
693 const char* shared_lib;
694 char prop[PROP_VALUE_MAX];
695 char* options = prop;
696 // Prefer malloc debug since it existed first and is a more complete
697 // malloc interceptor than the hooks.
698 if (CheckLoadMallocDebug(&options)) {
699 prefix = "debug";
700 shared_lib = DEBUG_SHARED_LIB;
701 } else if (CheckLoadMallocHooks(&options)) {
702 prefix = "hooks";
703 shared_lib = HOOKS_SHARED_LIB;
704 } else if (CheckLoadHeapprofd()) {
705 prefix = "heapprofd";
706 shared_lib = HEAPPROFD_SHARED_LIB;
707 } else {
708 return;
709 }
710 if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
711 install_hooks(globals, options, prefix, shared_lib);
712 atomic_store(&g_heapprofd_init_in_progress, false);
713 }
714}
715
716// Initializes memory allocation framework.
717// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
718__BIONIC_WEAK_FOR_NATIVE_BRIDGE
719__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
720 malloc_init_impl(globals);
721}
722
Florian Mayerf7f71e32018-08-31 15:36:48 -0700723static void* InitHeapprofd(void*) {
724 __libc_globals.mutate([](libc_globals* globals) {
725 install_hooks(globals, nullptr, HEAPPROFD_PREFIX, HEAPPROFD_SHARED_LIB);
726 });
727 atomic_store(&g_heapprofd_init_in_progress, false);
Florian Mayer176a4752018-10-23 11:48:34 +0100728 // Allow to install hook again to re-initialize heap profiling after the
729 // current session finished.
730 atomic_store(&g_heapprofd_init_hook_installed, false);
Florian Mayerf7f71e32018-08-31 15:36:48 -0700731 return nullptr;
732}
733
734static void* InitHeapprofdHook(size_t bytes) {
Florian Mayer176a4752018-10-23 11:48:34 +0100735 if (!atomic_exchange(&g_heapprofd_init_hook_installed, true)) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700736 __libc_globals.mutate([](libc_globals* globals) {
737 atomic_store(&globals->malloc_dispatch.malloc, nullptr);
738 });
739
740 pthread_t thread_id;
741 if (pthread_create(&thread_id, nullptr, InitHeapprofd, nullptr) == -1)
742 error_log("%s: heapprofd: failed to pthread_create.", getprogname());
743 else if (pthread_detach(thread_id) == -1)
744 error_log("%s: heapprofd: failed to pthread_detach", getprogname());
745 if (pthread_setname_np(thread_id, "heapprofdinit") == -1)
746 error_log("%s: heapprod: failed to pthread_setname_np", getprogname());
747 }
748 return Malloc(malloc)(bytes);
749}
750
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000751extern "C" void MaybeInstallInitHeapprofdHook(int) {
752 // Zygote child processes must be marked profileable.
753 if (gMallocLeakZygoteChild &&
754 !atomic_load_explicit_const(&gMallocZygoteChildProfileable, memory_order_acquire)) {
755 return;
756 }
757
Florian Mayerf7f71e32018-08-31 15:36:48 -0700758 if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
759 __libc_globals.mutate([](libc_globals* globals) {
Florian Mayere965bcd2018-11-23 15:35:42 +0000760 atomic_store(&globals->malloc_dispatch.malloc, InitHeapprofdHook);
Florian Mayerf7f71e32018-08-31 15:36:48 -0700761 });
762 }
763}
764
Christopher Ferris63860cb2015-11-16 17:30:32 -0800765#endif // !LIBC_STATIC
Colin Cross869691c2016-01-29 12:48:18 -0800766
767// =============================================================================
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000768// Platform-internal mallopt variant.
769// =============================================================================
770
771#if !defined(LIBC_STATIC)
Florian Mayerdb59b892018-11-27 17:06:54 +0000772bool MallocDispatchReset() {
773 if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
774 __libc_globals.mutate([](libc_globals* globals) {
775 globals->malloc_dispatch = __libc_malloc_default_dispatch;
776 });
777 atomic_store(&g_heapprofd_init_in_progress, false);
778 return true;
779 }
780 errno = EAGAIN;
781 return false;
782}
783
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000784// Marks this process as a profileable zygote child.
785bool HandleInitZygoteChildProfiling() {
786 atomic_store_explicit(&gMallocZygoteChildProfileable, true,
787 memory_order_release);
788
789 // Conditionally start "from startup" profiling.
790 if (CheckLoadHeapprofd()) {
791 // Directly call the signal handler (will correctly guard against
792 // concurrent signal delivery).
793 MaybeInstallInitHeapprofdHook(HEAPPROFD_SIGNAL);
794 }
795 return true;
796}
797
798#else
799
Florian Mayerdb59b892018-11-27 17:06:54 +0000800bool MallocDispatchReset() {
801 return true;
802}
803
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000804bool HandleInitZygoteChildProfiling() {
805 return true;
806}
807
808#endif // !defined(LIBC_STATIC)
809
810bool android_mallopt(int opcode, void* arg, size_t arg_size) {
811 if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
812 if (arg != nullptr || arg_size != 0) {
813 errno = EINVAL;
814 return false;
815 }
816 return HandleInitZygoteChildProfiling();
817 }
Florian Mayerdb59b892018-11-27 17:06:54 +0000818 if (opcode == M_RESET_HOOKS) {
819 if (arg != nullptr || arg_size != 0) {
820 errno = EINVAL;
821 return false;
822 }
823 return MallocDispatchReset();
824 }
Ryan Savitskiecc37e32018-12-14 15:57:21 +0000825
826 errno = ENOTSUP;
827 return false;
828}
829
830// =============================================================================
Colin Cross869691c2016-01-29 12:48:18 -0800831// Exported for use by libmemunreachable.
832// =============================================================================
833
834// Calls callback for every allocation in the anonymous heap mapping
835// [base, base+size). Must be called between malloc_disable and malloc_enable.
836extern "C" int malloc_iterate(uintptr_t base, size_t size,
837 void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700838 auto _iterate = atomic_load_explicit_const(
839 &__libc_globals->malloc_dispatch.iterate,
840 default_read_memory_order);
Colin Cross869691c2016-01-29 12:48:18 -0800841 if (__predict_false(_iterate != nullptr)) {
842 return _iterate(base, size, callback, arg);
843 }
844 return Malloc(iterate)(base, size, callback, arg);
845}
846
847// Disable calls to malloc so malloc_iterate gets a consistent view of
848// allocated memory.
849extern "C" void malloc_disable() {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700850 auto _malloc_disable = atomic_load_explicit_const(
851 & __libc_globals->malloc_dispatch.malloc_disable,
852 default_read_memory_order);
Colin Cross869691c2016-01-29 12:48:18 -0800853 if (__predict_false(_malloc_disable != nullptr)) {
854 return _malloc_disable();
855 }
856 return Malloc(malloc_disable)();
857}
858
859// Re-enable calls to malloc after a previous call to malloc_disable.
860extern "C" void malloc_enable() {
Florian Mayerf7f71e32018-08-31 15:36:48 -0700861 auto _malloc_enable = atomic_load_explicit_const(
862 &__libc_globals->malloc_dispatch.malloc_enable,
863 default_read_memory_order);
Colin Cross869691c2016-01-29 12:48:18 -0800864 if (__predict_false(_malloc_enable != nullptr)) {
865 return _malloc_enable();
866 }
867 return Malloc(malloc_enable)();
868}
Colin Cross2d4721c2016-02-02 11:57:54 -0800869
870#ifndef LIBC_STATIC
871extern "C" ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800872 void* func = g_functions[FUNC_MALLOC_BACKTRACE];
873 if (func == nullptr) {
Colin Cross2d4721c2016-02-02 11:57:54 -0800874 return 0;
875 }
Christopher Ferrisdb478a62018-02-07 18:42:14 -0800876 return reinterpret_cast<malloc_backtrace_func_t>(func)(pointer, frames, frame_count);
Colin Cross2d4721c2016-02-02 11:57:54 -0800877}
878#else
879extern "C" ssize_t malloc_backtrace(void*, uintptr_t*, size_t) {
880 return 0;
881}
882#endif