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