xref: /aosp_15_r20/external/perfetto/src/traced/probes/ftrace/compact_sched.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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/compact_sched.h"
18 
19 #include <stdint.h>
20 #include <optional>
21 
22 #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
23 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
24 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
25 #include "src/traced/probes/ftrace/event_info_constants.h"
26 #include "src/traced/probes/ftrace/ftrace_config_utils.h"
27 
28 namespace perfetto {
29 
30 namespace {
31 
32 // Pre-parse the format of sched_switch, checking if our simplifying
33 // assumptions about possible widths/signedness hold, and record the subset
34 // of the format that will be used during parsing.
ValidateSchedSwitchFormat(const Event & event)35 std::optional<CompactSchedSwitchFormat> ValidateSchedSwitchFormat(
36     const Event& event) {
37   using protos::pbzero::SchedSwitchFtraceEvent;
38 
39   CompactSchedSwitchFormat switch_format;
40   switch_format.event_id = event.ftrace_event_id;
41   switch_format.size = event.size;
42 
43   bool prev_state_valid = false;
44   bool next_pid_valid = false;
45   bool next_prio_valid = false;
46   bool next_comm_valid = false;
47   for (const auto& field : event.fields) {
48     switch (field.proto_field_id) {
49       case SchedSwitchFtraceEvent::kPrevStateFieldNumber:
50         switch_format.prev_state_offset = field.ftrace_offset;
51         switch_format.prev_state_type = field.ftrace_type;
52 
53         // kernel type: long
54         prev_state_valid = (field.ftrace_type == kFtraceInt32 ||
55                             field.ftrace_type == kFtraceInt64);
56         break;
57 
58       case SchedSwitchFtraceEvent::kNextPidFieldNumber:
59         switch_format.next_pid_offset = field.ftrace_offset;
60         switch_format.next_pid_type = field.ftrace_type;
61 
62         // kernel type: pid_t
63         next_pid_valid = (field.ftrace_type == kFtracePid32);
64         break;
65 
66       case SchedSwitchFtraceEvent::kNextPrioFieldNumber:
67         switch_format.next_prio_offset = field.ftrace_offset;
68         switch_format.next_prio_type = field.ftrace_type;
69 
70         // kernel type: int
71         next_prio_valid = (field.ftrace_type == kFtraceInt32);
72         break;
73 
74       case SchedSwitchFtraceEvent::kNextCommFieldNumber:
75         switch_format.next_comm_offset = field.ftrace_offset;
76 
77         next_comm_valid =
78             (field.ftrace_type == kFtraceFixedCString &&
79              field.ftrace_size == CommInterner::kExpectedCommLength);
80         break;
81       default:
82         break;
83     }
84   }
85 
86   if (!prev_state_valid || !next_pid_valid || !next_prio_valid ||
87       !next_comm_valid) {
88     return std::nullopt;
89   }
90   return std::make_optional(switch_format);
91 }
92 
93 // Pre-parse the format of sched_waking, checking if our simplifying
94 // assumptions about possible widths/signedness hold, and record the subset
95 // of the format that will be used during parsing.
ValidateSchedWakingFormat(const Event & event,const std::vector<Field> & common_fields)96 std::optional<CompactSchedWakingFormat> ValidateSchedWakingFormat(
97     const Event& event,
98     const std::vector<Field>& common_fields) {
99   using protos::pbzero::FtraceEvent;
100   using protos::pbzero::SchedWakingFtraceEvent;
101 
102   CompactSchedWakingFormat waking_format;
103   waking_format.event_id = event.ftrace_event_id;
104   waking_format.size = event.size;
105 
106   bool pid_valid = false;
107   bool target_cpu_valid = false;
108   bool prio_valid = false;
109   bool comm_valid = false;
110   bool common_flags_valid = false;
111 
112   for (const Field& field : common_fields) {
113     if (field.proto_field_id == FtraceEvent::kCommonFlagsFieldNumber) {
114       waking_format.common_flags_offset = field.ftrace_offset;
115       waking_format.common_flags_type = field.ftrace_type;
116 
117       common_flags_valid = (field.ftrace_type == kFtraceUint8);
118       break;
119     }
120   }
121 
122   for (const Field& field : event.fields) {
123     switch (field.proto_field_id) {
124       case SchedWakingFtraceEvent::kPidFieldNumber:
125         waking_format.pid_offset = field.ftrace_offset;
126         waking_format.pid_type = field.ftrace_type;
127 
128         // kernel type: pid_t
129         pid_valid = (field.ftrace_type == kFtracePid32);
130         break;
131 
132       case SchedWakingFtraceEvent::kTargetCpuFieldNumber:
133         waking_format.target_cpu_offset = field.ftrace_offset;
134         waking_format.target_cpu_type = field.ftrace_type;
135 
136         // kernel type: int
137         target_cpu_valid = (field.ftrace_type == kFtraceInt32);
138         break;
139 
140       case SchedWakingFtraceEvent::kPrioFieldNumber:
141         waking_format.prio_offset = field.ftrace_offset;
142         waking_format.prio_type = field.ftrace_type;
143 
144         // kernel type: int
145         prio_valid = (field.ftrace_type == kFtraceInt32);
146         break;
147 
148       case SchedWakingFtraceEvent::kCommFieldNumber:
149         waking_format.comm_offset = field.ftrace_offset;
150 
151         comm_valid = (field.ftrace_type == kFtraceFixedCString &&
152                       field.ftrace_size == CommInterner::kExpectedCommLength);
153         break;
154       default:
155         break;
156     }
157   }
158 
159   if (!pid_valid || !target_cpu_valid || !prio_valid || !comm_valid ||
160       !common_flags_valid) {
161     return std::nullopt;
162   }
163   return std::make_optional(waking_format);
164 }
165 
166 }  // namespace
167 
168 // TODO(rsavitski): could avoid looping over all events if the caller did the
169 // work to remember the relevant events (translation table construction already
170 // loops over them).
ValidateFormatForCompactSched(const std::vector<Event> & events,const std::vector<Field> & common_fields)171 CompactSchedEventFormat ValidateFormatForCompactSched(
172     const std::vector<Event>& events,
173     const std::vector<Field>& common_fields) {
174   using protos::pbzero::FtraceEvent;
175 
176   std::optional<CompactSchedSwitchFormat> switch_format;
177   std::optional<CompactSchedWakingFormat> waking_format;
178   for (const Event& event : events) {
179     if (event.proto_field_id == FtraceEvent::kSchedSwitchFieldNumber) {
180       switch_format = ValidateSchedSwitchFormat(event);
181     }
182     if (event.proto_field_id == FtraceEvent::kSchedWakingFieldNumber) {
183       waking_format = ValidateSchedWakingFormat(event, common_fields);
184     }
185   }
186 
187   if (switch_format.has_value() && waking_format.has_value()) {
188     return CompactSchedEventFormat{/*format_valid=*/true, switch_format.value(),
189                                    waking_format.value()};
190   } else {
191     PERFETTO_ELOG("Unexpected sched_switch or sched_waking format.");
192     return CompactSchedEventFormat{/*format_valid=*/false,
193                                    CompactSchedSwitchFormat{},
194                                    CompactSchedWakingFormat{}};
195   }
196 }
197 
InvalidCompactSchedEventFormatForTesting()198 CompactSchedEventFormat InvalidCompactSchedEventFormatForTesting() {
199   return CompactSchedEventFormat{/*format_valid=*/false,
200                                  CompactSchedSwitchFormat{},
201                                  CompactSchedWakingFormat{}};
202 }
203 
CreateCompactSchedConfig(const FtraceConfig & request,bool switch_requested,const CompactSchedEventFormat & compact_format)204 CompactSchedConfig CreateCompactSchedConfig(
205     const FtraceConfig& request,
206     bool switch_requested,
207     const CompactSchedEventFormat& compact_format) {
208   // If compile-time assumptions don't hold, we'll fall back onto encoding
209   // events individually.
210   if (!compact_format.format_valid) {
211     return CompactSchedConfig{/*enabled=*/false};
212   }
213   // Enabled unless we're not recording sched_switch, or explicitly opting out.
214   // Note: compact sched_waking depends on sched_switch (for derived
215   // common_pid), so use verbose encoding if the config requests only
216   // sched_waking.
217   const auto& compact = request.compact_sched();
218   if (!switch_requested || (compact.has_enabled() && !compact.enabled())) {
219     return CompactSchedConfig{/*enabled=*/false};
220   }
221   return CompactSchedConfig{/*enabled=*/true};
222 }
223 
EnabledCompactSchedConfigForTesting()224 CompactSchedConfig EnabledCompactSchedConfigForTesting() {
225   return CompactSchedConfig{/*enabled=*/true};
226 }
227 
DisabledCompactSchedConfigForTesting()228 CompactSchedConfig DisabledCompactSchedConfigForTesting() {
229   return CompactSchedConfig{/*enabled=*/false};
230 }
231 
Write(protos::pbzero::FtraceEventBundle::CompactSched * compact_out) const232 void CompactSchedSwitchBuffer::Write(
233     protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
234   compact_out->set_switch_timestamp(timestamp_);
235   compact_out->set_switch_next_pid(next_pid_);
236   compact_out->set_switch_prev_state(prev_state_);
237   compact_out->set_switch_next_prio(next_prio_);
238   compact_out->set_switch_next_comm_index(next_comm_index_);
239 }
240 
Reset()241 void CompactSchedSwitchBuffer::Reset() {
242   last_timestamp_ = 0;
243   timestamp_.Reset();
244   next_pid_.Reset();
245   prev_state_.Reset();
246   next_prio_.Reset();
247   next_comm_index_.Reset();
248 }
249 
Write(protos::pbzero::FtraceEventBundle::CompactSched * compact_out) const250 void CompactSchedWakingBuffer::Write(
251     protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
252   compact_out->set_waking_timestamp(timestamp_);
253   compact_out->set_waking_pid(pid_);
254   compact_out->set_waking_target_cpu(target_cpu_);
255   compact_out->set_waking_prio(prio_);
256   compact_out->set_waking_comm_index(comm_index_);
257   compact_out->set_waking_common_flags(common_flags_);
258 }
259 
Reset()260 void CompactSchedWakingBuffer::Reset() {
261   last_timestamp_ = 0;
262   timestamp_.Reset();
263   pid_.Reset();
264   target_cpu_.Reset();
265   prio_.Reset();
266   comm_index_.Reset();
267   common_flags_.Reset();
268 }
269 
Write(protos::pbzero::FtraceEventBundle::CompactSched * compact_out) const270 void CommInterner::Write(
271     protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
272   for (size_t i = 0; i < interned_comms_size_; i++) {
273     compact_out->add_intern_table(interned_comms_[i].data(),
274                                   interned_comms_[i].size());
275   }
276 }
277 
Reset()278 void CommInterner::Reset() {
279   intern_buf_write_pos_ = 0;
280   interned_comms_size_ = 0;
281 }
282 
WriteAndReset(protos::pbzero::FtraceEventBundle * bundle)283 void CompactSchedBuffer::WriteAndReset(
284     protos::pbzero::FtraceEventBundle* bundle) {
285   if (switch_.size() > 0 || waking_.size() > 0) {
286     auto* compact_out = bundle->set_compact_sched();
287 
288     PERFETTO_DCHECK(interner_.interned_comms_size() > 0);
289     interner_.Write(compact_out);
290 
291     if (switch_.size() > 0)
292       switch_.Write(compact_out);
293 
294     if (waking_.size() > 0)
295       waking_.Write(compact_out);
296   }
297   Reset();
298 }
299 
Reset()300 void CompactSchedBuffer::Reset() {
301   interner_.Reset();
302   switch_.Reset();
303   waking_.Reset();
304 }
305 
306 }  // namespace perfetto
307