blob: 663ca215944d5dbaa230384c11d109c709d01af3 [file] [log] [blame]
Isabelle Taylord404ea12018-02-19 17:28:01 +00001/*
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#include "src/traced/probes/probes_producer.h"
18
19#include <stdio.h>
Anna Zappone27ac99c2018-03-06 14:25:35 +000020#include <queue>
Isabelle Taylord404ea12018-02-19 17:28:01 +000021#include <string>
22
23#include "perfetto/base/logging.h"
24#include "perfetto/traced/traced.h"
25#include "perfetto/tracing/core/data_source_config.h"
26#include "perfetto/tracing/core/data_source_descriptor.h"
Hector Dearmana89cc572018-02-23 12:02:58 +000027#include "perfetto/tracing/core/ftrace_config.h"
Isabelle Taylord404ea12018-02-19 17:28:01 +000028#include "perfetto/tracing/core/trace_config.h"
29#include "perfetto/tracing/core/trace_packet.h"
30
31#include "src/process_stats/file_utils.h"
32#include "src/process_stats/procfs_utils.h"
33
Anna Zappone27ac99c2018-03-06 14:25:35 +000034#include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
Isabelle Taylord404ea12018-02-19 17:28:01 +000035#include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
36#include "perfetto/trace/ps/process_tree.pbzero.h"
37#include "perfetto/trace/trace_packet.pbzero.h"
38
39namespace perfetto {
40namespace {
41
42uint64_t kInitialConnectionBackoffMs = 100;
43uint64_t kMaxConnectionBackoffMs = 30 * 1000;
44const char* kFtraceSourceName = "com.google.perfetto.ftrace";
45const char* kProcessStatsSourceName = "com.google.perfetto.process_stats";
Anna Zappone27ac99c2018-03-06 14:25:35 +000046const char* kInodeFileMapSourceName = "com.google.perfetto.inode_file_map";
Isabelle Taylord404ea12018-02-19 17:28:01 +000047
48} // namespace.
49
50// State transition diagram:
51// +----------------------------+
52// v +
53// NotStarted -> NotConnected -> Connecting -> Connected
54// ^ +
55// +--------------+
56//
57
Hector Dearmanc8488032018-03-02 13:12:01 +000058ProbesProducer::ProbesProducer() {}
Isabelle Taylord404ea12018-02-19 17:28:01 +000059ProbesProducer::~ProbesProducer() = default;
60
61void ProbesProducer::OnConnect() {
62 PERFETTO_DCHECK(state_ == kConnecting);
63 state_ = kConnected;
64 ResetConnectionBackoff();
65 PERFETTO_LOG("Connected to the service");
66
67 DataSourceDescriptor ftrace_descriptor;
68 ftrace_descriptor.set_name(kFtraceSourceName);
Lalit Magantia88807d2018-03-05 18:21:38 +000069 endpoint_->RegisterDataSource(ftrace_descriptor, [](DataSourceInstanceID) {});
Isabelle Taylord404ea12018-02-19 17:28:01 +000070
71 DataSourceDescriptor process_stats_descriptor;
72 process_stats_descriptor.set_name(kProcessStatsSourceName);
73 endpoint_->RegisterDataSource(process_stats_descriptor,
Lalit Magantia88807d2018-03-05 18:21:38 +000074 [](DataSourceInstanceID) {});
Anna Zappone27ac99c2018-03-06 14:25:35 +000075
76 DataSourceDescriptor inode_map_descriptor;
77 inode_map_descriptor.set_name(kInodeFileMapSourceName);
78 endpoint_->RegisterDataSource(inode_map_descriptor,
79 [](DataSourceInstanceID) {});
Isabelle Taylord404ea12018-02-19 17:28:01 +000080}
81
82void ProbesProducer::OnDisconnect() {
83 PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
84 state_ = kNotConnected;
85 PERFETTO_LOG("Disconnected from tracing service");
86 IncreaseConnectionBackoff();
87
88 // TODO(hjd): Erase all sinks and add e2e test for this.
89 task_runner_->PostDelayedTask([this] { this->Connect(); },
90 connection_backoff_ms_);
91}
92
93void ProbesProducer::CreateDataSourceInstance(
94 DataSourceInstanceID id,
95 const DataSourceConfig& source_config) {
Isabelle Taylord404ea12018-02-19 17:28:01 +000096 if (source_config.name() == kFtraceSourceName) {
97 CreateFtraceDataSourceInstance(id, source_config);
Anna Zappone27ac99c2018-03-06 14:25:35 +000098 } else if (source_config.name() == kInodeFileMapSourceName) {
99 CreateInodeFileMapDataSourceInstance(id, source_config);
Hector Dearman77451692018-03-08 16:21:13 +0000100 } else if (source_config.name() == kProcessStatsSourceName) {
101 CreateProcessStatsDataSourceInstance(id, source_config);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000102 } else {
103 PERFETTO_ELOG("Data source name: %s not recognised.",
104 source_config.name().c_str());
105 }
106}
107
Anna Zappone27ac99c2018-03-06 14:25:35 +0000108void ProbesProducer::AddWatchdogsTimer(DataSourceInstanceID id,
109 const DataSourceConfig& source_config) {
110 if (source_config.trace_duration_ms() != 0)
111 watchdogs_.emplace(id, base::Watchdog::GetInstance()->CreateFatalTimer(
112 5000 + 2 * source_config.trace_duration_ms()));
113}
114
Isabelle Taylord404ea12018-02-19 17:28:01 +0000115void ProbesProducer::CreateFtraceDataSourceInstance(
116 DataSourceInstanceID id,
117 const DataSourceConfig& source_config) {
118 // Don't retry if FtraceController::Create() failed once.
119 // This can legitimately happen on user builds where we cannot access the
120 // debug paths, e.g., because of SELinux rules.
121 if (ftrace_creation_failed_)
122 return;
123
124 // Lazily create on the first instance.
125 if (!ftrace_) {
126 ftrace_ = FtraceController::Create(task_runner_);
127
128 if (!ftrace_) {
129 PERFETTO_ELOG("Failed to create FtraceController");
130 ftrace_creation_failed_ = true;
131 return;
132 }
133
134 ftrace_->DisableAllEvents();
135 ftrace_->ClearTrace();
136 }
137
138 PERFETTO_LOG("Ftrace start (id=%" PRIu64 ", target_buf=%" PRIu32 ")", id,
139 source_config.target_buffer());
140
Hector Dearmana89cc572018-02-23 12:02:58 +0000141 FtraceConfig proto_config = source_config.ftrace_config();
Isabelle Taylord404ea12018-02-19 17:28:01 +0000142
143 // TODO(hjd): Static cast is bad, target_buffer() should return a BufferID.
144 auto trace_writer = endpoint_->CreateTraceWriter(
145 static_cast<BufferID>(source_config.target_buffer()));
Hector Dearmanc8488032018-03-02 13:12:01 +0000146 auto delegate = std::unique_ptr<SinkDelegate>(
147 new SinkDelegate(task_runner_, std::move(trace_writer)));
Isabelle Taylord404ea12018-02-19 17:28:01 +0000148 auto sink = ftrace_->CreateSink(std::move(proto_config), delegate.get());
Hector Dearmanee3c49d2018-02-28 14:10:22 +0000149 if (!sink) {
150 PERFETTO_ELOG("Failed to start tracing (maybe someone else is using it?)");
151 return;
152 }
Isabelle Taylord404ea12018-02-19 17:28:01 +0000153 delegate->sink(std::move(sink));
154 delegates_.emplace(id, std::move(delegate));
Anna Zappone27ac99c2018-03-06 14:25:35 +0000155 AddWatchdogsTimer(id, source_config);
156}
157
158void ProbesProducer::CreateInodeFileMapDataSourceInstance(
159 DataSourceInstanceID id,
160 const DataSourceConfig& source_config) {
161 PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
162 id, source_config.target_buffer());
163 auto trace_writer = endpoint_->CreateTraceWriter(
164 static_cast<BufferID>(source_config.target_buffer()));
Anna Zappone45a282a2018-03-07 15:22:25 +0000165 CreateDeviceToInodeMap("/system/", &system_inodes_);
Anna Zappone27ac99c2018-03-06 14:25:35 +0000166 auto file_map_source = std::unique_ptr<InodeFileMapDataSource>(
Anna Zappone45a282a2018-03-07 15:22:25 +0000167 new InodeFileMapDataSource(&system_inodes_, std::move(trace_writer)));
Anna Zappone27ac99c2018-03-06 14:25:35 +0000168 file_map_sources_.emplace(id, std::move(file_map_source));
169 AddWatchdogsTimer(id, source_config);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000170}
171
172void ProbesProducer::CreateProcessStatsDataSourceInstance(
Hector Dearman77451692018-03-08 16:21:13 +0000173 DataSourceInstanceID id,
Isabelle Taylord404ea12018-02-19 17:28:01 +0000174 const DataSourceConfig& source_config) {
Hector Dearman77451692018-03-08 16:21:13 +0000175 PERFETTO_DCHECK(process_stats_sources_.count(id) == 0);
176 process_stats_sources_.insert(id);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000177 auto trace_writer = endpoint_->CreateTraceWriter(
178 static_cast<BufferID>(source_config.target_buffer()));
179 procfs_utils::ProcessMap processes;
180 auto trace_packet = trace_writer->NewTracePacket();
181 protos::pbzero::ProcessTree* process_tree = trace_packet->set_process_tree();
182
183 file_utils::ForEachPidInProcPath(
184 "/proc", [&processes, &process_tree](int pid) {
185 if (!processes.count(pid)) {
186 if (procfs_utils::ReadTgid(pid) != pid)
187 return;
188 processes[pid] = procfs_utils::ReadProcessInfo(pid);
189 }
190 ProcessInfo* process = processes[pid].get();
191 procfs_utils::ReadProcessThreads(process);
192 auto* process_writer = process_tree->add_processes();
193 process_writer->set_pid(process->pid);
194 process_writer->set_ppid(process->ppid);
Isabelle Taylore3504972018-02-26 10:27:51 +0000195 for (const auto& field : process->cmdline)
196 process_writer->add_cmdline(field.c_str());
Isabelle Taylord404ea12018-02-19 17:28:01 +0000197 for (auto& thread : process->threads) {
198 auto* thread_writer = process_writer->add_threads();
199 thread_writer->set_tid(thread.second.tid);
200 thread_writer->set_name(thread.second.name);
201 }
202 });
203 trace_packet->Finalize();
204}
205
Anna Zappone45a282a2018-03-07 15:22:25 +0000206// static
207void ProbesProducer::CreateDeviceToInodeMap(
208 const std::string& root_directory,
209 std::map<uint64_t, InodeMap>* block_device_map) {
210 // Return immediately if we've already filled in the system map
211 if (!block_device_map->empty())
212 return;
213 std::queue<std::string> queue;
214 queue.push(root_directory);
215 while (!queue.empty()) {
216 struct dirent* entry;
217 std::string filepath = queue.front();
218 queue.pop();
219 DIR* dir = opendir(filepath.c_str());
220 filepath += "/";
221 if (dir == nullptr)
222 continue;
223 while ((entry = readdir(dir)) != nullptr) {
224 std::string filename = entry->d_name;
225 if (filename == "." || filename == "..")
226 continue;
227 uint64_t inode_number = entry->d_ino;
228 uint64_t block_device_id = 0;
229 InodeMap& inode_map = (*block_device_map)[block_device_id];
230 // Default
231 Type type = protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
232 if (entry->d_type == DT_DIR) {
233 // Continue iterating through files if current entry is a directory
234 queue.push(filepath + filename);
235 type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
236 } else if (entry->d_type == DT_REG) {
237 type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
238 }
239 inode_map[inode_number].first = type;
240 inode_map[inode_number].second.emplace(filepath + filename);
241 }
242 closedir(dir);
243 }
244}
245
Isabelle Taylord404ea12018-02-19 17:28:01 +0000246void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
247 PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
Hector Dearman77451692018-03-08 16:21:13 +0000248 // |id| could be the id of any of the datasources we handle:
249 PERFETTO_DCHECK((delegates_.count(id) + process_stats_sources_.count(id) +
250 file_map_sources_.count(id)) == 1);
251 delegates_.erase(id);
252 process_stats_sources_.erase(id);
253 file_map_sources_.erase(id);
254 watchdogs_.erase(id);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000255}
256
257void ProbesProducer::ConnectWithRetries(const char* socket_name,
258 base::TaskRunner* task_runner) {
259 PERFETTO_DCHECK(state_ == kNotStarted);
260 state_ = kNotConnected;
261
262 ResetConnectionBackoff();
263 socket_name_ = socket_name;
264 task_runner_ = task_runner;
265 Connect();
266}
267
268void ProbesProducer::Connect() {
269 PERFETTO_DCHECK(state_ == kNotConnected);
270 state_ = kConnecting;
271 endpoint_ = ProducerIPCClient::Connect(socket_name_, this, task_runner_);
272}
273
274void ProbesProducer::IncreaseConnectionBackoff() {
275 connection_backoff_ms_ *= 2;
276 if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
277 connection_backoff_ms_ = kMaxConnectionBackoffMs;
278}
279
280void ProbesProducer::ResetConnectionBackoff() {
281 connection_backoff_ms_ = kInitialConnectionBackoffMs;
282}
283
Hector Dearmanc8488032018-03-02 13:12:01 +0000284ProbesProducer::SinkDelegate::SinkDelegate(base::TaskRunner* task_runner,
285 std::unique_ptr<TraceWriter> writer)
286 : task_runner_(task_runner),
287 writer_(std::move(writer)),
288 weak_factory_(this) {}
Isabelle Taylord404ea12018-02-19 17:28:01 +0000289
290ProbesProducer::SinkDelegate::~SinkDelegate() = default;
291
292ProbesProducer::FtraceBundleHandle
Hector Dearmanc8488032018-03-02 13:12:01 +0000293
Isabelle Taylord404ea12018-02-19 17:28:01 +0000294ProbesProducer::SinkDelegate::GetBundleForCpu(size_t) {
295 trace_packet_ = writer_->NewTracePacket();
296 return FtraceBundleHandle(trace_packet_->set_ftrace_events());
297}
298
Hector Dearmanc8488032018-03-02 13:12:01 +0000299void ProbesProducer::SinkDelegate::OnBundleComplete(
300 size_t,
301 FtraceBundleHandle,
302 const FtraceMetadata& metadata) {
Isabelle Taylord404ea12018-02-19 17:28:01 +0000303 trace_packet_->Finalize();
Hector Dearmanc8488032018-03-02 13:12:01 +0000304 if (!metadata.inodes.empty()) {
305 auto weak_this = weak_factory_.GetWeakPtr();
306 auto inodes = metadata.inodes;
307 task_runner_->PostTask([weak_this, inodes] {
308 if (weak_this)
309 weak_this->OnInodes(inodes);
310 });
311 }
312}
313
314void ProbesProducer::SinkDelegate::OnInodes(
315 const std::vector<uint64_t>& inodes) {
316 PERFETTO_DLOG("Saw FtraceBundle with %zu inodes.", inodes.size());
Isabelle Taylord404ea12018-02-19 17:28:01 +0000317}
318
Anna Zappone27ac99c2018-03-06 14:25:35 +0000319ProbesProducer::InodeFileMapDataSource::InodeFileMapDataSource(
Anna Zappone45a282a2018-03-07 15:22:25 +0000320 std::map<uint64_t, InodeMap>* file_system_inodes,
Anna Zappone27ac99c2018-03-06 14:25:35 +0000321 std::unique_ptr<TraceWriter> writer)
Anna Zappone45a282a2018-03-07 15:22:25 +0000322 : file_system_inodes_(file_system_inodes), writer_(std::move(writer)) {}
Anna Zappone27ac99c2018-03-06 14:25:35 +0000323
324ProbesProducer::InodeFileMapDataSource::~InodeFileMapDataSource() = default;
325
326void ProbesProducer::InodeFileMapDataSource::WriteInodes(
327 const FtraceMetadata& metadata) {
328 auto trace_packet = writer_->NewTracePacket();
329 auto inode_file_map = trace_packet->set_inode_file_map();
Anna Zappone45a282a2018-03-07 15:22:25 +0000330 // TODO(azappone): Get block_device_id and mount_points & add to the proto
331 uint64_t block_device_id = 0;
Anna Zappone27ac99c2018-03-06 14:25:35 +0000332 auto inodes = metadata.inodes;
333 for (const auto& inode : inodes) {
334 auto* entry = inode_file_map->add_entries();
335 entry->set_inode_number(inode);
Anna Zappone45a282a2018-03-07 15:22:25 +0000336 auto block_device_map = file_system_inodes_->find(block_device_id);
337 if (block_device_map != file_system_inodes_->end()) {
338 auto inode_map = block_device_map->second.find(inode);
339 if (inode_map != block_device_map->second.end()) {
340 entry->set_type(inode_map->second.first);
341 for (const auto& path : inode_map->second.second)
342 entry->add_paths(path.c_str());
343 }
344 }
Anna Zappone27ac99c2018-03-06 14:25:35 +0000345 }
346 trace_packet->Finalize();
347}
348
Isabelle Taylord404ea12018-02-19 17:28:01 +0000349} // namespace perfetto