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_HEAPPROFD_PRODUCER_H_ 18 #define SRC_PROFILING_MEMORY_HEAPPROFD_PRODUCER_H_ 19 20 #include <functional> 21 #include <map> 22 23 #include "perfetto/base/optional.h" 24 #include "perfetto/base/task_runner.h" 25 #include "perfetto/base/unix_socket.h" 26 #include "perfetto/base/unix_task_runner.h" 27 28 #include "perfetto/tracing/core/basic_types.h" 29 #include "perfetto/tracing/core/data_source_config.h" 30 #include "perfetto/tracing/core/producer.h" 31 #include "perfetto/tracing/core/trace_writer.h" 32 #include "perfetto/tracing/core/tracing_service.h" 33 34 #include "src/profiling/memory/bookkeeping.h" 35 #include "src/profiling/memory/proc_utils.h" 36 #include "src/profiling/memory/system_property.h" 37 #include "src/profiling/memory/unwinding.h" 38 39 namespace perfetto { 40 namespace profiling { 41 42 struct Process { 43 pid_t pid; 44 std::string cmdline; 45 }; 46 47 class LogHistogram { 48 public: 49 static const uint64_t kMaxBucket; 50 static constexpr size_t kBuckets = 20; 51 Add(uint64_t value)52 void Add(uint64_t value) { values_[GetBucket(value)]++; } 53 std::vector<std::pair<uint64_t, uint64_t>> GetData(); 54 55 private: 56 size_t GetBucket(uint64_t value); 57 58 std::array<uint64_t, kBuckets> values_ = {}; 59 }; 60 61 // TODO(rsavitski): central daemon can do less work if it knows that the global 62 // operating mode is fork-based, as it then will not be interacting with the 63 // clients. This can be implemented as an additional mode here. 64 enum class HeapprofdMode { kCentral, kChild }; 65 66 // Heap profiling producer. Can be instantiated in two modes, central and 67 // child (also referred to as fork mode). 68 // 69 // The central mode producer is instantiated by the system heapprofd daemon. Its 70 // primary responsibility is activating profiling (via system properties and 71 // signals) in targets identified by profiling configs. On debug platform 72 // builds, the central producer can also handle the out-of-process unwinding & 73 // writing of the profiles for all client processes. 74 // 75 // An alternative model is where the central heapprofd triggers the profiling in 76 // the target process, but the latter fork-execs a private heapprofd binary to 77 // handle unwinding only for that process. The forked heapprofd instantiates 78 // this producer in the "child" mode. In this scenario, the profiled process 79 // never talks to the system daemon. 80 // 81 // TODO(fmayer||rsavitski): cover interesting invariants/structure of the 82 // implementation (e.g. number of data sources in child mode), including 83 // threading structure. 84 class HeapprofdProducer : public Producer, public UnwindingWorker::Delegate { 85 public: 86 friend class SocketDelegate; 87 88 // TODO(fmayer): Split into two delegates for the listening socket in kCentral 89 // and for the per-client sockets to make this easier to understand? 90 // Alternatively, find a better name for this. 91 class SocketDelegate : public base::UnixSocket::EventListener { 92 public: SocketDelegate(HeapprofdProducer * producer)93 SocketDelegate(HeapprofdProducer* producer) : producer_(producer) {} 94 95 void OnDisconnect(base::UnixSocket* self) override; 96 void OnNewIncomingConnection( 97 base::UnixSocket* self, 98 std::unique_ptr<base::UnixSocket> new_connection) override; 99 void OnDataAvailable(base::UnixSocket* self) override; 100 101 private: 102 HeapprofdProducer* producer_; 103 }; 104 105 HeapprofdProducer(HeapprofdMode mode, base::TaskRunner* task_runner); 106 ~HeapprofdProducer() override; 107 108 // Producer Impl: 109 void OnConnect() override; 110 void OnDisconnect() override; 111 void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override; 112 void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override; 113 void StopDataSource(DataSourceInstanceID) override; 114 void OnTracingSetup() override; 115 void Flush(FlushRequestID, 116 const DataSourceInstanceID* data_source_ids, 117 size_t num_data_sources) override; ClearIncrementalState(const DataSourceInstanceID *,size_t)118 void ClearIncrementalState(const DataSourceInstanceID* /*data_source_ids*/, 119 size_t /*num_data_sources*/) override {} 120 121 // TODO(fmayer): Refactor once/if we have generic reconnect logic. 122 void ConnectWithRetries(const char* socket_name); 123 void DumpAll(); 124 125 // UnwindingWorker::Delegate impl: 126 void PostAllocRecord(AllocRecord) override; 127 void PostFreeRecord(FreeRecord) override; 128 void PostSocketDisconnected(DataSourceInstanceID, 129 pid_t, 130 SharedRingBuffer::Stats) override; 131 132 void HandleAllocRecord(AllocRecord); 133 void HandleFreeRecord(FreeRecord); 134 void HandleSocketDisconnected(DataSourceInstanceID, 135 pid_t, 136 SharedRingBuffer::Stats); 137 138 // Valid only if mode_ == kChild. 139 void SetTargetProcess(pid_t target_pid, 140 std::string target_cmdline, 141 base::ScopedFile inherited_socket); 142 // Valid only if mode_ == kChild. Kicks off a periodic check that the child 143 // heapprofd is actively working on a data source (which should correspond to 144 // the target process). The first check is delayed to let the freshly spawned 145 // producer get the data sources from the tracing service (i.e. traced). 146 void ScheduleActiveDataSourceWatchdog(); 147 148 // Exposed for testing. 149 void SetProducerEndpoint( 150 std::unique_ptr<TracingService::ProducerEndpoint> endpoint); 151 152 private: 153 // State of the connection to tracing service (traced). 154 enum State { 155 kNotStarted = 0, 156 kNotConnected, 157 kConnecting, 158 kConnected, 159 }; 160 161 struct ProcessState { ProcessStateProcessState162 ProcessState(GlobalCallstackTrie* callsites) : heap_tracker(callsites) {} 163 bool disconnected = false; 164 bool buffer_overran = false; 165 bool buffer_corrupted = false; 166 167 uint64_t heap_samples = 0; 168 uint64_t map_reparses = 0; 169 uint64_t unwinding_errors = 0; 170 171 uint64_t total_unwinding_time_us = 0; 172 LogHistogram unwinding_time_us; 173 HeapTracker heap_tracker; 174 }; 175 176 struct DataSource { 177 DataSourceInstanceID id; 178 std::unique_ptr<TraceWriter> trace_writer; 179 HeapprofdConfig config; 180 ClientConfiguration client_configuration; 181 std::vector<SystemProperties::Handle> properties; 182 std::set<pid_t> signaled_pids; 183 std::set<pid_t> rejected_pids; 184 std::map<pid_t, ProcessState> process_states; 185 std::vector<std::string> normalized_cmdlines; 186 uint64_t next_index_ = 0; 187 }; 188 189 struct PendingProcess { 190 std::unique_ptr<base::UnixSocket> sock; 191 DataSourceInstanceID data_source_instance_id; 192 SharedRingBuffer shmem; 193 }; 194 195 void HandleClientConnection(std::unique_ptr<base::UnixSocket> new_connection, 196 Process process); 197 198 void ConnectService(); 199 void Restart(); 200 void ResetConnectionBackoff(); 201 void IncreaseConnectionBackoff(); 202 203 void FinishDataSourceFlush(FlushRequestID flush_id); 204 bool Dump(DataSourceInstanceID id, 205 FlushRequestID flush_id, 206 bool has_flush_id); 207 void DoContinuousDump(DataSourceInstanceID id, uint32_t dump_interval); 208 209 UnwindingWorker& UnwinderForPID(pid_t); 210 bool IsPidProfiled(pid_t); 211 DataSource* GetDataSourceForProcess(const Process& proc); 212 void RecordOtherSourcesAsRejected(DataSource* active_ds, const Process& proc); 213 214 // Specific to mode_ == kCentral 215 std::unique_ptr<base::UnixSocket> MakeListeningSocket(); 216 217 // Specific to mode_ == kChild 218 void TerminateProcess(int exit_status); 219 // Specific to mode_ == kChild 220 void ActiveDataSourceWatchdogCheck(); 221 // Adopts the (connected) sockets inherited from the target process, invoking 222 // the on-connection callback. 223 // Specific to mode_ == kChild 224 void AdoptTargetProcessSocket(); 225 226 // Class state: 227 228 // Task runner is owned by the main thread. 229 base::TaskRunner* const task_runner_; 230 const HeapprofdMode mode_; 231 232 // State of connection to the tracing service. 233 State state_ = kNotStarted; 234 uint32_t connection_backoff_ms_ = 0; 235 const char* producer_sock_name_ = nullptr; 236 237 // Client processes that have connected, but with which we have not yet 238 // finished the handshake. 239 std::map<pid_t, PendingProcess> pending_processes_; 240 241 // Must outlive data_sources_ - owns at least the shared memory referenced by 242 // TraceWriters. 243 std::unique_ptr<TracingService::ProducerEndpoint> endpoint_; 244 245 // Must outlive data_sources_ - HeapTracker references the trie. 246 GlobalCallstackTrie callsites_; 247 248 // Must outlive data_sources_ - DataSource can hold 249 // SystemProperties::Handle-s. 250 // Specific to mode_ == kCentral 251 SystemProperties properties_; 252 253 std::map<FlushRequestID, size_t> flushes_in_progress_; 254 std::map<DataSourceInstanceID, DataSource> data_sources_; 255 std::vector<UnwindingWorker> unwinding_workers_; 256 257 // Specific to mode_ == kCentral 258 std::unique_ptr<base::UnixSocket> listening_socket_; 259 260 // Specific to mode_ == kChild 261 Process target_process_{base::kInvalidPid, ""}; 262 // This is a valid FD only between SetTargetProcess and 263 // AdoptTargetProcessSocket. 264 // Specific to mode_ == kChild 265 base::ScopedFile inherited_fd_; 266 267 SocketDelegate socket_delegate_; 268 269 base::WeakPtrFactory<HeapprofdProducer> weak_factory_; // Keep last. 270 }; 271 272 } // namespace profiling 273 } // namespace perfetto 274 275 #endif // SRC_PROFILING_MEMORY_HEAPPROFD_PRODUCER_H_ 276