profiling: Add malloc hooks.
Change-Id: Iea9ab138810a2c38b092e3c4823e1b8a56fdf25f
diff --git a/Android.bp b/Android.bp
index 8e6341e..e7cd8f0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,78 @@
//
// This file is automatically generated by tools/gen_android_bp. Do not edit.
+// GN target: //:heapprofd
+cc_binary {
+ name: "heapprofd",
+ srcs: [
+ "src/base/event.cc",
+ "src/base/file_utils.cc",
+ "src/base/metatrace.cc",
+ "src/base/page_allocator.cc",
+ "src/base/string_splitter.cc",
+ "src/base/string_utils.cc",
+ "src/base/temp_file.cc",
+ "src/base/thread_checker.cc",
+ "src/base/time.cc",
+ "src/base/unix_socket.cc",
+ "src/base/unix_task_runner.cc",
+ "src/base/virtual_destructors.cc",
+ "src/base/watchdog_posix.cc",
+ "src/profiling/memory/bookkeeping.cc",
+ "src/profiling/memory/main.cc",
+ "src/profiling/memory/record_reader.cc",
+ "src/profiling/memory/socket_listener.cc",
+ "src/profiling/memory/string_interner.cc",
+ "src/profiling/memory/unwinding.cc",
+ "src/profiling/memory/wire_protocol.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprocinfo",
+ "libunwindstack",
+ ],
+ defaults: [
+ "perfetto_defaults",
+ ],
+}
+
+// GN target: //:heapprofd_client
+cc_library_shared {
+ name: "heapprofd_client",
+ srcs: [
+ "src/base/event.cc",
+ "src/base/file_utils.cc",
+ "src/base/metatrace.cc",
+ "src/base/page_allocator.cc",
+ "src/base/string_splitter.cc",
+ "src/base/string_utils.cc",
+ "src/base/temp_file.cc",
+ "src/base/thread_checker.cc",
+ "src/base/time.cc",
+ "src/base/unix_socket.cc",
+ "src/base/unix_task_runner.cc",
+ "src/base/virtual_destructors.cc",
+ "src/base/watchdog_posix.cc",
+ "src/profiling/memory/client.cc",
+ "src/profiling/memory/malloc_hooks.cc",
+ "src/profiling/memory/sampler.cc",
+ "src/profiling/memory/wire_protocol.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprocinfo",
+ "libunwindstack",
+ ],
+ defaults: [
+ "perfetto_defaults",
+ ],
+ include_dirs: [
+ "bionic/libc",
+ ],
+}
+
// GN target: //:libperfetto
cc_library_shared {
name: "libperfetto",
diff --git a/BUILD.gn b/BUILD.gn
index 15f2bd9..576f101 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -274,3 +274,25 @@
]
}
}
+
+if (!build_with_chromium) {
+ # TODO(fmayer): Investigate shared library for common pieces.
+ shared_library("heapprofd_client") {
+ deps = [
+ "src/profiling/memory:malloc_hooks",
+ ]
+ }
+
+ executable("heapprofd") {
+ deps = [
+ "gn:default_deps",
+ "src/base",
+ "src/base:unix_socket",
+ "src/profiling/memory:daemon",
+ "src/profiling/memory:wire_protocol",
+ ]
+ sources = [
+ "src/profiling/memory/main.cc",
+ ]
+ }
+}
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index d143901..871c99f 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -21,6 +21,7 @@
"../../../buildtools:libunwindstack",
"../../../gn:default_deps",
"../../base",
+ "../../base:unix_socket",
]
sources = [
"wire_protocol.cc",
@@ -35,7 +36,7 @@
"../../../buildtools:libunwindstack",
"../../../gn:default_deps",
"../../base",
- "../../ipc",
+ "../../base:unix_socket",
]
sources = [
"bookkeeping.cc",
@@ -59,6 +60,7 @@
"../../../buildtools:libunwindstack",
"../../../gn:default_deps",
"../../base",
+ "../../base:unix_socket",
]
sources = [
"client.cc",
@@ -94,15 +96,20 @@
]
}
-executable("heapprofd") {
+# This will export publicly visibile symbols for the malloc_hooks.
+source_set("malloc_hooks") {
deps = [
- ":daemon",
+ ":client",
":wire_protocol",
"../../../gn:default_deps",
"../../base",
- "../../ipc",
+ "../../base:unix_socket",
+ ]
+ cflags = [
+ "-isystem",
+ rebase_path("../../../buildtools/bionic/libc", root_build_dir),
]
sources = [
- "main.cc",
+ "malloc_hooks.cc",
]
}
diff --git a/src/profiling/memory/malloc_hooks.cc b/src/profiling/memory/malloc_hooks.cc
new file mode 100644
index 0000000..ed42605
--- /dev/null
+++ b/src/profiling/memory/malloc_hooks.cc
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <malloc.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include <atomic>
+
+#include <private/bionic_malloc_dispatch.h>
+
+#include "perfetto/base/build_config.h"
+#include "src/profiling/memory/client.h"
+#include "src/profiling/memory/wire_protocol.h"
+
+// The real malloc function pointers we get in initialize.
+static std::atomic<const MallocDispatch*> g_dispatch{nullptr};
+static std::atomic<perfetto::Client*> g_client{nullptr};
+static constexpr size_t kNumConnections = 2;
+
+// The only writes are in the initialization function. Because Bionic does a
+// release write after initialization and an acquire read to retrieve the hooked
+// malloc functions, we can use relaxed memory mode for both writing, and more
+// importantly because in the fast-path, reading.
+static constexpr std::memory_order write_order = std::memory_order_relaxed;
+
+static perfetto::Client* GetClient() {
+ return g_client.load(std::memory_order_relaxed);
+}
+
+static const MallocDispatch* GetDispatch() {
+ return g_dispatch.load(std::memory_order_relaxed);
+}
+
+// This is so we can make an so that we can swap out with the existing
+// libc_malloc_hooks.so
+#ifndef HEAPPROFD_PREFIX
+#define HEAPPROFD_PREFIX heapprofd
+#endif
+
+#define HEAPPROFD_ADD_PREFIX(name) \
+ PERFETTO_BUILDFLAG_CAT(HEAPPROFD_PREFIX, name)
+
+#pragma GCC visibility push(default)
+extern "C" {
+
+bool HEAPPROFD_ADD_PREFIX(_initialize)(const MallocDispatch* malloc_dispatch,
+ int* malloc_zygote_child,
+ const char* options);
+void HEAPPROFD_ADD_PREFIX(_finalize)();
+void HEAPPROFD_ADD_PREFIX(_dump_heap)(const char* file_name);
+void HEAPPROFD_ADD_PREFIX(_get_malloc_leak_info)(uint8_t** info,
+ size_t* overall_size,
+ size_t* info_size,
+ size_t* total_memory,
+ size_t* backtrace_size);
+bool HEAPPROFD_ADD_PREFIX(_write_malloc_leak_info)(FILE* fp);
+ssize_t HEAPPROFD_ADD_PREFIX(_malloc_backtrace)(void* pointer,
+ uintptr_t* frames,
+ size_t frame_count);
+void HEAPPROFD_ADD_PREFIX(_free_malloc_leak_info)(uint8_t* info);
+size_t HEAPPROFD_ADD_PREFIX(_malloc_usable_size)(void* pointer);
+void* HEAPPROFD_ADD_PREFIX(_malloc)(size_t size);
+void HEAPPROFD_ADD_PREFIX(_free)(void* pointer);
+void* HEAPPROFD_ADD_PREFIX(_aligned_alloc)(size_t alignment, size_t size);
+void* HEAPPROFD_ADD_PREFIX(_memalign)(size_t alignment, size_t bytes);
+void* HEAPPROFD_ADD_PREFIX(_realloc)(void* pointer, size_t bytes);
+void* HEAPPROFD_ADD_PREFIX(_calloc)(size_t nmemb, size_t bytes);
+struct mallinfo HEAPPROFD_ADD_PREFIX(_mallinfo)();
+int HEAPPROFD_ADD_PREFIX(_mallopt)(int param, int value);
+int HEAPPROFD_ADD_PREFIX(_posix_memalign)(void** memptr,
+ size_t alignment,
+ size_t size);
+int HEAPPROFD_ADD_PREFIX(_iterate)(uintptr_t base,
+ size_t size,
+ void (*callback)(uintptr_t base,
+ size_t size,
+ void* arg),
+ void* arg);
+void HEAPPROFD_ADD_PREFIX(_malloc_disable)();
+void HEAPPROFD_ADD_PREFIX(_malloc_enable)();
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* HEAPPROFD_ADD_PREFIX(_pvalloc)(size_t bytes);
+void* HEAPPROFD_ADD_PREFIX(_valloc)(size_t size);
+#endif
+}
+#pragma GCC visibility pop
+
+bool HEAPPROFD_ADD_PREFIX(_initialize)(const MallocDispatch* malloc_dispatch,
+ int*,
+ const char*) {
+ g_dispatch.store(malloc_dispatch, write_order);
+ // This can store a nullptr, so we have to check in the hooks below to avoid
+ // segfaulting in that case.
+ g_client.store(new (std::nothrow) perfetto::Client(
+ perfetto::kHeapprofdSocketFile, kNumConnections),
+ write_order);
+ return true;
+}
+
+void HEAPPROFD_ADD_PREFIX(_finalize)() {
+ // TODO(fmayer): Shut down client.
+ // Allow to re-enable existing client on subsequent initialize call.
+}
+
+void HEAPPROFD_ADD_PREFIX(_dump_heap)(const char*) {}
+
+void HEAPPROFD_ADD_PREFIX(
+ _get_malloc_leak_info)(uint8_t**, size_t*, size_t*, size_t*, size_t*) {}
+
+bool HEAPPROFD_ADD_PREFIX(_write_malloc_leak_info)(FILE*) {
+ return false;
+}
+
+ssize_t HEAPPROFD_ADD_PREFIX(_malloc_backtrace)(void*, uintptr_t*, size_t) {
+ return -1;
+}
+
+void HEAPPROFD_ADD_PREFIX(_free_malloc_leak_info)(uint8_t*) {}
+
+size_t HEAPPROFD_ADD_PREFIX(_malloc_usable_size)(void* pointer) {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->malloc_usable_size(pointer);
+}
+
+void* HEAPPROFD_ADD_PREFIX(_malloc)(size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ void* addr = dispatch->malloc(size);
+ if (client) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr),
+ dispatch->malloc, dispatch->free);
+ }
+ return addr;
+}
+
+void HEAPPROFD_ADD_PREFIX(_free)(void* pointer) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ if (client)
+ client->RecordFree(reinterpret_cast<uint64_t>(pointer));
+ return dispatch->free(pointer);
+}
+
+void* HEAPPROFD_ADD_PREFIX(_aligned_alloc)(size_t alignment, size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ void* addr = dispatch->aligned_alloc(alignment, size);
+ if (client) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr),
+ dispatch->malloc, dispatch->free);
+ }
+ return addr;
+}
+
+void* HEAPPROFD_ADD_PREFIX(_memalign)(size_t alignment, size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ void* addr = dispatch->memalign(alignment, size);
+ if (client) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr),
+ dispatch->malloc, dispatch->free);
+ }
+ return addr;
+}
+
+void* HEAPPROFD_ADD_PREFIX(_realloc)(void* pointer, size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ if (client && pointer)
+ client->RecordFree(reinterpret_cast<uint64_t>(pointer));
+ void* addr = dispatch->realloc(pointer, size);
+ if (client && size > 0) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr),
+ dispatch->malloc, dispatch->free);
+ }
+ return addr;
+}
+
+void* HEAPPROFD_ADD_PREFIX(_calloc)(size_t nmemb, size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ void* addr = dispatch->calloc(nmemb, size);
+ if (client) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr),
+ dispatch->malloc, dispatch->free);
+ }
+ return addr;
+}
+
+struct mallinfo HEAPPROFD_ADD_PREFIX(_mallinfo)() {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->mallinfo();
+}
+
+int HEAPPROFD_ADD_PREFIX(_mallopt)(int param, int value) {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->mallopt(param, value);
+}
+
+int HEAPPROFD_ADD_PREFIX(_posix_memalign)(void** memptr,
+ size_t alignment,
+ size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ perfetto::Client* client = GetClient();
+ int res = dispatch->posix_memalign(memptr, alignment, size);
+ if (res == 0 && client) {
+ client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(*memptr),
+ dispatch->malloc, dispatch->free);
+ }
+ return res;
+}
+
+int HEAPPROFD_ADD_PREFIX(_iterate)(uintptr_t,
+ size_t,
+ void (*)(uintptr_t base,
+ size_t size,
+ void* arg),
+ void*) {
+ return 0;
+}
+
+void HEAPPROFD_ADD_PREFIX(_malloc_disable)() {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->malloc_disable();
+}
+
+void HEAPPROFD_ADD_PREFIX(_malloc_enable)() {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->malloc_enable();
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* HEAPPROFD_ADD_PREFIX(_pvalloc)(size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->pvalloc(size);
+}
+
+void* HEAPPROFD_ADD_PREFIX(_valloc)(size_t size) {
+ const MallocDispatch* dispatch = GetDispatch();
+ return dispatch->valloc(size);
+}
+
+#endif
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index e32289e..3483c1a 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -44,6 +44,8 @@
'//:traced',
'//:traced_probes',
'//:trace_to_text',
+ '//:heapprofd_client',
+ '//:heapprofd',
]
# Defines a custom init_rc argument to be applied to the corresponding output
@@ -94,6 +96,13 @@
'libservices',
}
+# Additional arguments to apply to Android.bp rules.
+additional_args = {
+ "heapprofd_client": [
+ ("include_dirs", ["bionic/libc"]),
+ ],
+}
+
def enable_gmock(module):
module.static_libs.append('libgmock')
@@ -201,6 +210,8 @@
self._output_field(output, 'defaults')
self._output_field(output, 'cflags')
self._output_field(output, 'local_include_dirs')
+ for key, value in additional_args.get(self.name, []):
+ self._write_value(output, key, value)
disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
if self.user_debug_flag or disable_pdk:
@@ -219,6 +230,9 @@
def _output_field(self, output, name, sort=True):
value = getattr(self, name)
+ return self._write_value(output, name, value, sort)
+
+ def _write_value(self, output, name, value, sort=True):
if not value:
return
if isinstance(value, set):