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 #ifndef SRC_PROFILING_MEMORY_CLIENT_H_ 18 #define SRC_PROFILING_MEMORY_CLIENT_H_ 19 20 #include <stddef.h> 21 #include <sys/types.h> 22 23 #include <atomic> 24 #include <condition_variable> 25 #include <mutex> 26 #include <vector> 27 28 #include <unwindstack/Arch.h> 29 30 #include "perfetto/base/build_config.h" 31 #include "perfetto/base/compiler.h" 32 #include "perfetto/ext/base/unix_socket.h" 33 #include "src/profiling/memory/sampler.h" 34 #include "src/profiling/memory/shared_ring_buffer.h" 35 #include "src/profiling/memory/unhooked_allocator.h" 36 #include "src/profiling/memory/wire_protocol.h" 37 38 namespace perfetto { 39 namespace profiling { 40 41 struct StackRange { 42 const char* begin; 43 // One past the highest address part of the stack. 44 const char* end; 45 }; 46 47 StackRange GetThreadStackRange(); 48 StackRange GetSigAltStackRange(); 49 StackRange GetMainThreadStackRange(); 50 51 constexpr uint64_t kInfiniteTries = 0; 52 constexpr uint32_t kClientSockTimeoutMs = 1000; 53 54 uint64_t GetMaxTries(const ClientConfiguration& client_config); 55 56 // Profiling client, used to sample and record the malloc/free family of calls, 57 // and communicate the necessary state to a separate profiling daemon process. 58 // 59 // Created and owned by the malloc hooks. 60 // 61 // Methods of this class are thread-safe unless otherwise stated, in which case 62 // the caller needs to synchronize calls behind a mutex or similar. 63 // 64 // Implementation warning: this class should not use any heap, as otherwise its 65 // destruction would enter the possibly-hooked |free|, which can reference the 66 // Client itself. If avoiding the heap is not possible, then look at using 67 // UnhookedAllocator. 68 class Client { 69 public: 70 // Returns a client that is ready for sampling allocations, using the given 71 // socket (which should already be connected to heapprofd). 72 // 73 // Returns a shared_ptr since that is how the client will ultimately be used, 74 // and to take advantage of std::allocate_shared putting the object & the 75 // control block in one block of memory. 76 static std::shared_ptr<Client> CreateAndHandshake( 77 base::UnixSocketRaw sock, 78 UnhookedAllocator<Client> unhooked_allocator); 79 80 static std::optional<base::UnixSocketRaw> ConnectToHeapprofd( 81 const std::string& sock_name); 82 83 bool RecordMalloc(uint32_t heap_id, 84 uint64_t sample_size, 85 uint64_t alloc_size, 86 uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT; 87 88 // Add address to buffer of deallocations. Flushes the buffer if necessary. 89 bool RecordFree(uint32_t heap_id, 90 uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT; 91 bool RecordHeapInfo(uint32_t heap_id, 92 const char* heap_name, 93 uint64_t interval); 94 AddClientSpinlockBlockedUs(size_t n)95 void AddClientSpinlockBlockedUs(size_t n) { 96 shmem_.AddClientSpinlockBlockedUs(n); 97 } 98 99 // Public for std::allocate_shared. Use CreateAndHandshake() to create 100 // instances instead. 101 Client(base::UnixSocketRaw sock, 102 ClientConfiguration client_config, 103 SharedRingBuffer shmem, 104 pid_t pid_at_creation, 105 StackRange main_thread_stack_range); 106 107 ~Client(); 108 client_config()109 const ClientConfiguration& client_config() { return client_config_; } adaptive_sampling_shmem_threshold()110 uint64_t adaptive_sampling_shmem_threshold() { 111 return client_config_.adaptive_sampling_shmem_threshold; 112 } adaptive_sampling_max_sampling_interval_bytes()113 uint64_t adaptive_sampling_max_sampling_interval_bytes() { 114 return client_config_.adaptive_sampling_max_sampling_interval_bytes; 115 } write_avail()116 uint64_t write_avail() { return shmem_.write_avail(); } 117 118 bool IsConnected(); 119 120 private: 121 #if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_RISCV) && \ 122 !PERFETTO_HAS_BUILTIN_STACK_ADDRESS() 123 // For specific architectures, such as riscv, different calling conventions 124 // make a difference in the meaning of the frame pointer. (see comments in 125 // client.cc) So, we want to use other method to get the stack address for 126 // specific architectures such as riscv. 127 ssize_t GetStackRegister(unwindstack::ArchEnum arch); 128 uintptr_t GetStackAddress(char* reg_data, unwindstack::ArchEnum arch); 129 #endif 130 const char* GetStackEnd(const char* stacktop); 131 bool SendControlSocketByte() PERFETTO_WARN_UNUSED_RESULT; 132 int64_t SendWireMessageWithRetriesIfBlocking(const WireMessage&) 133 PERFETTO_WARN_UNUSED_RESULT; 134 135 bool IsPostFork(); 136 137 ClientConfiguration client_config_; 138 uint64_t max_shmem_tries_; 139 base::UnixSocketRaw sock_; 140 141 StackRange main_thread_stack_range_{nullptr, nullptr}; 142 std::atomic<uint64_t> 143 sequence_number_[base::ArraySize(ClientConfiguration{}.heaps)] = {}; 144 SharedRingBuffer shmem_; 145 146 // Used to detect (during the slow path) the situation where the process has 147 // forked during profiling, and is performing malloc operations in the child. 148 // In this scenario, we want to stop profiling in the child, as otherwise 149 // it'll proceed to write to the same shared buffer & control socket (with 150 // duplicate sequence ids). 151 const pid_t pid_at_creation_; 152 bool detected_fork_ = false; 153 bool postfork_return_value_ = false; 154 }; 155 156 } // namespace profiling 157 } // namespace perfetto 158 159 #endif // SRC_PROFILING_MEMORY_CLIENT_H_ 160