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