blob: af690222bc083a22f95e15010c31dce11a4b5de1 [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 Zappone4ea73c02018-03-09 16:01:21 +000020#include <sys/stat.h>
Anna Zappone27ac99c2018-03-06 14:25:35 +000021#include <queue>
Isabelle Taylord404ea12018-02-19 17:28:01 +000022#include <string>
23
24#include "perfetto/base/logging.h"
25#include "perfetto/traced/traced.h"
26#include "perfetto/tracing/core/data_source_config.h"
27#include "perfetto/tracing/core/data_source_descriptor.h"
Hector Dearmana89cc572018-02-23 12:02:58 +000028#include "perfetto/tracing/core/ftrace_config.h"
Isabelle Taylord404ea12018-02-19 17:28:01 +000029#include "perfetto/tracing/core/trace_config.h"
30#include "perfetto/tracing/core/trace_packet.h"
31
32#include "src/process_stats/file_utils.h"
33#include "src/process_stats/procfs_utils.h"
34
Anna Zappone27ac99c2018-03-06 14:25:35 +000035#include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
Isabelle Taylord404ea12018-02-19 17:28:01 +000036#include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
37#include "perfetto/trace/ps/process_tree.pbzero.h"
38#include "perfetto/trace/trace_packet.pbzero.h"
39
40namespace perfetto {
41namespace {
42
43uint64_t kInitialConnectionBackoffMs = 100;
44uint64_t kMaxConnectionBackoffMs = 30 * 1000;
45const char* kFtraceSourceName = "com.google.perfetto.ftrace";
46const char* kProcessStatsSourceName = "com.google.perfetto.process_stats";
Anna Zappone27ac99c2018-03-06 14:25:35 +000047const char* kInodeFileMapSourceName = "com.google.perfetto.inode_file_map";
Isabelle Taylord404ea12018-02-19 17:28:01 +000048
49} // namespace.
50
51// State transition diagram:
52// +----------------------------+
53// v +
54// NotStarted -> NotConnected -> Connecting -> Connected
55// ^ +
56// +--------------+
57//
58
Hector Dearmanc8488032018-03-02 13:12:01 +000059ProbesProducer::ProbesProducer() {}
Isabelle Taylord404ea12018-02-19 17:28:01 +000060ProbesProducer::~ProbesProducer() = default;
61
62void ProbesProducer::OnConnect() {
63 PERFETTO_DCHECK(state_ == kConnecting);
64 state_ = kConnected;
65 ResetConnectionBackoff();
66 PERFETTO_LOG("Connected to the service");
67
68 DataSourceDescriptor ftrace_descriptor;
69 ftrace_descriptor.set_name(kFtraceSourceName);
Lalit Magantia88807d2018-03-05 18:21:38 +000070 endpoint_->RegisterDataSource(ftrace_descriptor, [](DataSourceInstanceID) {});
Isabelle Taylord404ea12018-02-19 17:28:01 +000071
72 DataSourceDescriptor process_stats_descriptor;
73 process_stats_descriptor.set_name(kProcessStatsSourceName);
74 endpoint_->RegisterDataSource(process_stats_descriptor,
Lalit Magantia88807d2018-03-05 18:21:38 +000075 [](DataSourceInstanceID) {});
Anna Zappone27ac99c2018-03-06 14:25:35 +000076
77 DataSourceDescriptor inode_map_descriptor;
78 inode_map_descriptor.set_name(kInodeFileMapSourceName);
79 endpoint_->RegisterDataSource(inode_map_descriptor,
80 [](DataSourceInstanceID) {});
Isabelle Taylord404ea12018-02-19 17:28:01 +000081}
82
83void ProbesProducer::OnDisconnect() {
84 PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
85 state_ = kNotConnected;
86 PERFETTO_LOG("Disconnected from tracing service");
87 IncreaseConnectionBackoff();
88
89 // TODO(hjd): Erase all sinks and add e2e test for this.
90 task_runner_->PostDelayedTask([this] { this->Connect(); },
91 connection_backoff_ms_);
92}
93
94void ProbesProducer::CreateDataSourceInstance(
95 DataSourceInstanceID id,
96 const DataSourceConfig& source_config) {
Isabelle Taylord404ea12018-02-19 17:28:01 +000097 if (source_config.name() == kFtraceSourceName) {
98 CreateFtraceDataSourceInstance(id, source_config);
Anna Zappone27ac99c2018-03-06 14:25:35 +000099 } else if (source_config.name() == kInodeFileMapSourceName) {
100 CreateInodeFileMapDataSourceInstance(id, source_config);
Hector Dearman77451692018-03-08 16:21:13 +0000101 } else if (source_config.name() == kProcessStatsSourceName) {
102 CreateProcessStatsDataSourceInstance(id, source_config);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000103 } else {
104 PERFETTO_ELOG("Data source name: %s not recognised.",
105 source_config.name().c_str());
106 }
107}
108
Anna Zappone27ac99c2018-03-06 14:25:35 +0000109void ProbesProducer::AddWatchdogsTimer(DataSourceInstanceID id,
110 const DataSourceConfig& source_config) {
111 if (source_config.trace_duration_ms() != 0)
112 watchdogs_.emplace(id, base::Watchdog::GetInstance()->CreateFatalTimer(
113 5000 + 2 * source_config.trace_duration_ms()));
114}
115
Isabelle Taylord404ea12018-02-19 17:28:01 +0000116void ProbesProducer::CreateFtraceDataSourceInstance(
117 DataSourceInstanceID id,
118 const DataSourceConfig& source_config) {
119 // Don't retry if FtraceController::Create() failed once.
120 // This can legitimately happen on user builds where we cannot access the
121 // debug paths, e.g., because of SELinux rules.
122 if (ftrace_creation_failed_)
123 return;
124
125 // Lazily create on the first instance.
126 if (!ftrace_) {
127 ftrace_ = FtraceController::Create(task_runner_);
128
129 if (!ftrace_) {
130 PERFETTO_ELOG("Failed to create FtraceController");
131 ftrace_creation_failed_ = true;
132 return;
133 }
134
135 ftrace_->DisableAllEvents();
136 ftrace_->ClearTrace();
137 }
138
139 PERFETTO_LOG("Ftrace start (id=%" PRIu64 ", target_buf=%" PRIu32 ")", id,
140 source_config.target_buffer());
141
Hector Dearmana89cc572018-02-23 12:02:58 +0000142 FtraceConfig proto_config = source_config.ftrace_config();
Isabelle Taylord404ea12018-02-19 17:28:01 +0000143
144 // TODO(hjd): Static cast is bad, target_buffer() should return a BufferID.
145 auto trace_writer = endpoint_->CreateTraceWriter(
146 static_cast<BufferID>(source_config.target_buffer()));
Hector Dearmanc8488032018-03-02 13:12:01 +0000147 auto delegate = std::unique_ptr<SinkDelegate>(
148 new SinkDelegate(task_runner_, std::move(trace_writer)));
Isabelle Taylord404ea12018-02-19 17:28:01 +0000149 auto sink = ftrace_->CreateSink(std::move(proto_config), delegate.get());
Hector Dearmanee3c49d2018-02-28 14:10:22 +0000150 if (!sink) {
151 PERFETTO_ELOG("Failed to start tracing (maybe someone else is using it?)");
152 return;
153 }
Isabelle Taylord404ea12018-02-19 17:28:01 +0000154 delegate->sink(std::move(sink));
155 delegates_.emplace(id, std::move(delegate));
Anna Zappone27ac99c2018-03-06 14:25:35 +0000156 AddWatchdogsTimer(id, source_config);
157}
158
159void ProbesProducer::CreateInodeFileMapDataSourceInstance(
160 DataSourceInstanceID id,
161 const DataSourceConfig& source_config) {
162 PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
163 id, source_config.target_buffer());
164 auto trace_writer = endpoint_->CreateTraceWriter(
165 static_cast<BufferID>(source_config.target_buffer()));
Anna Zappone45a282a2018-03-07 15:22:25 +0000166 CreateDeviceToInodeMap("/system/", &system_inodes_);
Anna Zappone27ac99c2018-03-06 14:25:35 +0000167 auto file_map_source = std::unique_ptr<InodeFileMapDataSource>(
Anna Zappone45a282a2018-03-07 15:22:25 +0000168 new InodeFileMapDataSource(&system_inodes_, std::move(trace_writer)));
Anna Zappone27ac99c2018-03-06 14:25:35 +0000169 file_map_sources_.emplace(id, std::move(file_map_source));
170 AddWatchdogsTimer(id, source_config);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000171}
172
173void ProbesProducer::CreateProcessStatsDataSourceInstance(
Hector Dearman77451692018-03-08 16:21:13 +0000174 DataSourceInstanceID id,
Isabelle Taylord404ea12018-02-19 17:28:01 +0000175 const DataSourceConfig& source_config) {
Hector Dearman77451692018-03-08 16:21:13 +0000176 PERFETTO_DCHECK(process_stats_sources_.count(id) == 0);
177 process_stats_sources_.insert(id);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000178 auto trace_writer = endpoint_->CreateTraceWriter(
179 static_cast<BufferID>(source_config.target_buffer()));
180 procfs_utils::ProcessMap processes;
181 auto trace_packet = trace_writer->NewTracePacket();
182 protos::pbzero::ProcessTree* process_tree = trace_packet->set_process_tree();
183
184 file_utils::ForEachPidInProcPath(
185 "/proc", [&processes, &process_tree](int pid) {
186 if (!processes.count(pid)) {
187 if (procfs_utils::ReadTgid(pid) != pid)
188 return;
189 processes[pid] = procfs_utils::ReadProcessInfo(pid);
190 }
191 ProcessInfo* process = processes[pid].get();
192 procfs_utils::ReadProcessThreads(process);
193 auto* process_writer = process_tree->add_processes();
194 process_writer->set_pid(process->pid);
195 process_writer->set_ppid(process->ppid);
Isabelle Taylore3504972018-02-26 10:27:51 +0000196 for (const auto& field : process->cmdline)
197 process_writer->add_cmdline(field.c_str());
Isabelle Taylord404ea12018-02-19 17:28:01 +0000198 for (auto& thread : process->threads) {
199 auto* thread_writer = process_writer->add_threads();
200 thread_writer->set_tid(thread.second.tid);
201 thread_writer->set_name(thread.second.name);
202 }
203 });
204 trace_packet->Finalize();
205}
206
Anna Zappone45a282a2018-03-07 15:22:25 +0000207// static
208void ProbesProducer::CreateDeviceToInodeMap(
209 const std::string& root_directory,
Anna Zappone4ea73c02018-03-09 16:01:21 +0000210 std::map<uint32_t, InodeMap>* block_device_map) {
Anna Zappone45a282a2018-03-07 15:22:25 +0000211 // Return immediately if we've already filled in the system map
212 if (!block_device_map->empty())
213 return;
214 std::queue<std::string> queue;
215 queue.push(root_directory);
216 while (!queue.empty()) {
217 struct dirent* entry;
218 std::string filepath = queue.front();
219 queue.pop();
220 DIR* dir = opendir(filepath.c_str());
221 filepath += "/";
222 if (dir == nullptr)
223 continue;
224 while ((entry = readdir(dir)) != nullptr) {
225 std::string filename = entry->d_name;
226 if (filename == "." || filename == "..")
227 continue;
228 uint64_t inode_number = entry->d_ino;
Anna Zappone4ea73c02018-03-09 16:01:21 +0000229 struct stat buf;
230 if (lstat(filepath.c_str(), &buf) != 0)
231 continue;
232 uint32_t block_device_id = buf.st_dev;
Anna Zappone45a282a2018-03-07 15:22:25 +0000233 InodeMap& inode_map = (*block_device_map)[block_device_id];
234 // Default
235 Type type = protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
Anna Zappone4ea73c02018-03-09 16:01:21 +0000236 // Readdir and stat not guaranteed to have directory info for all systems
237 if (entry->d_type == DT_DIR || S_ISDIR(buf.st_mode)) {
Anna Zappone45a282a2018-03-07 15:22:25 +0000238 // Continue iterating through files if current entry is a directory
239 queue.push(filepath + filename);
240 type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
Anna Zappone4ea73c02018-03-09 16:01:21 +0000241 } else if (entry->d_type == DT_REG || S_ISREG(buf.st_mode)) {
Anna Zappone45a282a2018-03-07 15:22:25 +0000242 type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
243 }
244 inode_map[inode_number].first = type;
245 inode_map[inode_number].second.emplace(filepath + filename);
246 }
247 closedir(dir);
248 }
249}
250
Isabelle Taylord404ea12018-02-19 17:28:01 +0000251void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
252 PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
Hector Dearman77451692018-03-08 16:21:13 +0000253 // |id| could be the id of any of the datasources we handle:
254 PERFETTO_DCHECK((delegates_.count(id) + process_stats_sources_.count(id) +
255 file_map_sources_.count(id)) == 1);
256 delegates_.erase(id);
257 process_stats_sources_.erase(id);
258 file_map_sources_.erase(id);
259 watchdogs_.erase(id);
Isabelle Taylord404ea12018-02-19 17:28:01 +0000260}
261
262void ProbesProducer::ConnectWithRetries(const char* socket_name,
263 base::TaskRunner* task_runner) {
264 PERFETTO_DCHECK(state_ == kNotStarted);
265 state_ = kNotConnected;
266
267 ResetConnectionBackoff();
268 socket_name_ = socket_name;
269 task_runner_ = task_runner;
270 Connect();
271}
272
273void ProbesProducer::Connect() {
274 PERFETTO_DCHECK(state_ == kNotConnected);
275 state_ = kConnecting;
276 endpoint_ = ProducerIPCClient::Connect(socket_name_, this, task_runner_);
277}
278
279void ProbesProducer::IncreaseConnectionBackoff() {
280 connection_backoff_ms_ *= 2;
281 if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
282 connection_backoff_ms_ = kMaxConnectionBackoffMs;
283}
284
285void ProbesProducer::ResetConnectionBackoff() {
286 connection_backoff_ms_ = kInitialConnectionBackoffMs;
287}
288
Hector Dearmanc8488032018-03-02 13:12:01 +0000289ProbesProducer::SinkDelegate::SinkDelegate(base::TaskRunner* task_runner,
290 std::unique_ptr<TraceWriter> writer)
291 : task_runner_(task_runner),
292 writer_(std::move(writer)),
293 weak_factory_(this) {}
Isabelle Taylord404ea12018-02-19 17:28:01 +0000294
295ProbesProducer::SinkDelegate::~SinkDelegate() = default;
296
297ProbesProducer::FtraceBundleHandle
Hector Dearmanc8488032018-03-02 13:12:01 +0000298
Isabelle Taylord404ea12018-02-19 17:28:01 +0000299ProbesProducer::SinkDelegate::GetBundleForCpu(size_t) {
300 trace_packet_ = writer_->NewTracePacket();
301 return FtraceBundleHandle(trace_packet_->set_ftrace_events());
302}
303
Hector Dearmanc8488032018-03-02 13:12:01 +0000304void ProbesProducer::SinkDelegate::OnBundleComplete(
305 size_t,
306 FtraceBundleHandle,
307 const FtraceMetadata& metadata) {
Isabelle Taylord404ea12018-02-19 17:28:01 +0000308 trace_packet_->Finalize();
Hector Dearmanc8488032018-03-02 13:12:01 +0000309 if (!metadata.inodes.empty()) {
310 auto weak_this = weak_factory_.GetWeakPtr();
311 auto inodes = metadata.inodes;
312 task_runner_->PostTask([weak_this, inodes] {
313 if (weak_this)
314 weak_this->OnInodes(inodes);
315 });
316 }
317}
318
319void ProbesProducer::SinkDelegate::OnInodes(
Anna Zappone4ea73c02018-03-09 16:01:21 +0000320 const std::vector<std::pair<uint64_t, uint32_t>>& inodes) {
Hector Dearmanc8488032018-03-02 13:12:01 +0000321 PERFETTO_DLOG("Saw FtraceBundle with %zu inodes.", inodes.size());
Isabelle Taylord404ea12018-02-19 17:28:01 +0000322}
323
Anna Zappone27ac99c2018-03-06 14:25:35 +0000324ProbesProducer::InodeFileMapDataSource::InodeFileMapDataSource(
Anna Zappone4ea73c02018-03-09 16:01:21 +0000325 std::map<uint32_t, InodeMap>* file_system_inodes,
Anna Zappone27ac99c2018-03-06 14:25:35 +0000326 std::unique_ptr<TraceWriter> writer)
Anna Zappone45a282a2018-03-07 15:22:25 +0000327 : file_system_inodes_(file_system_inodes), writer_(std::move(writer)) {}
Anna Zappone27ac99c2018-03-06 14:25:35 +0000328
329ProbesProducer::InodeFileMapDataSource::~InodeFileMapDataSource() = default;
330
331void ProbesProducer::InodeFileMapDataSource::WriteInodes(
332 const FtraceMetadata& metadata) {
333 auto trace_packet = writer_->NewTracePacket();
334 auto inode_file_map = trace_packet->set_inode_file_map();
Anna Zappone4ea73c02018-03-09 16:01:21 +0000335 // TODO(azappone): Get mount_points & add to the proto
Anna Zappone27ac99c2018-03-06 14:25:35 +0000336 auto inodes = metadata.inodes;
337 for (const auto& inode : inodes) {
Anna Zappone4ea73c02018-03-09 16:01:21 +0000338 uint32_t block_device_id = inode.first;
339 uint64_t inode_number = inode.second;
Anna Zappone27ac99c2018-03-06 14:25:35 +0000340 auto* entry = inode_file_map->add_entries();
Anna Zappone4ea73c02018-03-09 16:01:21 +0000341 entry->set_inode_number(inode_number);
Anna Zappone45a282a2018-03-07 15:22:25 +0000342 auto block_device_map = file_system_inodes_->find(block_device_id);
343 if (block_device_map != file_system_inodes_->end()) {
Anna Zappone4ea73c02018-03-09 16:01:21 +0000344 auto inode_map = block_device_map->second.find(inode_number);
Anna Zappone45a282a2018-03-07 15:22:25 +0000345 if (inode_map != block_device_map->second.end()) {
346 entry->set_type(inode_map->second.first);
347 for (const auto& path : inode_map->second.second)
348 entry->add_paths(path.c_str());
349 }
350 }
Anna Zappone27ac99c2018-03-06 14:25:35 +0000351 }
352 trace_packet->Finalize();
353}
354
Isabelle Taylord404ea12018-02-19 17:28:01 +0000355} // namespace perfetto