| /* | 
 |  * 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 <sys/types.h> | 
 | #include <sys/wait.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include <atomic> | 
 | #include <tuple> | 
 |  | 
 | #include <sys/system_properties.h> | 
 |  | 
 | #include <private/bionic_malloc.h> | 
 | #include <private/bionic_malloc_dispatch.h> | 
 |  | 
 | #include "perfetto/base/build_config.h" | 
 | #include "perfetto/base/logging.h" | 
 | #include "perfetto/base/unix_socket.h" | 
 | #include "perfetto/base/utils.h" | 
 | #include "src/profiling/memory/client.h" | 
 | #include "src/profiling/memory/proc_utils.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::profiling::Client*> g_client{nullptr}; | 
 | static constexpr size_t kNumConnections = 2; | 
 |  | 
 | static constexpr char kHeapprofdBinPath[] = "/system/bin/heapprofd"; | 
 |  | 
 | // 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::profiling::Client* GetClient() { | 
 |   return g_client.load(std::memory_order_relaxed); | 
 | } | 
 |  | 
 | static const MallocDispatch* GetDispatch() { | 
 |   return g_dispatch.load(std::memory_order_relaxed); | 
 | } | 
 |  | 
 | static void MallocDispatchReset(const MallocDispatch* dispatch) { | 
 |   android_mallopt(M_RESET_HOOKS, nullptr, 0); | 
 | } | 
 |  | 
 | // 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 | 
 |  | 
 | namespace { | 
 |  | 
 | std::string ReadSystemProperty(const char* key) { | 
 |   std::string prop_value; | 
 |   const prop_info* prop = __system_property_find(key); | 
 |   if (!prop) { | 
 |     return prop_value;  // empty | 
 |   } | 
 |   __system_property_read_callback( | 
 |       prop, | 
 |       [](void* cookie, const char* name, const char* value, uint32_t) { | 
 |         std::string* prop_value = reinterpret_cast<std::string*>(cookie); | 
 |         *prop_value = value; | 
 |       }, | 
 |       &prop_value); | 
 |   return prop_value; | 
 | } | 
 |  | 
 | bool ShouldForkPrivateDaemon() { | 
 |   std::string build_type = ReadSystemProperty("ro.build.type"); | 
 |   if (build_type.empty()) { | 
 |     PERFETTO_ELOG( | 
 |         "Cannot determine platform build type, proceeding in fork mode " | 
 |         "profiling."); | 
 |     return true; | 
 |   } | 
 |  | 
 |   // On development builds, we support both modes of profiling, depending on a | 
 |   // system property. | 
 |   if (build_type == "userdebug" || build_type == "eng") { | 
 |     std::string mode = ReadSystemProperty("heapprofd.userdebug.mode"); | 
 |     return mode == "fork"; | 
 |   } | 
 |  | 
 |   // User/other builds - always fork private profiler. | 
 |   return true; | 
 | } | 
 |  | 
 | std::unique_ptr<perfetto::profiling::Client> CreateClientForCentralDaemon() { | 
 |   PERFETTO_DLOG("Constructing client for central daemon."); | 
 |  | 
 |   using perfetto::profiling::Client; | 
 |   return std::unique_ptr<Client>(new (std::nothrow) Client( | 
 |       perfetto::profiling::kHeapprofdSocketFile, kNumConnections)); | 
 | } | 
 |  | 
 | std::unique_ptr<perfetto::profiling::Client> CreateClientAndPrivateDaemon() { | 
 |   PERFETTO_DLOG("Setting up fork mode profiling."); | 
 |   // TODO(rsavitski): create kNumConnections socketpairs to match central mode | 
 |   // behavior. | 
 |   perfetto::base::UnixSocketRaw parent_sock; | 
 |   perfetto::base::UnixSocketRaw child_sock; | 
 |   std::tie(parent_sock, child_sock) = perfetto::base::UnixSocketRaw::CreatePair( | 
 |       perfetto::base::SockType::kStream); | 
 |  | 
 |   if (!parent_sock || !child_sock) { | 
 |     PERFETTO_PLOG("Failed to create socketpair."); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   child_sock.RetainOnExec(); | 
 |  | 
 |   // Record own pid and cmdline, to pass down to the forked heapprofd. | 
 |   pid_t target_pid = getpid(); | 
 |   std::string target_cmdline; | 
 |   if (!perfetto::profiling::GetCmdlineForPID(target_pid, &target_cmdline)) { | 
 |     PERFETTO_ELOG("Failed to read own cmdline."); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   pid_t fork_pid = fork(); | 
 |   if (fork_pid == -1) { | 
 |     PERFETTO_PLOG("Failed to fork."); | 
 |     return nullptr; | 
 |   } | 
 |   if (fork_pid == 0) {  // child | 
 |     // daemon() forks again, terminating the calling thread (i.e. the direct | 
 |     // child of the original process). So the rest of this codepath will be | 
 |     // executed in a (new) reparented process. | 
 |     if (daemon(/*nochdir=*/0, /*noclose=*/0) == -1) { | 
 |       PERFETTO_PLOG("Daemonization failed."); | 
 |       _exit(1); | 
 |     } | 
 |     std::string pid_arg = | 
 |         std::string("--exclusive-for-pid=") + std::to_string(target_pid); | 
 |     std::string cmd_arg = | 
 |         std::string("--exclusive-for-cmdline=") + target_cmdline; | 
 |     std::string fd_arg = | 
 |         std::string("--inherit-socket-fd=") + std::to_string(child_sock.fd()); | 
 |     const char* argv[] = {kHeapprofdBinPath, pid_arg.c_str(), cmd_arg.c_str(), | 
 |                           fd_arg.c_str(), nullptr}; | 
 |  | 
 |     execv(kHeapprofdBinPath, const_cast<char**>(argv)); | 
 |     PERFETTO_PLOG("Failed to execute private heapprofd."); | 
 |     _exit(1); | 
 |   }  // else - parent continuing the client setup | 
 |  | 
 |   child_sock.ReleaseFd().reset();  // close child socket's fd | 
 |   if (!parent_sock.SetTxTimeout(perfetto::profiling::kClientSockTxTimeoutMs)) { | 
 |     PERFETTO_PLOG("Failed to set socket transmit timeout."); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   // Wait on the immediate child to exit (allow for ECHILD in the unlikely case | 
 |   // we're in a process that has made its children unwaitable). | 
 |   siginfo_t unused = {}; | 
 |   if (PERFETTO_EINTR(waitid(P_PID, fork_pid, &unused, WEXITED)) == -1 && | 
 |       errno != ECHILD) { | 
 |     PERFETTO_PLOG("Failed to waitid on immediate child."); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   using perfetto::profiling::Client; | 
 |   std::vector<perfetto::base::UnixSocketRaw> client_sockets; | 
 |   client_sockets.emplace_back(std::move(parent_sock)); | 
 |   return std::unique_ptr<Client>(new (std::nothrow) | 
 |                                      Client(std::move(client_sockets))); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool HEAPPROFD_ADD_PREFIX(_initialize)(const MallocDispatch* malloc_dispatch, | 
 |                                        int*, | 
 |                                        const char*) { | 
 |   perfetto::profiling::Client* old_client = GetClient(); | 
 |   if (old_client) | 
 |     old_client->Shutdown(); | 
 |  | 
 |   // Table of pointers to backing implementation. | 
 |   g_dispatch.store(malloc_dispatch, write_order); | 
 |  | 
 |   std::unique_ptr<perfetto::profiling::Client> client = | 
 |       ShouldForkPrivateDaemon() ? CreateClientAndPrivateDaemon() | 
 |                                 : CreateClientForCentralDaemon(); | 
 |  | 
 |   if (!client || !client->inited()) { | 
 |     PERFETTO_LOG("Client not initialized, not installing hooks."); | 
 |     return false; | 
 |   } | 
 |  | 
 |   g_client.store(client.release()); | 
 |   return true; | 
 | } | 
 |  | 
 | void HEAPPROFD_ADD_PREFIX(_finalize)() { | 
 |   // TODO(fmayer): This should not leak. | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   if (client) | 
 |     client->Shutdown(); | 
 |   MallocDispatchReset(GetDispatch()); | 
 | } | 
 |  | 
 | 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::profiling::Client* client = GetClient(); | 
 |   void* addr = dispatch->malloc(size); | 
 |   if (client) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   return addr; | 
 | } | 
 |  | 
 | void HEAPPROFD_ADD_PREFIX(_free)(void* pointer) { | 
 |   const MallocDispatch* dispatch = GetDispatch(); | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   if (client) | 
 |     if (!client->RecordFree(reinterpret_cast<uint64_t>(pointer))) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   return dispatch->free(pointer); | 
 | } | 
 |  | 
 | void* HEAPPROFD_ADD_PREFIX(_aligned_alloc)(size_t alignment, size_t size) { | 
 |   const MallocDispatch* dispatch = GetDispatch(); | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   void* addr = dispatch->aligned_alloc(alignment, size); | 
 |   if (client) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   return addr; | 
 | } | 
 |  | 
 | void* HEAPPROFD_ADD_PREFIX(_memalign)(size_t alignment, size_t size) { | 
 |   const MallocDispatch* dispatch = GetDispatch(); | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   void* addr = dispatch->memalign(alignment, size); | 
 |   if (client) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   return addr; | 
 | } | 
 |  | 
 | void* HEAPPROFD_ADD_PREFIX(_realloc)(void* pointer, size_t size) { | 
 |   const MallocDispatch* dispatch = GetDispatch(); | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   if (client && pointer) | 
 |     if (!client->RecordFree(reinterpret_cast<uint64_t>(pointer))) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   void* addr = dispatch->realloc(pointer, size); | 
 |   if (client && size > 0) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   return addr; | 
 | } | 
 |  | 
 | void* HEAPPROFD_ADD_PREFIX(_calloc)(size_t nmemb, size_t size) { | 
 |   const MallocDispatch* dispatch = GetDispatch(); | 
 |   perfetto::profiling::Client* client = GetClient(); | 
 |   void* addr = dispatch->calloc(nmemb, size); | 
 |   if (client) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(addr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   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::profiling::Client* client = GetClient(); | 
 |   int res = dispatch->posix_memalign(memptr, alignment, size); | 
 |   if (res == 0 && client) { | 
 |     if (!client->MaybeSampleAlloc(size, reinterpret_cast<uint64_t>(*memptr), | 
 |                                   dispatch->malloc, dispatch->free)) | 
 |       MallocDispatchReset(GetDispatch()); | 
 |   } | 
 |   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 |