• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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