xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.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/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
18 
19 #include <limits>
20 
21 #include "perfetto/ext/base/string_view.h"
22 #include "src/trace_processor/importers/common/args_tracker.h"
23 #include "src/trace_processor/importers/common/event_tracker.h"
24 #include "src/trace_processor/importers/common/process_tracker.h"
25 #include "src/trace_processor/importers/common/sched_event_state.h"
26 #include "src/trace_processor/importers/common/sched_event_tracker.h"
27 #include "src/trace_processor/importers/common/system_info_tracker.h"
28 #include "src/trace_processor/importers/common/thread_state_tracker.h"
29 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
30 #include "src/trace_processor/storage/stats.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/types/task_state.h"
33 #include "src/trace_processor/types/trace_processor_context.h"
34 #include "src/trace_processor/types/variadic.h"
35 
36 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
37 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
38 
39 namespace perfetto {
40 namespace trace_processor {
41 
FtraceSchedEventTracker(TraceProcessorContext * context)42 FtraceSchedEventTracker::FtraceSchedEventTracker(TraceProcessorContext* context)
43     : context_(context) {
44   // pre-parse sched_switch
45   auto* switch_descriptor = GetMessageDescriptorForId(
46       protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber);
47   PERFETTO_CHECK(switch_descriptor->max_field_id == kSchedSwitchMaxFieldId);
48 
49   for (size_t i = 1; i <= kSchedSwitchMaxFieldId; i++) {
50     sched_switch_field_ids_[i] =
51         context->storage->InternString(switch_descriptor->fields[i].name);
52   }
53   sched_switch_id_ = context->storage->InternString(switch_descriptor->name);
54 
55   // pre-parse sched_waking
56   auto* waking_descriptor = GetMessageDescriptorForId(
57       protos::pbzero::FtraceEvent::kSchedWakingFieldNumber);
58   PERFETTO_CHECK(waking_descriptor->max_field_id == kSchedWakingMaxFieldId);
59 
60   for (size_t i = 1; i <= kSchedWakingMaxFieldId; i++) {
61     sched_waking_field_ids_[i] =
62         context->storage->InternString(waking_descriptor->fields[i].name);
63   }
64   sched_waking_id_ = context->storage->InternString(waking_descriptor->name);
65 }
66 
67 FtraceSchedEventTracker::~FtraceSchedEventTracker() = default;
68 
PushSchedSwitch(uint32_t cpu,int64_t ts,uint32_t prev_pid,base::StringView prev_comm,int32_t prev_prio,int64_t prev_state,uint32_t next_pid,base::StringView next_comm,int32_t next_prio)69 void FtraceSchedEventTracker::PushSchedSwitch(uint32_t cpu,
70                                               int64_t ts,
71                                               uint32_t prev_pid,
72                                               base::StringView prev_comm,
73                                               int32_t prev_prio,
74                                               int64_t prev_state,
75                                               uint32_t next_pid,
76                                               base::StringView next_comm,
77                                               int32_t next_prio) {
78   if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(
79           ts, "sched_switch", stats::sched_switch_out_of_order)) {
80     return;
81   }
82 
83   StringId next_comm_id = context_->storage->InternString(next_comm);
84   UniqueTid next_utid = context_->process_tracker->UpdateThreadName(
85       next_pid, next_comm_id, ThreadNamePriority::kFtrace);
86 
87   // First use this data to close the previous slice.
88   bool prev_pid_match_prev_next_pid = false;
89   auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
90   uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
91   StringId prev_state_string_id = TaskStateToStringId(prev_state);
92   if (prev_state_string_id == kNullStringId) {
93     context_->storage->IncrementStats(stats::task_state_invalid);
94   }
95   if (pending_slice_idx < std::numeric_limits<uint32_t>::max()) {
96     prev_pid_match_prev_next_pid = prev_pid == pending_sched->last_pid;
97     if (PERFETTO_LIKELY(prev_pid_match_prev_next_pid)) {
98       context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
99                                                        prev_state_string_id);
100     } else {
101       // If the pids are not consistent, make a note of this.
102       context_->storage->IncrementStats(stats::mismatched_sched_switch_tids);
103     }
104   }
105 
106   // We have to intern prev_comm again because our assumption that
107   // this event's |prev_comm| == previous event's |next_comm| does not hold
108   // if the thread changed its name while scheduled.
109   StringId prev_comm_id = context_->storage->InternString(prev_comm);
110   UniqueTid prev_utid = context_->process_tracker->UpdateThreadName(
111       prev_pid, prev_comm_id, ThreadNamePriority::kFtrace);
112 
113   AddRawSchedSwitchEvent(cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio,
114                          prev_state, next_pid, next_comm_id, next_prio);
115 
116   auto new_slice_idx = context_->sched_event_tracker->AddStartSlice(
117       cpu, ts, next_utid, next_prio);
118 
119   // Finally, update the info for the next sched switch on this CPU.
120   pending_sched->pending_slice_storage_idx = new_slice_idx;
121   pending_sched->last_pid = next_pid;
122   pending_sched->last_utid = next_utid;
123   pending_sched->last_prio = next_prio;
124 
125   // Update the ThreadState table.
126   ThreadStateTracker::GetOrCreate(context_)->PushSchedSwitchEvent(
127       ts, cpu, prev_utid, prev_state_string_id, next_utid);
128 }
129 
PushSchedSwitchCompact(uint32_t cpu,int64_t ts,int64_t prev_state,uint32_t next_pid,int32_t next_prio,StringId next_comm_id,bool parse_only_into_raw)130 void FtraceSchedEventTracker::PushSchedSwitchCompact(uint32_t cpu,
131                                                      int64_t ts,
132                                                      int64_t prev_state,
133                                                      uint32_t next_pid,
134                                                      int32_t next_prio,
135                                                      StringId next_comm_id,
136                                                      bool parse_only_into_raw) {
137   if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(
138           ts, "sched_switch", stats::sched_switch_out_of_order)) {
139     return;
140   }
141 
142   UniqueTid next_utid = context_->process_tracker->UpdateThreadName(
143       next_pid, next_comm_id, ThreadNamePriority::kFtrace);
144 
145   // If we're processing the first compact event for this cpu, don't start a
146   // slice since we're missing the "prev_*" fields. The successive events will
147   // create slices as normal, but the first per-cpu switch is effectively
148   // discarded.
149   auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
150   if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
151     context_->storage->IncrementStats(stats::compact_sched_switch_skipped);
152 
153     pending_sched->last_pid = next_pid;
154     pending_sched->last_utid = next_utid;
155     pending_sched->last_prio = next_prio;
156     // Note: no pending slice, so leave |pending_slice_storage_idx| in its
157     // invalid state.
158     return;
159   }
160 
161   // Close the pending slice if any (we won't have one when processing the first
162   // two compact events for a given cpu).
163   uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
164   StringId prev_state_str_id = TaskStateToStringId(prev_state);
165   if (prev_state_str_id == kNullStringId) {
166     context_->storage->IncrementStats(stats::task_state_invalid);
167   }
168   if (pending_slice_idx != std::numeric_limits<uint32_t>::max()) {
169     context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
170                                                      prev_state_str_id);
171   }
172 
173   // Use the previous event's values to infer this event's "prev_*" fields.
174   // There are edge cases, but this assumption should still produce sensible
175   // results in the absence of data loss.
176   UniqueTid prev_utid = pending_sched->last_utid;
177   uint32_t prev_pid = pending_sched->last_pid;
178   int32_t prev_prio = pending_sched->last_prio;
179 
180   // Do a fresh task name lookup in case it was updated by a task_rename while
181   // scheduled.
182   StringId prev_comm_id =
183       context_->storage->thread_table()[prev_utid].name().value_or(
184           kNullStringId);
185 
186   AddRawSchedSwitchEvent(cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio,
187                          prev_state, next_pid, next_comm_id, next_prio);
188 
189   // Update the info for the next sched switch on this CPU.
190   pending_sched->last_pid = next_pid;
191   pending_sched->last_utid = next_utid;
192   pending_sched->last_prio = next_prio;
193 
194   // Subtle: if only inserting into raw, we're ending with:
195   // * updated |pending_sched->last_*| fields
196   // * still-defaulted |pending_slice_storage_idx|
197   // This is similar to the first compact_sched_switch per cpu.
198   if (PERFETTO_UNLIKELY(parse_only_into_raw))
199     return;
200 
201   // Update per-cpu Sched table.
202   auto new_slice_idx = context_->sched_event_tracker->AddStartSlice(
203       cpu, ts, next_utid, next_prio);
204   pending_sched->pending_slice_storage_idx = new_slice_idx;
205 
206   // Update the per-thread ThreadState table.
207   ThreadStateTracker::GetOrCreate(context_)->PushSchedSwitchEvent(
208       ts, cpu, prev_utid, prev_state_str_id, next_utid);
209 }
210 
211 // Processes a sched_waking that was decoded from a compact representation,
212 // adding to the raw and instants tables.
PushSchedWakingCompact(uint32_t cpu,int64_t ts,uint32_t wakee_pid,uint16_t target_cpu,uint16_t prio,StringId comm_id,uint16_t common_flags,bool parse_only_into_raw)213 void FtraceSchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
214                                                      int64_t ts,
215                                                      uint32_t wakee_pid,
216                                                      uint16_t target_cpu,
217                                                      uint16_t prio,
218                                                      StringId comm_id,
219                                                      uint16_t common_flags,
220                                                      bool parse_only_into_raw) {
221   if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(
222           ts, "sched_waking", stats::sched_waking_out_of_order)) {
223     return;
224   }
225 
226   // We infer the task that emitted the event (i.e. common_pid) from the
227   // scheduling slices. Drop the event if we haven't seen any sched_switch
228   // events for this cpu yet.
229   // Note that if sched_switch wasn't enabled, we will have to skip all
230   // compact waking events.
231   auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
232   if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
233     context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
234     return;
235   }
236   auto curr_utid = pending_sched->last_utid;
237 
238   if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
239     tables::FtraceEventTable::Row row;
240     row.ts = ts;
241     row.name = sched_waking_id_;
242     row.utid = curr_utid;
243     row.common_flags = common_flags;
244     row.ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
245 
246     // Add an entry to the raw table.
247     RawId id = context_->storage->mutable_ftrace_event_table()->Insert(row).id;
248 
249     using SW = protos::pbzero::SchedWakingFtraceEvent;
250     auto inserter = context_->args_tracker->AddArgsTo(id);
251     auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
252       StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
253       inserter.AddArg(key, var);
254     };
255     add_raw_arg(SW::kCommFieldNumber, Variadic::String(comm_id));
256     add_raw_arg(SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
257     add_raw_arg(SW::kPrioFieldNumber, Variadic::Integer(prio));
258     add_raw_arg(SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
259   }
260 
261   if (PERFETTO_UNLIKELY(parse_only_into_raw))
262     return;
263 
264   // Add a waking entry to the ThreadState table.
265   auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
266   ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(
267       ts, wakee_utid, curr_utid, common_flags);
268 }
269 
AddRawSchedSwitchEvent(uint32_t cpu,int64_t ts,UniqueTid prev_utid,uint32_t prev_pid,StringId prev_comm_id,int32_t prev_prio,int64_t prev_state,uint32_t next_pid,StringId next_comm_id,int32_t next_prio)270 void FtraceSchedEventTracker::AddRawSchedSwitchEvent(uint32_t cpu,
271                                                      int64_t ts,
272                                                      UniqueTid prev_utid,
273                                                      uint32_t prev_pid,
274                                                      StringId prev_comm_id,
275                                                      int32_t prev_prio,
276                                                      int64_t prev_state,
277                                                      uint32_t next_pid,
278                                                      StringId next_comm_id,
279                                                      int32_t next_prio) {
280   if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
281     // Push the raw event - this is done as the raw ftrace event codepath does
282     // not insert sched_switch.
283     auto ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
284     RawId id = context_->storage->mutable_ftrace_event_table()
285                    ->Insert({ts, sched_switch_id_, prev_utid, {}, {}, ucpu})
286                    .id;
287 
288     // Note: this ordering is important. The events should be pushed in the same
289     // order as the order of fields in the proto; this is used by the raw table
290     // to index these events using the field ids.
291     using SS = protos::pbzero::SchedSwitchFtraceEvent;
292 
293     auto inserter = context_->args_tracker->AddArgsTo(id);
294     auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
295       StringId key = sched_switch_field_ids_[static_cast<size_t>(field_num)];
296       inserter.AddArg(key, var);
297     };
298     add_raw_arg(SS::kPrevCommFieldNumber, Variadic::String(prev_comm_id));
299     add_raw_arg(SS::kPrevPidFieldNumber, Variadic::Integer(prev_pid));
300     add_raw_arg(SS::kPrevPrioFieldNumber, Variadic::Integer(prev_prio));
301     add_raw_arg(SS::kPrevStateFieldNumber, Variadic::Integer(prev_state));
302     add_raw_arg(SS::kNextCommFieldNumber, Variadic::String(next_comm_id));
303     add_raw_arg(SS::kNextPidFieldNumber, Variadic::Integer(next_pid));
304     add_raw_arg(SS::kNextPrioFieldNumber, Variadic::Integer(next_prio));
305   }
306 }
307 
TaskStateToStringId(int64_t task_state_int)308 StringId FtraceSchedEventTracker::TaskStateToStringId(int64_t task_state_int) {
309   using ftrace_utils::TaskState;
310   std::optional<VersionNumber> kernel_version =
311       SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
312 
313   TaskState task_state = TaskState::FromRawPrevState(
314       static_cast<uint16_t>(task_state_int), kernel_version);
315   return task_state.is_valid()
316              ? context_->storage->InternString(task_state.ToString().data())
317              : kNullStringId;
318 }
319 
320 }  // namespace trace_processor
321 }  // namespace perfetto
322