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_tokenizer.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/base/status.h"
21 #include "perfetto/protozero/proto_decoder.h"
22 #include "perfetto/protozero/proto_utils.h"
23 #include "perfetto/trace_processor/basic_types.h"
24 #include "src/trace_processor/importers/common/machine_tracker.h"
25 #include "src/trace_processor/importers/common/metadata_tracker.h"
26 #include "src/trace_processor/sorter/trace_sorter.h"
27 #include "src/trace_processor/storage/metadata.h"
28 #include "src/trace_processor/storage/stats.h"
29 #include "src/trace_processor/storage/trace_storage.h"
30 #include "src/trace_processor/types/variadic.h"
31 #include "src/trace_processor/util/status_macros.h"
32
33 #include "protos/perfetto/common/builtin_clock.pbzero.h"
34 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
35 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
36 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
37 #include "protos/perfetto/trace/ftrace/thermal_exynos.pbzero.h"
38
39 namespace perfetto {
40 namespace trace_processor {
41
42 using protozero::ProtoDecoder;
43 using protozero::proto_utils::MakeTagVarInt;
44 using protozero::proto_utils::ParseVarInt;
45
46 using protos::pbzero::BuiltinClock;
47 using protos::pbzero::FtraceClock;
48 using protos::pbzero::FtraceEventBundle;
49
50 namespace {
51
52 static constexpr uint32_t kFtraceGlobalClockIdForOldKernels = 64;
53
ResolveTraceTime(TraceProcessorContext * context,ClockTracker::ClockId clock_id,int64_t ts)54 PERFETTO_ALWAYS_INLINE base::StatusOr<int64_t> ResolveTraceTime(
55 TraceProcessorContext* context,
56 ClockTracker::ClockId clock_id,
57 int64_t ts) {
58 // On most traces (i.e. P+), the clock should be BOOTTIME.
59 if (PERFETTO_LIKELY(clock_id == BuiltinClock::BUILTIN_CLOCK_BOOTTIME))
60 return ts;
61 return context->clock_tracker->ToTraceTime(clock_id, ts);
62 }
63
64 // Fast path for parsing the event id of an ftrace event.
65 // Speculate on the fact that, if the timestamp was found, the common pid
66 // will appear immediately after and the event id immediately after that.
TryFastParseFtraceEventId(const uint8_t * start,const uint8_t * end)67 uint64_t TryFastParseFtraceEventId(const uint8_t* start, const uint8_t* end) {
68 constexpr auto kPidFieldNumber = protos::pbzero::FtraceEvent::kPidFieldNumber;
69 constexpr auto kPidFieldTag = MakeTagVarInt(kPidFieldNumber);
70
71 // If the next byte is not the common pid's tag, just skip the field.
72 constexpr uint32_t kMaxPidLength = 5;
73 if (PERFETTO_UNLIKELY(static_cast<uint32_t>(end - start) <= kMaxPidLength ||
74 start[0] != kPidFieldTag)) {
75 return 0;
76 }
77
78 // Skip the common pid.
79 uint64_t common_pid = 0;
80 const uint8_t* common_pid_end = ParseVarInt(start + 1, end, &common_pid);
81 if (PERFETTO_UNLIKELY(common_pid_end == start + 1)) {
82 return 0;
83 }
84
85 // Read the next varint: this should be the event id tag.
86 uint64_t event_tag = 0;
87 const uint8_t* event_id_end = ParseVarInt(common_pid_end, end, &event_tag);
88 if (event_id_end == common_pid_end) {
89 return 0;
90 }
91
92 constexpr uint8_t kFieldTypeNumBits = 3;
93 constexpr uint64_t kFieldTypeMask =
94 (1 << kFieldTypeNumBits) - 1; // 0000 0111;
95
96 // The event wire type should be length delimited.
97 auto wire_type = static_cast<protozero::proto_utils::ProtoWireType>(
98 event_tag & kFieldTypeMask);
99 if (wire_type != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
100 return 0;
101 }
102 return event_tag >> kFieldTypeNumBits;
103 }
104
105 } // namespace
106
107 PERFETTO_ALWAYS_INLINE
TokenizeFtraceBundle(TraceBlobView bundle,RefPtr<PacketSequenceStateGeneration> state,uint32_t packet_sequence_id)108 base::Status FtraceTokenizer::TokenizeFtraceBundle(
109 TraceBlobView bundle,
110 RefPtr<PacketSequenceStateGeneration> state,
111 uint32_t packet_sequence_id) {
112 protos::pbzero::FtraceEventBundle::Decoder decoder(bundle.data(),
113 bundle.length());
114
115 if (PERFETTO_UNLIKELY(!decoder.has_cpu())) {
116 PERFETTO_ELOG("CPU field not found in FtraceEventBundle");
117 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
118 return base::OkStatus();
119 }
120
121 uint32_t cpu = decoder.cpu();
122 static constexpr uint32_t kMaxCpuCount = 1024;
123 if (PERFETTO_UNLIKELY(cpu >= kMaxCpuCount)) {
124 return base::ErrStatus(
125 "CPU %u is greater than maximum allowed of %u. This is likely because "
126 "of trace corruption",
127 cpu, kMaxCpuCount);
128 }
129
130 if (PERFETTO_UNLIKELY(decoder.lost_events())) {
131 // If set, it means that the kernel overwrote an unspecified number of
132 // events since our last read from the per-cpu buffer.
133 context_->storage->SetIndexedStats(stats::ftrace_cpu_has_data_loss,
134 static_cast<int>(cpu), 1);
135 }
136
137 ClockTracker::ClockId clock_id;
138 switch (decoder.ftrace_clock()) {
139 case FtraceClock::FTRACE_CLOCK_UNSPECIFIED:
140 clock_id = BuiltinClock::BUILTIN_CLOCK_BOOTTIME;
141 break;
142 case FtraceClock::FTRACE_CLOCK_GLOBAL:
143 clock_id = ClockTracker::SequenceToGlobalClock(
144 packet_sequence_id, kFtraceGlobalClockIdForOldKernels);
145 break;
146 case FtraceClock::FTRACE_CLOCK_MONO_RAW:
147 clock_id = BuiltinClock::BUILTIN_CLOCK_MONOTONIC_RAW;
148 break;
149 case FtraceClock::FTRACE_CLOCK_LOCAL:
150 return base::ErrStatus("Unable to parse ftrace packets with local clock");
151 default:
152 return base::ErrStatus(
153 "Unable to parse ftrace packets with unknown clock");
154 }
155
156 if (decoder.has_ftrace_timestamp()) {
157 PERFETTO_DCHECK(clock_id != BuiltinClock::BUILTIN_CLOCK_BOOTTIME);
158 HandleFtraceClockSnapshot(decoder.ftrace_timestamp(),
159 decoder.boot_timestamp(), packet_sequence_id);
160 }
161
162 if (decoder.has_compact_sched()) {
163 TokenizeFtraceCompactSched(cpu, clock_id, decoder.compact_sched());
164 }
165
166 for (auto it = decoder.event(); it; ++it) {
167 TokenizeFtraceEvent(cpu, clock_id, bundle.slice(it->data(), it->size()),
168 state);
169 }
170
171 // First bundle on each cpu is special since ftrace is recorded in per-cpu
172 // buffers. In traces written by perfetto v44+ we know the timestamp from
173 // which this cpu's data stream is valid. This is important for parsing ring
174 // buffer traces, as not all per-cpu data streams will be valid from the same
175 // timestamp.
176 if (cpu >= per_cpu_seen_first_bundle_.size()) {
177 per_cpu_seen_first_bundle_.resize(cpu + 1);
178 }
179 if (!per_cpu_seen_first_bundle_[cpu]) {
180 per_cpu_seen_first_bundle_[cpu] = true;
181
182 // if this cpu's timestamp is the new max, update the metadata table entry
183 if (decoder.has_last_read_event_timestamp()) {
184 int64_t timestamp = 0;
185 ASSIGN_OR_RETURN(
186 timestamp,
187 ResolveTraceTime(
188 context_, clock_id,
189 static_cast<int64_t>(decoder.last_read_event_timestamp())));
190
191 std::optional<SqlValue> curr_latest_timestamp =
192 context_->metadata_tracker->GetMetadata(
193 metadata::ftrace_latest_data_start_ns);
194
195 if (!curr_latest_timestamp.has_value() ||
196 timestamp > curr_latest_timestamp->AsLong()) {
197 context_->metadata_tracker->SetMetadata(
198 metadata::ftrace_latest_data_start_ns,
199 Variadic::Integer(timestamp));
200 }
201 }
202 }
203 return base::OkStatus();
204 }
205
206 PERFETTO_ALWAYS_INLINE
TokenizeFtraceEvent(uint32_t cpu,ClockTracker::ClockId clock_id,TraceBlobView event,RefPtr<PacketSequenceStateGeneration> state)207 void FtraceTokenizer::TokenizeFtraceEvent(
208 uint32_t cpu,
209 ClockTracker::ClockId clock_id,
210 TraceBlobView event,
211 RefPtr<PacketSequenceStateGeneration> state) {
212 constexpr auto kTimestampFieldNumber =
213 protos::pbzero::FtraceEvent::kTimestampFieldNumber;
214 constexpr auto kTimestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
215
216 const uint8_t* data = event.data();
217 const size_t length = event.length();
218
219 // Speculate on the following sequence of varints
220 // - timestamp tag
221 // - timestamp (64 bit)
222 // - common pid tag
223 // - common pid (32 bit)
224 // - event tag
225 uint64_t raw_timestamp = 0;
226 bool timestamp_found = false;
227 uint64_t event_id = 0;
228 if (PERFETTO_LIKELY(length > 10 && data[0] == kTimestampFieldTag)) {
229 // Fastpath.
230 const uint8_t* ts_end = ParseVarInt(data + 1, data + 11, &raw_timestamp);
231 timestamp_found = ts_end != data + 1;
232 if (PERFETTO_LIKELY(timestamp_found)) {
233 event_id = TryFastParseFtraceEventId(ts_end, data + length);
234 }
235 }
236
237 // Slowpath for finding the timestamp.
238 if (PERFETTO_UNLIKELY(!timestamp_found)) {
239 ProtoDecoder decoder(data, length);
240 if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
241 timestamp_found = true;
242 raw_timestamp = ts_field.as_uint64();
243 }
244 if (PERFETTO_UNLIKELY(!timestamp_found)) {
245 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
246 return;
247 }
248 }
249
250 // Slowpath for finding the event id.
251 if (PERFETTO_UNLIKELY(event_id == 0)) {
252 ProtoDecoder decoder(data, length);
253 for (auto f = decoder.ReadField(); f.valid(); f = decoder.ReadField()) {
254 // Find the first length-delimited tag as this corresponds to the ftrace
255 // event.
256 if (f.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
257 event_id = f.id();
258 break;
259 }
260 }
261 if (PERFETTO_UNLIKELY(event_id == 0)) {
262 context_->storage->IncrementStats(stats::ftrace_missing_event_id);
263 return;
264 }
265 }
266
267 if (PERFETTO_UNLIKELY(
268 event_id == protos::pbzero::FtraceEvent::kGpuWorkPeriodFieldNumber)) {
269 TokenizeFtraceGpuWorkPeriod(cpu, std::move(event), std::move(state));
270 return;
271 } else if (PERFETTO_UNLIKELY(event_id ==
272 protos::pbzero::FtraceEvent::
273 kThermalExynosAcpmBulkFieldNumber)) {
274 TokenizeFtraceThermalExynosAcpmBulk(cpu, std::move(event),
275 std::move(state));
276 return;
277 }
278
279 int64_t int64_timestamp = static_cast<int64_t>(raw_timestamp);
280 base::StatusOr<int64_t> timestamp =
281 ResolveTraceTime(context_, clock_id, int64_timestamp);
282
283 // ClockTracker will increment some error stats if it failed to convert the
284 // timestamp so just return.
285 if (!timestamp.ok()) {
286 DlogWithLimit(timestamp.status());
287 return;
288 }
289
290 context_->sorter->PushFtraceEvent(cpu, *timestamp, std::move(event),
291 std::move(state), context_->machine_id());
292 }
293
294 PERFETTO_ALWAYS_INLINE
TokenizeFtraceCompactSched(uint32_t cpu,ClockTracker::ClockId clock_id,protozero::ConstBytes packet)295 void FtraceTokenizer::TokenizeFtraceCompactSched(uint32_t cpu,
296 ClockTracker::ClockId clock_id,
297 protozero::ConstBytes packet) {
298 FtraceEventBundle::CompactSched::Decoder compact_sched(packet);
299
300 // Build the interning table for comm fields.
301 std::vector<StringId> string_table;
302 string_table.reserve(512);
303 for (auto it = compact_sched.intern_table(); it; it++) {
304 StringId value = context_->storage->InternString(*it);
305 string_table.push_back(value);
306 }
307
308 TokenizeFtraceCompactSchedSwitch(cpu, clock_id, compact_sched, string_table);
309 TokenizeFtraceCompactSchedWaking(cpu, clock_id, compact_sched, string_table);
310 }
311
TokenizeFtraceCompactSchedSwitch(uint32_t cpu,ClockTracker::ClockId clock_id,const FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)312 void FtraceTokenizer::TokenizeFtraceCompactSchedSwitch(
313 uint32_t cpu,
314 ClockTracker::ClockId clock_id,
315 const FtraceEventBundle::CompactSched::Decoder& compact,
316 const std::vector<StringId>& string_table) {
317 // Accumulator for timestamp deltas.
318 int64_t timestamp_acc = 0;
319
320 // The events' fields are stored in a structure-of-arrays style, using packed
321 // repeated fields. Walk each repeated field in step to recover individual
322 // events.
323 bool parse_error = false;
324 auto timestamp_it = compact.switch_timestamp(&parse_error);
325 auto pstate_it = compact.switch_prev_state(&parse_error);
326 auto npid_it = compact.switch_next_pid(&parse_error);
327 auto nprio_it = compact.switch_next_prio(&parse_error);
328 auto comm_it = compact.switch_next_comm_index(&parse_error);
329 for (; timestamp_it && pstate_it && npid_it && nprio_it && comm_it;
330 ++timestamp_it, ++pstate_it, ++npid_it, ++nprio_it, ++comm_it) {
331 InlineSchedSwitch event{};
332
333 // delta-encoded timestamp
334 timestamp_acc += static_cast<int64_t>(*timestamp_it);
335 int64_t event_timestamp = timestamp_acc;
336
337 // index into the interned string table
338 PERFETTO_DCHECK(*comm_it < string_table.size());
339 event.next_comm = string_table[*comm_it];
340
341 event.prev_state = *pstate_it;
342 event.next_pid = *npid_it;
343 event.next_prio = *nprio_it;
344
345 base::StatusOr<int64_t> timestamp =
346 ResolveTraceTime(context_, clock_id, event_timestamp);
347 if (!timestamp.ok()) {
348 DlogWithLimit(timestamp.status());
349 return;
350 }
351 context_->sorter->PushInlineFtraceEvent(cpu, *timestamp, event,
352 context_->machine_id());
353 }
354
355 // Check that all packed buffers were decoded correctly, and fully.
356 bool sizes_match =
357 !timestamp_it && !pstate_it && !npid_it && !nprio_it && !comm_it;
358 if (parse_error || !sizes_match)
359 context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
360 }
361
TokenizeFtraceCompactSchedWaking(uint32_t cpu,ClockTracker::ClockId clock_id,const FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)362 void FtraceTokenizer::TokenizeFtraceCompactSchedWaking(
363 uint32_t cpu,
364 ClockTracker::ClockId clock_id,
365 const FtraceEventBundle::CompactSched::Decoder& compact,
366 const std::vector<StringId>& string_table) {
367 // Accumulator for timestamp deltas.
368 int64_t timestamp_acc = 0;
369
370 // The events' fields are stored in a structure-of-arrays style, using packed
371 // repeated fields. Walk each repeated field in step to recover individual
372 // events.
373 bool parse_error = false;
374 auto timestamp_it = compact.waking_timestamp(&parse_error);
375 auto pid_it = compact.waking_pid(&parse_error);
376 auto tcpu_it = compact.waking_target_cpu(&parse_error);
377 auto prio_it = compact.waking_prio(&parse_error);
378 auto comm_it = compact.waking_comm_index(&parse_error);
379 auto common_flags_it = compact.waking_common_flags(&parse_error);
380
381 for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
382 ++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
383 InlineSchedWaking event{};
384
385 // delta-encoded timestamp
386 timestamp_acc += static_cast<int64_t>(*timestamp_it);
387 int64_t event_timestamp = timestamp_acc;
388
389 // index into the interned string table
390 PERFETTO_DCHECK(*comm_it < string_table.size());
391 event.comm = string_table[*comm_it];
392
393 event.pid = *pid_it;
394 event.target_cpu = static_cast<uint16_t>(*tcpu_it);
395 event.prio = static_cast<uint16_t>(*prio_it);
396
397 if (common_flags_it) {
398 event.common_flags = static_cast<uint16_t>(*common_flags_it);
399 common_flags_it++;
400 }
401
402 base::StatusOr<int64_t> timestamp =
403 ResolveTraceTime(context_, clock_id, event_timestamp);
404 if (!timestamp.ok()) {
405 DlogWithLimit(timestamp.status());
406 return;
407 }
408 context_->sorter->PushInlineFtraceEvent(cpu, *timestamp, event,
409 context_->machine_id());
410 }
411
412 // Check that all packed buffers were decoded correctly, and fully.
413 bool sizes_match =
414 !timestamp_it && !pid_it && !tcpu_it && !prio_it && !comm_it;
415 if (parse_error || !sizes_match)
416 context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
417 }
418
HandleFtraceClockSnapshot(int64_t ftrace_ts,int64_t boot_ts,uint32_t packet_sequence_id)419 void FtraceTokenizer::HandleFtraceClockSnapshot(int64_t ftrace_ts,
420 int64_t boot_ts,
421 uint32_t packet_sequence_id) {
422 // If we've already seen a snapshot at this timestamp, don't unnecessarily
423 // add another entry to the clock tracker.
424 if (latest_ftrace_clock_snapshot_ts_ == ftrace_ts)
425 return;
426 latest_ftrace_clock_snapshot_ts_ = ftrace_ts;
427
428 ClockTracker::ClockId global_id = ClockTracker::SequenceToGlobalClock(
429 packet_sequence_id, kFtraceGlobalClockIdForOldKernels);
430 context_->clock_tracker->AddSnapshot(
431 {ClockTracker::ClockTimestamp(global_id, ftrace_ts),
432 ClockTracker::ClockTimestamp(BuiltinClock::BUILTIN_CLOCK_BOOTTIME,
433 boot_ts)});
434 }
435
TokenizeFtraceGpuWorkPeriod(uint32_t cpu,TraceBlobView event,RefPtr<PacketSequenceStateGeneration> state)436 void FtraceTokenizer::TokenizeFtraceGpuWorkPeriod(
437 uint32_t cpu,
438 TraceBlobView event,
439 RefPtr<PacketSequenceStateGeneration> state) {
440 // Special handling of valid gpu_work_period tracepoint events which contain
441 // timestamp values for the GPU time period nested inside the event data.
442 const uint8_t* data = event.data();
443 const size_t length = event.length();
444
445 ProtoDecoder decoder(data, length);
446 auto ts_field =
447 decoder.FindField(protos::pbzero::FtraceEvent::kGpuWorkPeriodFieldNumber);
448 if (!ts_field.valid()) {
449 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
450 return;
451 }
452
453 protos::pbzero::GpuWorkPeriodFtraceEvent::Decoder gpu_work_event(
454 ts_field.data(), ts_field.size());
455 if (!gpu_work_event.has_start_time_ns()) {
456 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
457 return;
458 }
459 uint64_t raw_timestamp = gpu_work_event.start_time_ns();
460
461 // Enforce clock type for the event data to be CLOCK_MONOTONIC_RAW
462 // as specified, to calculate the timestamp correctly.
463 int64_t int64_timestamp = static_cast<int64_t>(raw_timestamp);
464 base::StatusOr<int64_t> timestamp = ResolveTraceTime(
465 context_, BuiltinClock::BUILTIN_CLOCK_MONOTONIC_RAW, int64_timestamp);
466
467 // ClockTracker will increment some error stats if it failed to convert the
468 // timestamp so just return.
469 if (!timestamp.ok()) {
470 DlogWithLimit(timestamp.status());
471 return;
472 }
473
474 context_->sorter->PushFtraceEvent(cpu, *timestamp, std::move(event),
475 std::move(state), context_->machine_id());
476 }
477
TokenizeFtraceThermalExynosAcpmBulk(uint32_t cpu,TraceBlobView event,RefPtr<PacketSequenceStateGeneration> state)478 void FtraceTokenizer::TokenizeFtraceThermalExynosAcpmBulk(
479 uint32_t cpu,
480 TraceBlobView event,
481 RefPtr<PacketSequenceStateGeneration> state) {
482 // Special handling of valid thermal_exynos_acpm_bulk tracepoint events which
483 // contains the right timestamp value nested inside the event data.
484 const uint8_t* data = event.data();
485 const size_t length = event.length();
486
487 ProtoDecoder decoder(data, length);
488 auto ts_field = decoder.FindField(
489 protos::pbzero::FtraceEvent::kThermalExynosAcpmBulkFieldNumber);
490 if (!ts_field.valid()) {
491 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
492 return;
493 }
494
495 protos::pbzero::ThermalExynosAcpmBulkFtraceEvent::Decoder
496 thermal_exynos_acpm_bulk_event(ts_field.data(), ts_field.size());
497 if (!thermal_exynos_acpm_bulk_event.has_timestamp()) {
498 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
499 return;
500 }
501 int64_t timestamp =
502 static_cast<int64_t>(thermal_exynos_acpm_bulk_event.timestamp());
503 context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event),
504 std::move(state), context_->machine_id());
505 }
506
507 } // namespace trace_processor
508 } // namespace perfetto
509