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