• 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/protozero/proto_decoder.h"
21 #include "perfetto/protozero/proto_utils.h"
22 #include "src/trace_processor/storage/stats.h"
23 #include "src/trace_processor/storage/trace_storage.h"
24 #include "src/trace_processor/trace_sorter.h"
25 
26 #include "protos/perfetto/common/builtin_clock.pbzero.h"
27 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
28 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
29 
30 namespace perfetto {
31 namespace trace_processor {
32 
33 using protozero::ProtoDecoder;
34 using protozero::proto_utils::MakeTagVarInt;
35 using protozero::proto_utils::ParseVarInt;
36 
37 using protos::pbzero::BuiltinClock;
38 using protos::pbzero::FtraceClock;
39 using protos::pbzero::FtraceEventBundle;
40 
41 namespace {
42 
43 static constexpr uint32_t kFtraceGlobalClockIdForOldKernels = 64;
44 
ResolveTraceTime(TraceProcessorContext * context,ClockTracker::ClockId clock_id,int64_t ts)45 PERFETTO_ALWAYS_INLINE base::Optional<int64_t> ResolveTraceTime(
46     TraceProcessorContext* context,
47     ClockTracker::ClockId clock_id,
48     int64_t ts) {
49   // On most traces (i.e. P+), the clock should be BOOTTIME.
50   if (PERFETTO_LIKELY(clock_id == BuiltinClock::BUILTIN_CLOCK_BOOTTIME))
51     return ts;
52   return context->clock_tracker->ToTraceTime(clock_id, ts);
53 }
54 
55 }  // namespace
56 
57 PERFETTO_ALWAYS_INLINE
TokenizeFtraceBundle(TraceBlobView bundle,PacketSequenceState * state,uint32_t packet_sequence_id)58 base::Status FtraceTokenizer::TokenizeFtraceBundle(
59     TraceBlobView bundle,
60     PacketSequenceState* state,
61     uint32_t packet_sequence_id) {
62   protos::pbzero::FtraceEventBundle::Decoder decoder(bundle.data(),
63                                                      bundle.length());
64 
65   if (PERFETTO_UNLIKELY(!decoder.has_cpu())) {
66     PERFETTO_ELOG("CPU field not found in FtraceEventBundle");
67     context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
68     return base::OkStatus();
69   }
70 
71   uint32_t cpu = decoder.cpu();
72   ClockTracker::ClockId clock_id;
73   switch (decoder.ftrace_clock()) {
74     case FtraceClock::FTRACE_CLOCK_UNSPECIFIED:
75       clock_id = BuiltinClock::BUILTIN_CLOCK_BOOTTIME;
76       break;
77     case FtraceClock::FTRACE_CLOCK_GLOBAL:
78       clock_id = ClockTracker::SeqScopedClockIdToGlobal(
79           packet_sequence_id, kFtraceGlobalClockIdForOldKernels);
80       break;
81     case FtraceClock::FTRACE_CLOCK_LOCAL:
82       return base::ErrStatus("Unable to parse ftrace packets with local clock");
83     default:
84       return base::ErrStatus(
85           "Unable to parse ftrace packets with unknown clock");
86   }
87 
88   if (decoder.has_ftrace_timestamp()) {
89     PERFETTO_DCHECK(clock_id != BuiltinClock::BUILTIN_CLOCK_BOOTTIME);
90     HandleFtraceClockSnapshot(decoder.ftrace_timestamp(),
91                               decoder.boot_timestamp(), packet_sequence_id);
92   }
93 
94   if (decoder.has_compact_sched()) {
95     TokenizeFtraceCompactSched(cpu, clock_id, decoder.compact_sched());
96   }
97 
98   for (auto it = decoder.event(); it; ++it) {
99     TokenizeFtraceEvent(cpu, clock_id, bundle.slice(it->data(), it->size()),
100                         state);
101   }
102   return base::OkStatus();
103 }
104 
105 PERFETTO_ALWAYS_INLINE
TokenizeFtraceEvent(uint32_t cpu,ClockTracker::ClockId clock_id,TraceBlobView event,PacketSequenceState * state)106 void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu,
107                                           ClockTracker::ClockId clock_id,
108                                           TraceBlobView event,
109                                           PacketSequenceState* state) {
110   constexpr auto kTimestampFieldNumber =
111       protos::pbzero::FtraceEvent::kTimestampFieldNumber;
112   constexpr auto kTimestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
113 
114   const uint8_t* data = event.data();
115   const size_t length = event.length();
116   ProtoDecoder decoder(data, length);
117 
118   // Speculate on the fact that the timestamp is often the 1st field of the
119   // event.
120   uint64_t raw_timestamp = 0;
121   bool timestamp_found = false;
122   if (PERFETTO_LIKELY(length > 10 && data[0] == kTimestampFieldTag)) {
123     // Fastpath.
124     const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
125     timestamp_found = next != data + 1;
126     decoder.Reset(next);
127   } else {
128     // Slowpath.
129     if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
130       timestamp_found = true;
131       raw_timestamp = ts_field.as_uint64();
132     }
133   }
134 
135   if (PERFETTO_UNLIKELY(!timestamp_found)) {
136     PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
137     context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
138     return;
139   }
140 
141   // ClockTracker will increment some error stats if it failed to convert the
142   // timestamp so just return.
143   int64_t int64_timestamp = static_cast<int64_t>(raw_timestamp);
144   base::Optional<int64_t> timestamp =
145       ResolveTraceTime(context_, clock_id, int64_timestamp);
146   if (!timestamp)
147     return;
148   context_->sorter->PushFtraceEvent(cpu, *timestamp, std::move(event), state);
149 }
150 
151 PERFETTO_ALWAYS_INLINE
TokenizeFtraceCompactSched(uint32_t cpu,ClockTracker::ClockId clock_id,protozero::ConstBytes packet)152 void FtraceTokenizer::TokenizeFtraceCompactSched(uint32_t cpu,
153                                                  ClockTracker::ClockId clock_id,
154                                                  protozero::ConstBytes packet) {
155   FtraceEventBundle::CompactSched::Decoder compact_sched(packet);
156 
157   // Build the interning table for comm fields.
158   std::vector<StringId> string_table;
159   string_table.reserve(512);
160   for (auto it = compact_sched.intern_table(); it; it++) {
161     StringId value = context_->storage->InternString(*it);
162     string_table.push_back(value);
163   }
164 
165   TokenizeFtraceCompactSchedSwitch(cpu, clock_id, compact_sched, string_table);
166   TokenizeFtraceCompactSchedWaking(cpu, clock_id, compact_sched, string_table);
167 }
168 
TokenizeFtraceCompactSchedSwitch(uint32_t cpu,ClockTracker::ClockId clock_id,const FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)169 void FtraceTokenizer::TokenizeFtraceCompactSchedSwitch(
170     uint32_t cpu,
171     ClockTracker::ClockId clock_id,
172     const FtraceEventBundle::CompactSched::Decoder& compact,
173     const std::vector<StringId>& string_table) {
174   // Accumulator for timestamp deltas.
175   int64_t timestamp_acc = 0;
176 
177   // The events' fields are stored in a structure-of-arrays style, using packed
178   // repeated fields. Walk each repeated field in step to recover individual
179   // events.
180   bool parse_error = false;
181   auto timestamp_it = compact.switch_timestamp(&parse_error);
182   auto pstate_it = compact.switch_prev_state(&parse_error);
183   auto npid_it = compact.switch_next_pid(&parse_error);
184   auto nprio_it = compact.switch_next_prio(&parse_error);
185   auto comm_it = compact.switch_next_comm_index(&parse_error);
186   for (; timestamp_it && pstate_it && npid_it && nprio_it && comm_it;
187        ++timestamp_it, ++pstate_it, ++npid_it, ++nprio_it, ++comm_it) {
188     InlineSchedSwitch event{};
189 
190     // delta-encoded timestamp
191     timestamp_acc += static_cast<int64_t>(*timestamp_it);
192     int64_t event_timestamp = timestamp_acc;
193 
194     // index into the interned string table
195     PERFETTO_DCHECK(*comm_it < string_table.size());
196     event.next_comm = string_table[*comm_it];
197 
198     event.prev_state = *pstate_it;
199     event.next_pid = *npid_it;
200     event.next_prio = *nprio_it;
201 
202     base::Optional<int64_t> timestamp =
203         ResolveTraceTime(context_, clock_id, event_timestamp);
204     if (!timestamp)
205       return;
206     context_->sorter->PushInlineFtraceEvent(cpu, *timestamp, event);
207   }
208 
209   // Check that all packed buffers were decoded correctly, and fully.
210   bool sizes_match =
211       !timestamp_it && !pstate_it && !npid_it && !nprio_it && !comm_it;
212   if (parse_error || !sizes_match)
213     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
214 }
215 
TokenizeFtraceCompactSchedWaking(uint32_t cpu,ClockTracker::ClockId clock_id,const FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)216 void FtraceTokenizer::TokenizeFtraceCompactSchedWaking(
217     uint32_t cpu,
218     ClockTracker::ClockId clock_id,
219     const FtraceEventBundle::CompactSched::Decoder& compact,
220     const std::vector<StringId>& string_table) {
221   // Accumulator for timestamp deltas.
222   int64_t timestamp_acc = 0;
223 
224   // The events' fields are stored in a structure-of-arrays style, using packed
225   // repeated fields. Walk each repeated field in step to recover individual
226   // events.
227   bool parse_error = false;
228   auto timestamp_it = compact.waking_timestamp(&parse_error);
229   auto pid_it = compact.waking_pid(&parse_error);
230   auto tcpu_it = compact.waking_target_cpu(&parse_error);
231   auto prio_it = compact.waking_prio(&parse_error);
232   auto comm_it = compact.waking_comm_index(&parse_error);
233 
234   for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
235        ++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
236     InlineSchedWaking event{};
237 
238     // delta-encoded timestamp
239     timestamp_acc += static_cast<int64_t>(*timestamp_it);
240     int64_t event_timestamp = timestamp_acc;
241 
242     // index into the interned string table
243     PERFETTO_DCHECK(*comm_it < string_table.size());
244     event.comm = string_table[*comm_it];
245 
246     event.pid = *pid_it;
247     event.target_cpu = *tcpu_it;
248     event.prio = *prio_it;
249 
250     base::Optional<int64_t> timestamp =
251         ResolveTraceTime(context_, clock_id, event_timestamp);
252     if (!timestamp)
253       return;
254     context_->sorter->PushInlineFtraceEvent(cpu, *timestamp, event);
255   }
256 
257   // Check that all packed buffers were decoded correctly, and fully.
258   bool sizes_match =
259       !timestamp_it && !pid_it && !tcpu_it && !prio_it && !comm_it;
260   if (parse_error || !sizes_match)
261     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
262 }
263 
HandleFtraceClockSnapshot(int64_t ftrace_ts,int64_t boot_ts,uint32_t packet_sequence_id)264 void FtraceTokenizer::HandleFtraceClockSnapshot(int64_t ftrace_ts,
265                                                 int64_t boot_ts,
266                                                 uint32_t packet_sequence_id) {
267   // If we've already seen a snapshot at this timestamp, don't unnecessarily
268   // add another entry to the clock tracker.
269   if (latest_ftrace_clock_snapshot_ts_ == ftrace_ts)
270     return;
271   latest_ftrace_clock_snapshot_ts_ = ftrace_ts;
272 
273   ClockTracker::ClockId global_id = ClockTracker::SeqScopedClockIdToGlobal(
274       packet_sequence_id, kFtraceGlobalClockIdForOldKernels);
275   context_->clock_tracker->AddSnapshot(
276       {ClockTracker::ClockValue(global_id, ftrace_ts),
277        ClockTracker::ClockValue(BuiltinClock::BUILTIN_CLOCK_BOOTTIME,
278                                 boot_ts)});
279 }
280 
281 }  // namespace trace_processor
282 }  // namespace perfetto
283