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