• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   FtraceController* ftrace = controller_weak_.get();
108   if (!ftrace)
109     return;
110   PERFETTO_CHECK(config_id_);  // Must be initialized at this point.
111   if (!ftrace->StartDataSource(this))
112     return;
113   DumpFtraceStats(&stats_before_);
114   setup_errors_ = FtraceSetupErrors();  // Dump only on START_OF_TRACE.
115 
116   if (config_.preserve_ftrace_buffer()) {
117     auto stats_packet = writer_->NewTracePacket();
118     auto* stats = stats_packet->set_ftrace_stats();
119     stats->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
120     stats->set_preserve_ftrace_buffer(true);
121   }
122 }
123 
DumpFtraceStats(FtraceStats * stats)124 void FtraceDataSource::DumpFtraceStats(FtraceStats* stats) {
125   if (controller_weak_)
126     controller_weak_->DumpFtraceStats(this, stats);
127   stats->setup_errors = std::move(setup_errors_);
128 }
129 
Flush(FlushRequestID flush_request_id,std::function<void ()> callback)130 void FtraceDataSource::Flush(FlushRequestID flush_request_id,
131                              std::function<void()> callback) {
132   if (!controller_weak_)
133     return;
134 
135   pending_flushes_[flush_request_id] = std::move(callback);
136 
137   // FtraceController will call OnFtraceFlushComplete() once the data has been
138   // drained from the ftrace buffer and written into the various writers of
139   // all its active data sources.
140   controller_weak_->Flush(flush_request_id);
141 }
142 
143 // Called by FtraceController after all CPUs have acked the flush or timed out.
OnFtraceFlushComplete(FlushRequestID flush_request_id)144 void FtraceDataSource::OnFtraceFlushComplete(FlushRequestID flush_request_id) {
145   auto it = pending_flushes_.find(flush_request_id);
146   if (it == pending_flushes_.end()) {
147     // This can genuinely happen in case of concurrent ftrace sessions. When a
148     // FtraceDataSource issues a flush, the controller has to drain ftrace data
149     // for everybody (there is only one kernel ftrace buffer for all sessions).
150     // FtraceController doesn't bother to remember which FtraceDataSource did or
151     // did not request a flush. Instead just boradcasts the
152     // OnFtraceFlushComplete() to all of them.
153     return;
154   }
155   auto callback = std::move(it->second);
156   pending_flushes_.erase(it);
157   if (writer_) {
158     WriteStats();
159     writer_->Flush(std::move(callback));
160   }
161 }
162 
WriteStats()163 void FtraceDataSource::WriteStats() {
164   {
165     auto before_packet = writer_->NewTracePacket();
166     auto out = before_packet->set_ftrace_stats();
167     out->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
168     stats_before_.Write(out);
169   }
170   {
171     FtraceStats stats_after{};
172     DumpFtraceStats(&stats_after);
173     auto after_packet = writer_->NewTracePacket();
174     auto out = after_packet->set_ftrace_stats();
175     out->set_phase(protos::pbzero::FtraceStats::Phase::END_OF_TRACE);
176     stats_after.Write(out);
177   }
178 }
179 
180 }  // namespace perfetto
181