• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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/ftrace/ftrace_controller.h"
18 
19 #include <fcntl.h>
20 #include <stdint.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 
27 #include <string>
28 
29 #include "perfetto/base/build_config.h"
30 #include "perfetto/base/logging.h"
31 #include "perfetto/base/time.h"
32 #include "perfetto/ext/base/file_utils.h"
33 #include "perfetto/ext/base/metatrace.h"
34 #include "perfetto/ext/base/string_utils.h"
35 #include "perfetto/ext/tracing/core/trace_writer.h"
36 #include "src/kallsyms/kernel_symbol_map.h"
37 #include "src/kallsyms/lazy_kernel_symbolizer.h"
38 #include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
39 #include "src/traced/probes/ftrace/cpu_reader.h"
40 #include "src/traced/probes/ftrace/cpu_stats_parser.h"
41 #include "src/traced/probes/ftrace/event_info.h"
42 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
43 #include "src/traced/probes/ftrace/ftrace_data_source.h"
44 #include "src/traced/probes/ftrace/ftrace_metadata.h"
45 #include "src/traced/probes/ftrace/ftrace_procfs.h"
46 #include "src/traced/probes/ftrace/ftrace_stats.h"
47 #include "src/traced/probes/ftrace/proto_translation_table.h"
48 #include "src/traced/probes/ftrace/vendor_tracepoints.h"
49 
50 namespace perfetto {
51 namespace {
52 
53 constexpr int kDefaultDrainPeriodMs = 100;
54 constexpr int kMinDrainPeriodMs = 1;
55 constexpr int kMaxDrainPeriodMs = 1000 * 60;
56 
57 // Read at most this many pages of data per cpu per read task. If we hit this
58 // limit on at least one cpu, we stop and repost the read task, letting other
59 // tasks get some cpu time before continuing reading.
60 constexpr size_t kMaxPagesPerCpuPerReadTick = 256;  // 1 MB per cpu
61 
62 // When reading and parsing data for a particular cpu, we do it in batches of
63 // this many pages. In other words, we'll read up to
64 // |kParsingBufferSizePages| into memory, parse them, and then repeat if we
65 // still haven't caught up to the writer. A working set of 32 pages is 128k of
66 // data, which should fit in a typical L2D cache. Furthermore, the batching
67 // limits the memory usage of traced_probes.
68 //
69 // TODO(rsavitski): consider making buffering & parsing page counts independent,
70 // should be a single counter in the cpu_reader, similar to lost_events case.
71 constexpr size_t kParsingBufferSizePages = 32;
72 
ClampDrainPeriodMs(uint32_t drain_period_ms)73 uint32_t ClampDrainPeriodMs(uint32_t drain_period_ms) {
74   if (drain_period_ms == 0) {
75     return kDefaultDrainPeriodMs;
76   }
77   if (drain_period_ms < kMinDrainPeriodMs ||
78       kMaxDrainPeriodMs < drain_period_ms) {
79     PERFETTO_LOG("drain_period_ms was %u should be between %u and %u",
80                  drain_period_ms, kMinDrainPeriodMs, kMaxDrainPeriodMs);
81     return kDefaultDrainPeriodMs;
82   }
83   return drain_period_ms;
84 }
85 
WriteToFile(const char * path,const char * str)86 bool WriteToFile(const char* path, const char* str) {
87   auto fd = base::OpenFile(path, O_WRONLY);
88   if (!fd)
89     return false;
90   const size_t str_len = strlen(str);
91   return base::WriteAll(*fd, str, str_len) == static_cast<ssize_t>(str_len);
92 }
93 
ClearFile(const char * path)94 bool ClearFile(const char* path) {
95   auto fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
96   return !!fd;
97 }
98 
ReadFtraceNowTs(const base::ScopedFile & cpu_stats_fd)99 std::optional<int64_t> ReadFtraceNowTs(const base::ScopedFile& cpu_stats_fd) {
100   PERFETTO_CHECK(cpu_stats_fd);
101 
102   char buf[512];
103   ssize_t res = PERFETTO_EINTR(pread(*cpu_stats_fd, buf, sizeof(buf) - 1, 0));
104   if (res <= 0)
105     return std::nullopt;
106   buf[res] = '\0';
107 
108   FtraceCpuStats stats{};
109   DumpCpuStats(buf, &stats);
110   return static_cast<int64_t>(stats.now_ts * 1000 * 1000 * 1000);
111 }
112 
GetAtraceVendorEvents(FtraceProcfs * tracefs)113 std::map<std::string, std::vector<GroupAndName>> GetAtraceVendorEvents(
114     FtraceProcfs* tracefs) {
115 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
116   if (base::FileExists(vendor_tracepoints::kCategoriesFile)) {
117     std::map<std::string, std::vector<GroupAndName>> vendor_evts;
118     base::Status status =
119         vendor_tracepoints::DiscoverAccessibleVendorTracepointsWithFile(
120             vendor_tracepoints::kCategoriesFile, &vendor_evts, tracefs);
121     if (!status.ok()) {
122       PERFETTO_ELOG("Cannot load vendor categories: %s", status.c_message());
123     }
124     return vendor_evts;
125   } else {
126     AtraceHalWrapper hal;
127     return vendor_tracepoints::DiscoverVendorTracepointsWithHal(&hal, tracefs);
128   }
129 #else
130   base::ignore_result(tracefs);
131   return {};
132 #endif
133 }
134 
135 }  // namespace
136 
137 // Method of last resort to reset ftrace state.
138 // We don't know what state the rest of the system and process is so as far
139 // as possible avoid allocations.
HardResetFtraceState()140 bool HardResetFtraceState() {
141   for (const char* const* item = FtraceProcfs::kTracingPaths; *item; ++item) {
142     std::string prefix(*item);
143     PERFETTO_CHECK(base::EndsWith(prefix, "/"));
144     bool res = true;
145     res &= WriteToFile((prefix + "tracing_on").c_str(), "0");
146     res &= WriteToFile((prefix + "buffer_size_kb").c_str(), "4");
147     // Not checking success because these files might not be accessible on
148     // older or release builds of Android:
149     WriteToFile((prefix + "events/enable").c_str(), "0");
150     WriteToFile((prefix + "events/raw_syscalls/filter").c_str(), "0");
151     WriteToFile((prefix + "current_tracer").c_str(), "nop");
152     res &= ClearFile((prefix + "trace").c_str());
153     if (res)
154       return true;
155   }
156   return false;
157 }
158 
159 // static
Create(base::TaskRunner * runner,Observer * observer)160 std::unique_ptr<FtraceController> FtraceController::Create(
161     base::TaskRunner* runner,
162     Observer* observer) {
163   std::unique_ptr<FtraceProcfs> ftrace_procfs =
164       FtraceProcfs::CreateGuessingMountPoint("");
165   if (!ftrace_procfs)
166     return nullptr;
167 
168   std::unique_ptr<ProtoTranslationTable> table = ProtoTranslationTable::Create(
169       ftrace_procfs.get(), GetStaticEventInfo(), GetStaticCommonFieldsInfo());
170   if (!table)
171     return nullptr;
172 
173   std::map<std::string, std::vector<GroupAndName>> vendor_evts =
174       GetAtraceVendorEvents(ftrace_procfs.get());
175 
176   SyscallTable syscalls = SyscallTable::FromCurrentArch();
177 
178   std::unique_ptr<FtraceConfigMuxer> muxer =
179       std::unique_ptr<FtraceConfigMuxer>(new FtraceConfigMuxer(
180           ftrace_procfs.get(), table.get(), std::move(syscalls), vendor_evts));
181   return std::unique_ptr<FtraceController>(
182       new FtraceController(std::move(ftrace_procfs), std::move(table),
183                            std::move(muxer), runner, observer));
184 }
185 
FtraceController(std::unique_ptr<FtraceProcfs> ftrace_procfs,std::unique_ptr<ProtoTranslationTable> table,std::unique_ptr<FtraceConfigMuxer> muxer,base::TaskRunner * task_runner,Observer * observer)186 FtraceController::FtraceController(std::unique_ptr<FtraceProcfs> ftrace_procfs,
187                                    std::unique_ptr<ProtoTranslationTable> table,
188                                    std::unique_ptr<FtraceConfigMuxer> muxer,
189                                    base::TaskRunner* task_runner,
190                                    Observer* observer)
191     : task_runner_(task_runner),
192       observer_(observer),
193       symbolizer_(new LazyKernelSymbolizer()),
194       primary_(std::move(ftrace_procfs), std::move(table), std::move(muxer)),
195       ftrace_clock_snapshot_(new FtraceClockSnapshot()),
196       weak_factory_(this) {}
197 
~FtraceController()198 FtraceController::~FtraceController() {
199   while (!data_sources_.empty()) {
200     RemoveDataSource(*data_sources_.begin());
201   }
202   PERFETTO_DCHECK(data_sources_.empty());
203   PERFETTO_DCHECK(primary_.started_data_sources.empty());
204   PERFETTO_DCHECK(primary_.per_cpu.empty());
205   PERFETTO_DCHECK(secondary_instances_.empty());
206 }
207 
NowMs() const208 uint64_t FtraceController::NowMs() const {
209   return static_cast<uint64_t>(base::GetWallTimeMs().count());
210 }
211 
StartIfNeeded(FtraceInstanceState * instance)212 void FtraceController::StartIfNeeded(FtraceInstanceState* instance) {
213   using FtraceClock = protos::pbzero::FtraceClock;
214   if (instance->started_data_sources.size() > 1)
215     return;
216 
217   // Lazily allocate the memory used for reading & parsing ftrace. In the case
218   // of multiple ftrace instances, this might already be valid.
219   if (!parsing_mem_.IsValid()) {
220     parsing_mem_ =
221         base::PagedMemory::Allocate(base::kPageSize * kParsingBufferSizePages);
222   }
223 
224   PERFETTO_DCHECK(instance->per_cpu.empty());
225   size_t num_cpus = instance->ftrace_procfs->NumberOfCpus();
226   instance->per_cpu.clear();
227   instance->per_cpu.reserve(num_cpus);
228   size_t period_page_quota =
229       instance->ftrace_config_muxer->GetPerCpuBufferSizePages();
230   for (size_t cpu = 0; cpu < num_cpus; cpu++) {
231     auto reader = std::unique_ptr<CpuReader>(
232         new CpuReader(cpu, instance->table.get(), symbolizer_.get(),
233                       ftrace_clock_snapshot_.get(),
234                       instance->ftrace_procfs->OpenPipeForCpu(cpu)));
235     instance->per_cpu.emplace_back(std::move(reader), period_page_quota);
236   }
237 
238   // Special case for primary instance: if not using the boot clock, take
239   // manual clock snapshots so that the trace parser can do a best effort
240   // conversion back to boot. This is primarily for old kernels that predate
241   // boot support, and therefore default to "global" clock.
242   if (instance == &primary_ && instance->ftrace_config_muxer->ftrace_clock() !=
243                                    FtraceClock::FTRACE_CLOCK_UNSPECIFIED) {
244     cpu_zero_stats_fd_ = primary_.ftrace_procfs->OpenCpuStats(0 /* cpu */);
245     MaybeSnapshotFtraceClock();
246   }
247 
248   // Start a new repeating read task (even if there is already one posted due
249   // to a different ftrace instance). Any old tasks will stop due to generation
250   // checks.
251   auto generation = ++generation_;
252   auto drain_period_ms = GetDrainPeriodMs();
253   auto weak_this = weak_factory_.GetWeakPtr();
254   task_runner_->PostDelayedTask(
255       [weak_this, generation] {
256         if (weak_this)
257           weak_this->ReadTick(generation);
258       },
259       drain_period_ms - (NowMs() % drain_period_ms));
260 }
261 
262 // We handle the ftrace buffers in a repeating task (ReadTick). On a given tick,
263 // we iterate over all per-cpu buffers, parse their contents, and then write out
264 // the serialized packets. This is handled by |CpuReader| instances, which
265 // attempt to read from their respective per-cpu buffer fd until they catch up
266 // to the head of the buffer, or hit a transient error.
267 //
268 // The readers work in batches of |kParsingBufferSizePages| pages for cache
269 // locality, and to limit memory usage.
270 //
271 // However, the reading happens on the primary thread, shared with the rest of
272 // the service (including ipc). If there is a lot of ftrace data to read, we
273 // want to yield to the event loop, re-enqueueing a continuation task at the end
274 // of the immediate queue (letting other enqueued tasks to run before
275 // continuing). Therefore we introduce |kMaxPagesPerCpuPerReadTick|.
276 //
277 // There is also a possibility that the ftrace bandwidth is particularly high.
278 // We do not want to continue trying to catch up to the event stream (via
279 // continuation tasks) without bound, as we want to limit our cpu% usage.  We
280 // assume that given a config saying "per-cpu kernel ftrace buffer is N pages,
281 // and drain every T milliseconds", we should not read more than N pages per
282 // drain period. Therefore we introduce |per_cpu.period_page_quota|. If the
283 // consumer wants to handle a high bandwidth of ftrace events, they should set
284 // the config values appropriately.
ReadTick(int generation)285 void FtraceController::ReadTick(int generation) {
286   metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
287                              metatrace::FTRACE_READ_TICK);
288   if (generation != generation_ || GetStartedDataSourcesCount() == 0) {
289     return;
290   }
291 
292   // Read all cpu buffers with remaining per-period quota.
293   bool all_cpus_done = ReadTickForInstance(&primary_);
294   for (auto& kv : secondary_instances_) {
295     all_cpus_done &= ReadTickForInstance(kv.second.get());
296   }
297 
298   observer_->OnFtraceDataWrittenIntoDataSourceBuffers();
299 
300   // More work to do in this period.
301   auto weak_this = weak_factory_.GetWeakPtr();
302   if (!all_cpus_done) {
303     PERFETTO_DLOG("Reposting immediate ReadTick as there's more work.");
304     task_runner_->PostTask([weak_this, generation] {
305       if (weak_this)
306         weak_this->ReadTick(generation);
307     });
308   } else {
309     // Done until next drain period.
310     size_t period_page_quota =
311         primary_.ftrace_config_muxer->GetPerCpuBufferSizePages();
312     for (auto& per_cpu : primary_.per_cpu)
313       per_cpu.period_page_quota = period_page_quota;
314 
315     for (auto& it : secondary_instances_) {
316       FtraceInstanceState* instance = it.second.get();
317       size_t quota = instance->ftrace_config_muxer->GetPerCpuBufferSizePages();
318       for (auto& per_cpu : instance->per_cpu) {
319         per_cpu.period_page_quota = quota;
320       }
321     }
322 
323     // Snapshot the clock so the data in the next period will be clock synced as
324     // well.
325     MaybeSnapshotFtraceClock();
326 
327     auto drain_period_ms = GetDrainPeriodMs();
328     task_runner_->PostDelayedTask(
329         [weak_this, generation] {
330           if (weak_this)
331             weak_this->ReadTick(generation);
332         },
333         drain_period_ms - (NowMs() % drain_period_ms));
334   }
335 }
336 
ReadTickForInstance(FtraceInstanceState * instance)337 bool FtraceController::ReadTickForInstance(FtraceInstanceState* instance) {
338   if (instance->started_data_sources.empty())
339     return true;
340 
341 #if PERFETTO_DCHECK_IS_ON()
342   // The OnFtraceDataWrittenIntoDataSourceBuffers() below is supposed to clear
343   // all metadata, including the |kernel_addrs| map for symbolization.
344   for (FtraceDataSource* ds : instance->started_data_sources) {
345     FtraceMetadata* ftrace_metadata = ds->mutable_metadata();
346     PERFETTO_DCHECK(ftrace_metadata->kernel_addrs.empty());
347     PERFETTO_DCHECK(ftrace_metadata->last_kernel_addr_index_written == 0);
348   }
349 #endif
350 
351   bool all_cpus_done = true;
352   uint8_t* parsing_buf = reinterpret_cast<uint8_t*>(parsing_mem_.Get());
353   const auto ftrace_clock = instance->ftrace_config_muxer->ftrace_clock();
354   for (size_t i = 0; i < instance->per_cpu.size(); i++) {
355     size_t orig_quota = instance->per_cpu[i].period_page_quota;
356     if (orig_quota == 0)
357       continue;
358 
359     size_t max_pages = std::min(orig_quota, kMaxPagesPerCpuPerReadTick);
360     CpuReader& cpu_reader = *instance->per_cpu[i].reader;
361     cpu_reader.set_ftrace_clock(ftrace_clock);
362     size_t pages_read =
363         cpu_reader.ReadCycle(parsing_buf, kParsingBufferSizePages, max_pages,
364                              instance->started_data_sources);
365 
366     size_t new_quota = (pages_read >= orig_quota) ? 0 : orig_quota - pages_read;
367     instance->per_cpu[i].period_page_quota = new_quota;
368 
369     // Reader got stopped by the cap on the number of pages (to not do too much
370     // work on the shared thread at once), but can read more in this drain
371     // period. Repost the ReadTick (on the immediate queue) to iterate over all
372     // cpus again. In other words, we will keep reposting work for all cpus as
373     // long as at least one of them hits the read page cap each tick. If all
374     // readers catch up to the event stream (pages_read < max_pages), or exceed
375     // their quota, we will stop for the given period.
376     PERFETTO_DCHECK(pages_read <= max_pages);
377     if (pages_read == max_pages && new_quota > 0) {
378       all_cpus_done = false;
379     }
380   }
381   return all_cpus_done;
382 }
383 
GetDrainPeriodMs()384 uint32_t FtraceController::GetDrainPeriodMs() {
385   if (data_sources_.empty())
386     return kDefaultDrainPeriodMs;
387   uint32_t min_drain_period_ms = kMaxDrainPeriodMs + 1;
388   for (const FtraceDataSource* data_source : data_sources_) {
389     if (data_source->config().drain_period_ms() < min_drain_period_ms)
390       min_drain_period_ms = data_source->config().drain_period_ms();
391   }
392   return ClampDrainPeriodMs(min_drain_period_ms);
393 }
394 
Flush(FlushRequestID flush_id)395 void FtraceController::Flush(FlushRequestID flush_id) {
396   metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
397                              metatrace::FTRACE_CPU_FLUSH);
398 
399   FlushForInstance(&primary_);
400   for (auto& it : secondary_instances_) {
401     FlushForInstance(it.second.get());
402   }
403 
404   observer_->OnFtraceDataWrittenIntoDataSourceBuffers();
405 
406   for (FtraceDataSource* data_source : primary_.started_data_sources) {
407     data_source->OnFtraceFlushComplete(flush_id);
408   }
409   for (auto& kv : secondary_instances_) {
410     for (FtraceDataSource* data_source : kv.second->started_data_sources) {
411       data_source->OnFtraceFlushComplete(flush_id);
412     }
413   }
414 }
415 
FlushForInstance(FtraceInstanceState * instance)416 void FtraceController::FlushForInstance(FtraceInstanceState* instance) {
417   if (instance->started_data_sources.empty())
418     return;
419 
420   // Read all cpus in one go, limiting the per-cpu read amount to make sure we
421   // don't get stuck chasing the writer if there's a very high bandwidth of
422   // events.
423   size_t per_cpubuf_size_pages =
424       instance->ftrace_config_muxer->GetPerCpuBufferSizePages();
425   uint8_t* parsing_buf = reinterpret_cast<uint8_t*>(parsing_mem_.Get());
426   for (size_t i = 0; i < instance->per_cpu.size(); i++) {
427     instance->per_cpu[i].reader->ReadCycle(parsing_buf, kParsingBufferSizePages,
428                                            per_cpubuf_size_pages,
429                                            instance->started_data_sources);
430   }
431 }
432 
433 // We are not implicitly flushing on Stop. The tracing service is supposed to
434 // ask for an explicit flush before stopping, unless it needs to perform a
435 // non-graceful stop.
StopIfNeeded(FtraceInstanceState * instance)436 void FtraceController::StopIfNeeded(FtraceInstanceState* instance) {
437   if (!instance->started_data_sources.empty())
438     return;
439 
440   instance->per_cpu.clear();
441   if (instance == &primary_) {
442     cpu_zero_stats_fd_.reset();
443   }
444   // Muxer cannot change the current_tracer until we close the trace pipe fds
445   // (i.e. per_cpu). Hence an explicit request here.
446   instance->ftrace_config_muxer->ResetCurrentTracer();
447 
448   DestroyIfUnusedSeconaryInstance(instance);
449 
450   // Clean up global state if done with all data sources.
451   if (!data_sources_.empty())
452     return;
453 
454   if (!retain_ksyms_on_stop_) {
455     symbolizer_->Destroy();
456   }
457   retain_ksyms_on_stop_ = false;
458 
459   if (parsing_mem_.IsValid()) {
460     parsing_mem_.AdviseDontNeed(parsing_mem_.Get(), parsing_mem_.size());
461   }
462 }
463 
AddDataSource(FtraceDataSource * data_source)464 bool FtraceController::AddDataSource(FtraceDataSource* data_source) {
465   if (!ValidConfig(data_source->config()))
466     return false;
467 
468   FtraceInstanceState* instance =
469       GetOrCreateInstance(data_source->config().instance_name());
470   if (!instance)
471     return false;
472 
473   // note: from this point onwards, need to not leak a possibly created
474   // instance if returning early.
475 
476   FtraceConfigId config_id = next_cfg_id_++;
477   if (!instance->ftrace_config_muxer->SetupConfig(
478           config_id, data_source->config(),
479           data_source->mutable_setup_errors())) {
480     DestroyIfUnusedSeconaryInstance(instance);
481     return false;
482   }
483 
484   const FtraceDataSourceConfig* ds_config =
485       instance->ftrace_config_muxer->GetDataSourceConfig(config_id);
486   auto it_and_inserted = data_sources_.insert(data_source);
487   PERFETTO_DCHECK(it_and_inserted.second);
488   data_source->Initialize(config_id, ds_config);
489   return true;
490 }
491 
StartDataSource(FtraceDataSource * data_source)492 bool FtraceController::StartDataSource(FtraceDataSource* data_source) {
493   PERFETTO_DCHECK(data_sources_.count(data_source) > 0);
494 
495   FtraceConfigId config_id = data_source->config_id();
496   PERFETTO_CHECK(config_id);
497 
498   FtraceInstanceState* instance =
499       GetOrCreateInstance(data_source->config().instance_name());
500   PERFETTO_CHECK(instance);
501 
502   if (!instance->ftrace_config_muxer->ActivateConfig(config_id))
503     return false;
504   instance->started_data_sources.insert(data_source);
505   StartIfNeeded(instance);
506 
507   // Parse kernel symbols if required by the config. This can be an expensive
508   // operation (cpu-bound for 500ms+), so delay the StartDataSource
509   // acknowledgement until after we're done. This lets a consumer wait for the
510   // expensive work to be done by waiting on the "all data sources started"
511   // fence. This helps isolate the effects of the cpu-bound work on
512   // frequency scaling of cpus when recording benchmarks (b/236143653).
513   // Note that we're already recording data into the kernel ftrace
514   // buffers while doing the symbol parsing.
515   if (data_source->config().symbolize_ksyms()) {
516     symbolizer_->GetOrCreateKernelSymbolMap();
517     // If at least one config sets the KSYMS_RETAIN flag, keep the ksysm map
518     // around in StopIfNeeded().
519     const auto KRET = FtraceConfig::KSYMS_RETAIN;
520     retain_ksyms_on_stop_ |= data_source->config().ksyms_mem_policy() == KRET;
521   }
522 
523   return true;
524 }
525 
RemoveDataSource(FtraceDataSource * data_source)526 void FtraceController::RemoveDataSource(FtraceDataSource* data_source) {
527   size_t removed = data_sources_.erase(data_source);
528   if (!removed)
529     return;  // can happen if AddDataSource failed
530 
531   FtraceInstanceState* instance =
532       GetOrCreateInstance(data_source->config().instance_name());
533   PERFETTO_CHECK(instance);
534 
535   instance->ftrace_config_muxer->RemoveConfig(data_source->config_id());
536   instance->started_data_sources.erase(data_source);
537   StopIfNeeded(instance);
538 }
539 
DumpFtraceStats(FtraceDataSource * data_source,FtraceStats * stats_out)540 void FtraceController::DumpFtraceStats(FtraceDataSource* data_source,
541                                        FtraceStats* stats_out) {
542   FtraceInstanceState* instance =
543       GetInstance(data_source->config().instance_name());
544   PERFETTO_DCHECK(instance);
545   if (!instance)
546     return;
547 
548   DumpAllCpuStats(instance->ftrace_procfs.get(), stats_out);
549   if (symbolizer_ && symbolizer_->is_valid()) {
550     auto* symbol_map = symbolizer_->GetOrCreateKernelSymbolMap();
551     stats_out->kernel_symbols_parsed =
552         static_cast<uint32_t>(symbol_map->num_syms());
553     stats_out->kernel_symbols_mem_kb =
554         static_cast<uint32_t>(symbol_map->size_bytes() / 1024);
555   }
556 }
557 
MaybeSnapshotFtraceClock()558 void FtraceController::MaybeSnapshotFtraceClock() {
559   if (!cpu_zero_stats_fd_)
560     return;
561 
562   auto ftrace_clock = primary_.ftrace_config_muxer->ftrace_clock();
563   PERFETTO_DCHECK(ftrace_clock != protos::pbzero::FTRACE_CLOCK_UNSPECIFIED);
564 
565   // Snapshot the boot clock *before* reading CPU stats so that
566   // two clocks are as close togher as possible (i.e. if it was the
567   // other way round, we'd skew by the const of string parsing).
568   ftrace_clock_snapshot_->boot_clock_ts = base::GetBootTimeNs().count();
569 
570   // A value of zero will cause this snapshot to be skipped.
571   ftrace_clock_snapshot_->ftrace_clock_ts =
572       ReadFtraceNowTs(cpu_zero_stats_fd_).value_or(0);
573 }
574 
GetStartedDataSourcesCount() const575 size_t FtraceController::GetStartedDataSourcesCount() const {
576   size_t cnt = primary_.started_data_sources.size();
577   for (auto& it : secondary_instances_) {
578     cnt += it.second->started_data_sources.size();
579   }
580   return cnt;
581 }
582 
FtraceInstanceState(std::unique_ptr<FtraceProcfs> ft,std::unique_ptr<ProtoTranslationTable> ptt,std::unique_ptr<FtraceConfigMuxer> fcm)583 FtraceController::FtraceInstanceState::FtraceInstanceState(
584     std::unique_ptr<FtraceProcfs> ft,
585     std::unique_ptr<ProtoTranslationTable> ptt,
586     std::unique_ptr<FtraceConfigMuxer> fcm)
587     : ftrace_procfs(std::move(ft)),
588       table(std::move(ptt)),
589       ftrace_config_muxer(std::move(fcm)) {}
590 
GetOrCreateInstance(const std::string & instance_name)591 FtraceController::FtraceInstanceState* FtraceController::GetOrCreateInstance(
592     const std::string& instance_name) {
593   FtraceInstanceState* maybe_existing = GetInstance(instance_name);
594   if (maybe_existing)
595     return maybe_existing;
596 
597   PERFETTO_DCHECK(!instance_name.empty());
598   std::unique_ptr<FtraceInstanceState> instance =
599       CreateSecondaryInstance(instance_name);
600   if (!instance)
601     return nullptr;
602 
603   auto it_and_inserted = secondary_instances_.emplace(
604       std::piecewise_construct, std::forward_as_tuple(instance_name),
605       std::forward_as_tuple(std::move(instance)));
606   PERFETTO_CHECK(it_and_inserted.second);
607   return it_and_inserted.first->second.get();
608 }
609 
GetInstance(const std::string & instance_name)610 FtraceController::FtraceInstanceState* FtraceController::GetInstance(
611     const std::string& instance_name) {
612   if (instance_name.empty())
613     return &primary_;
614 
615   auto it = secondary_instances_.find(instance_name);
616   return it != secondary_instances_.end() ? it->second.get() : nullptr;
617 }
618 
DestroyIfUnusedSeconaryInstance(FtraceInstanceState * instance)619 void FtraceController::DestroyIfUnusedSeconaryInstance(
620     FtraceInstanceState* instance) {
621   if (instance == &primary_)
622     return;
623   for (auto it = secondary_instances_.begin(); it != secondary_instances_.end();
624        ++it) {
625     if (it->second.get() == instance &&
626         instance->ftrace_config_muxer->GetDataSourcesCount() == 0) {
627       // no data sources left referencing this secondary instance
628       secondary_instances_.erase(it);
629       return;
630     }
631   }
632   PERFETTO_FATAL("Bug in ftrace instance lifetimes");
633 }
634 
635 // TODO(rsavitski): dedupe with FtraceController::Create.
636 std::unique_ptr<FtraceController::FtraceInstanceState>
CreateSecondaryInstance(const std::string & instance_name)637 FtraceController::CreateSecondaryInstance(const std::string& instance_name) {
638   std::optional<std::string> instance_path = AbsolutePathForInstance(
639       primary_.ftrace_procfs->GetRootPath(), instance_name);
640   if (!instance_path.has_value()) {
641     PERFETTO_ELOG("Invalid ftrace instance name: \"%s\"",
642                   instance_name.c_str());
643     return nullptr;
644   }
645 
646   auto ftrace_procfs = FtraceProcfs::Create(*instance_path);
647   if (!ftrace_procfs) {
648     PERFETTO_ELOG("Failed to create ftrace procfs for \"%s\"",
649                   instance_path->c_str());
650     return nullptr;
651   }
652 
653   auto table = ProtoTranslationTable::Create(
654       ftrace_procfs.get(), GetStaticEventInfo(), GetStaticCommonFieldsInfo());
655   if (!table) {
656     PERFETTO_ELOG("Failed to create proto translation table for \"%s\"",
657                   instance_path->c_str());
658     return nullptr;
659   }
660 
661   // secondary instances don't support atrace and vendor tracepoint HAL
662   std::map<std::string, std::vector<GroupAndName>> vendor_evts;
663 
664   auto syscalls = SyscallTable::FromCurrentArch();
665 
666   auto muxer = std::unique_ptr<FtraceConfigMuxer>(new FtraceConfigMuxer(
667       ftrace_procfs.get(), table.get(), std::move(syscalls), vendor_evts,
668       /* secondary_instance= */ true));
669   return std::unique_ptr<FtraceInstanceState>(new FtraceInstanceState(
670       std::move(ftrace_procfs), std::move(table), std::move(muxer)));
671 }
672 
673 // TODO(rsavitski): we want to eventually add support for the default
674 // (primary_) tracefs path to be an instance itself, at which point we'll need
675 // to be careful to distinguish the tracefs mount point from the default
676 // instance path.
677 // static
AbsolutePathForInstance(const std::string & tracefs_root,const std::string & raw_cfg_name)678 std::optional<std::string> FtraceController::AbsolutePathForInstance(
679     const std::string& tracefs_root,
680     const std::string& raw_cfg_name) {
681   if (base::Contains(raw_cfg_name, '/') ||
682       base::StartsWith(raw_cfg_name, "..")) {
683     return std::nullopt;
684   }
685 
686   // ARM64 pKVM hypervisor tracing emulates an instance, but is not under
687   // instances/, we special-case that name for now.
688   if (raw_cfg_name == "hyp") {
689     std::string hyp_path = tracefs_root + "hyp/";
690     PERFETTO_LOG(
691         "Config specified reserved \"hyp\" instance name, using %s for events.",
692         hyp_path.c_str());
693     return std::make_optional(hyp_path);
694   }
695 
696   return tracefs_root + "instances/" + raw_cfg_name + "/";
697 }
698 
699 FtraceController::Observer::~Observer() = default;
700 
701 }  // namespace perfetto
702