xref: /aosp_15_r20/external/perfetto/src/traced/probes/ftrace/ftrace_data_source.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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 #include "src/traced/probes/ftrace/ftrace_data_source.h"
18 
19 #include "perfetto/ext/base/string_splitter.h"
20 #include "perfetto/ext/base/string_utils.h"
21 #include "perfetto/ext/base/string_view.h"
22 #include "perfetto/ext/base/subprocess.h"
23 #include "perfetto/protozero/scattered_heap_buffer.h"
24 #include "perfetto/tracing/core/data_source_descriptor.h"
25 #include "src/traced/probes/ftrace/cpu_reader.h"
26 #include "src/traced/probes/ftrace/ftrace_controller.h"
27 
28 #include "protos/perfetto/common/ftrace_descriptor.pbzero.h"
29 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
30 #include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"
31 #include "protos/perfetto/trace/trace_packet.pbzero.h"
32 
33 namespace perfetto {
34 namespace {
35 
FillFtraceDataSourceDescriptor(DataSourceDescriptor * dsd)36 void FillFtraceDataSourceDescriptor(DataSourceDescriptor* dsd) {
37   protozero::HeapBuffered<protos::pbzero::FtraceDescriptor> ftd;
38 
39 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
40   base::Subprocess p({"/system/bin/atrace", "--list_categories"});
41   p.args.stdin_mode = base::Subprocess::InputMode::kDevNull;
42   p.args.stdout_mode = base::Subprocess::OutputMode::kBuffer;
43   p.args.stderr_mode = base::Subprocess::OutputMode::kBuffer;
44   bool res = p.Call(/*timeout_ms=*/20000);
45   if (res) {
46     for (base::StringSplitter ss(std::move(p.output()), '\n'); ss.Next();) {
47       base::StringView line(ss.cur_token(), ss.cur_token_size());
48       size_t pos = line.find(" - ");
49       if (pos == line.npos) {
50         continue;
51       }
52       base::StringView name = line.substr(0, pos);
53       // Trim initial whitespaces
54       auto it = std::find_if(name.begin(), name.end(),
55                              [](char c) { return c != ' '; });
56       name = name.substr(static_cast<size_t>(it - name.begin()));
57 
58       base::StringView desc = line.substr(pos + 3);
59 
60       protos::pbzero::FtraceDescriptor::AtraceCategory* cat =
61           ftd->add_atrace_categories();
62       cat->set_name(name.data(), name.size());
63       cat->set_description(desc.data(), desc.size());
64     }
65   } else {
66     PERFETTO_ELOG("Failed to run atrace --list_categories code(%d): %s",
67                   p.returncode(), p.output().c_str());
68   }
69 #endif
70 
71   dsd->set_ftrace_descriptor_raw(ftd.SerializeAsString());
72 }
73 
74 }  // namespace
75 
76 // static
77 const ProbesDataSource::Descriptor FtraceDataSource::descriptor = {
78     /*name*/ "linux.ftrace",
79     /*flags*/ Descriptor::kFlagsNone,
80     /*fill_descriptor_func*/ &FillFtraceDataSourceDescriptor,
81 };
82 
FtraceDataSource(base::WeakPtr<FtraceController> controller_weak,TracingSessionID session_id,const FtraceConfig & config,std::unique_ptr<TraceWriter> writer)83 FtraceDataSource::FtraceDataSource(
84     base::WeakPtr<FtraceController> controller_weak,
85     TracingSessionID session_id,
86     const FtraceConfig& config,
87     std::unique_ptr<TraceWriter> writer)
88     : ProbesDataSource(session_id, &descriptor),
89       config_(config),
90       writer_(std::move(writer)),
91       controller_weak_(std::move(controller_weak)) {}
92 
~FtraceDataSource()93 FtraceDataSource::~FtraceDataSource() {
94   if (controller_weak_)
95     controller_weak_->RemoveDataSource(this);
96 }
97 
Initialize(FtraceConfigId config_id,const FtraceDataSourceConfig * parsing_config)98 void FtraceDataSource::Initialize(
99     FtraceConfigId config_id,
100     const FtraceDataSourceConfig* parsing_config) {
101   PERFETTO_CHECK(config_id);
102   config_id_ = config_id;
103   parsing_config_ = parsing_config;
104 }
105 
Start()106 void FtraceDataSource::Start() {
107   if (!controller_weak_)
108     return;
109 
110   PERFETTO_CHECK(config_id_);
111   if (!controller_weak_->StartDataSource(this))
112     return;
113 
114   // Note: recording is already active by this point, so the buffer stats are
115   // likely already non-zero even if this is the only ftrace data source.
116   controller_weak_->DumpFtraceStats(this, &stats_before_);
117 
118   // If serialising pre-existing ftrace data, emit a special packet so that
119   // trace_processor doesn't filter out data before start-of-trace.
120   if (config_.preserve_ftrace_buffer()) {
121     auto stats_packet = writer_->NewTracePacket();
122     auto* stats = stats_packet->set_ftrace_stats();
123     stats->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
124     stats->set_preserve_ftrace_buffer(true);
125   }
126 }
127 
Flush(FlushRequestID flush_request_id,std::function<void ()> callback)128 void FtraceDataSource::Flush(FlushRequestID flush_request_id,
129                              std::function<void()> callback) {
130   if (!controller_weak_)
131     return;
132 
133   pending_flushes_[flush_request_id] = std::move(callback);
134 
135   // FtraceController will call OnFtraceFlushComplete() once the data has been
136   // drained from the ftrace buffer and written into the various writers of
137   // all its active data sources.
138   controller_weak_->Flush(flush_request_id);
139 }
140 
141 // Called by FtraceController after all CPUs have acked the flush or timed out.
OnFtraceFlushComplete(FlushRequestID flush_request_id)142 void FtraceDataSource::OnFtraceFlushComplete(FlushRequestID flush_request_id) {
143   auto it = pending_flushes_.find(flush_request_id);
144   if (it == pending_flushes_.end()) {
145     // This can genuinely happen in case of concurrent ftrace sessions. When a
146     // FtraceDataSource issues a flush, the controller has to drain ftrace data
147     // for everybody (there is only one kernel ftrace buffer for all sessions).
148     // FtraceController doesn't bother to remember which FtraceDataSource did or
149     // did not request a flush. Instead just boradcasts the
150     // OnFtraceFlushComplete() to all of them.
151     return;
152   }
153   auto callback = std::move(it->second);
154   pending_flushes_.erase(it);
155   if (writer_) {
156     WriteStats();
157     writer_->Flush(std::move(callback));
158   }
159 }
160 
WriteStats()161 void FtraceDataSource::WriteStats() {
162   if (!controller_weak_) {
163     return;
164   }
165   {
166     auto before_packet = writer_->NewTracePacket();
167     auto out = before_packet->set_ftrace_stats();
168     out->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
169     stats_before_.Write(out);
170   }
171   {
172     FtraceStats stats_after{};
173     controller_weak_->DumpFtraceStats(this, &stats_after);
174     auto after_packet = writer_->NewTracePacket();
175     auto out = after_packet->set_ftrace_stats();
176     out->set_phase(protos::pbzero::FtraceStats::Phase::END_OF_TRACE);
177     stats_after.Write(out);
178     for (auto error : parse_errors_) {
179       out->add_ftrace_parse_errors(error);
180     }
181   }
182 }
183 
184 }  // namespace perfetto
185