• 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/trace/ftrace/ftrace_event.pbzero.h"
27 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
28 
29 namespace perfetto {
30 namespace trace_processor {
31 
32 using protozero::ProtoDecoder;
33 using protozero::proto_utils::MakeTagVarInt;
34 using protozero::proto_utils::ParseVarInt;
35 
36 PERFETTO_ALWAYS_INLINE
TokenizeFtraceBundle(TraceBlobView bundle,PacketSequenceState * state)37 void FtraceTokenizer::TokenizeFtraceBundle(TraceBlobView bundle,
38                                            PacketSequenceState* state) {
39   protos::pbzero::FtraceEventBundle::Decoder decoder(bundle.data(),
40                                                      bundle.length());
41 
42   if (PERFETTO_UNLIKELY(!decoder.has_cpu())) {
43     PERFETTO_ELOG("CPU field not found in FtraceEventBundle");
44     context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
45     return;
46   }
47 
48   uint32_t cpu = decoder.cpu();
49   if (PERFETTO_UNLIKELY(cpu > kMaxCpus)) {
50     PERFETTO_ELOG("CPU larger than kMaxCpus (%u > %zu)", cpu, kMaxCpus);
51     return;
52   }
53 
54   if (decoder.has_compact_sched()) {
55     TokenizeFtraceCompactSched(cpu, decoder.compact_sched().data,
56                                decoder.compact_sched().size);
57   }
58 
59   for (auto it = decoder.event(); it; ++it) {
60     protozero::ConstBytes event = *it;
61     size_t off = bundle.offset_of(event.data);
62     TokenizeFtraceEvent(cpu, bundle.slice(off, event.size), state);
63   }
64   context_->sorter->FinalizeFtraceEventBatch(cpu);
65 }
66 
67 PERFETTO_ALWAYS_INLINE
TokenizeFtraceEvent(uint32_t cpu,TraceBlobView event,PacketSequenceState * state)68 void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu,
69                                           TraceBlobView event,
70                                           PacketSequenceState* state) {
71   constexpr auto kTimestampFieldNumber =
72       protos::pbzero::FtraceEvent::kTimestampFieldNumber;
73   const uint8_t* data = event.data();
74   const size_t length = event.length();
75   ProtoDecoder decoder(data, length);
76   uint64_t raw_timestamp = 0;
77   bool timestamp_found = false;
78 
79   // Speculate on the fact that the timestamp is often the 1st field of the
80   // event.
81   constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
82   if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) {
83     // Fastpath.
84     const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
85     timestamp_found = next != data + 1;
86     decoder.Reset(next);
87   } else {
88     // Slowpath.
89     if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
90       timestamp_found = true;
91       raw_timestamp = ts_field.as_uint64();
92     }
93   }
94 
95   if (PERFETTO_UNLIKELY(!timestamp_found)) {
96     PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
97     context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
98     return;
99   }
100 
101   // We don't need to parse this packet, just push it to be sorted with
102   // the timestamp.
103   int64_t timestamp = static_cast<int64_t>(raw_timestamp);
104   context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event), state);
105 }
106 
107 PERFETTO_ALWAYS_INLINE
TokenizeFtraceCompactSched(uint32_t cpu,const uint8_t * data,size_t size)108 void FtraceTokenizer::TokenizeFtraceCompactSched(uint32_t cpu,
109                                                  const uint8_t* data,
110                                                  size_t size) {
111   protos::pbzero::FtraceEventBundle::CompactSched::Decoder compact_sched(data,
112                                                                          size);
113   // Build the interning table for comm fields.
114   std::vector<StringId> string_table;
115   string_table.reserve(512);
116   for (auto it = compact_sched.intern_table(); it; it++) {
117     StringId value = context_->storage->InternString(*it);
118     string_table.push_back(value);
119   }
120 
121   TokenizeFtraceCompactSchedSwitch(cpu, compact_sched, string_table);
122   TokenizeFtraceCompactSchedWaking(cpu, compact_sched, string_table);
123 }
124 
TokenizeFtraceCompactSchedSwitch(uint32_t cpu,const protos::pbzero::FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)125 void FtraceTokenizer::TokenizeFtraceCompactSchedSwitch(
126     uint32_t cpu,
127     const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
128     const std::vector<StringId>& string_table) {
129   // Accumulator for timestamp deltas.
130   int64_t timestamp_acc = 0;
131 
132   // The events' fields are stored in a structure-of-arrays style, using packed
133   // repeated fields. Walk each repeated field in step to recover individual
134   // events.
135   bool parse_error = false;
136   auto timestamp_it = compact.switch_timestamp(&parse_error);
137   auto pstate_it = compact.switch_prev_state(&parse_error);
138   auto npid_it = compact.switch_next_pid(&parse_error);
139   auto nprio_it = compact.switch_next_prio(&parse_error);
140   auto comm_it = compact.switch_next_comm_index(&parse_error);
141   for (; timestamp_it && pstate_it && npid_it && nprio_it && comm_it;
142        ++timestamp_it, ++pstate_it, ++npid_it, ++nprio_it, ++comm_it) {
143     InlineSchedSwitch event{};
144 
145     // delta-encoded timestamp
146     timestamp_acc += static_cast<int64_t>(*timestamp_it);
147     int64_t event_timestamp = timestamp_acc;
148 
149     // index into the interned string table
150     PERFETTO_DCHECK(*comm_it < string_table.size());
151     event.next_comm = string_table[*comm_it];
152 
153     event.prev_state = *pstate_it;
154     event.next_pid = *npid_it;
155     event.next_prio = *nprio_it;
156 
157     context_->sorter->PushInlineFtraceEvent(cpu, event_timestamp, event);
158   }
159 
160   // Check that all packed buffers were decoded correctly, and fully.
161   bool sizes_match =
162       !timestamp_it && !pstate_it && !npid_it && !nprio_it && !comm_it;
163   if (parse_error || !sizes_match)
164     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
165 }
166 
TokenizeFtraceCompactSchedWaking(uint32_t cpu,const protos::pbzero::FtraceEventBundle::CompactSched::Decoder & compact,const std::vector<StringId> & string_table)167 void FtraceTokenizer::TokenizeFtraceCompactSchedWaking(
168     uint32_t cpu,
169     const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
170     const std::vector<StringId>& string_table) {
171   // Accumulator for timestamp deltas.
172   int64_t timestamp_acc = 0;
173 
174   // The events' fields are stored in a structure-of-arrays style, using packed
175   // repeated fields. Walk each repeated field in step to recover individual
176   // events.
177   bool parse_error = false;
178   auto timestamp_it = compact.waking_timestamp(&parse_error);
179   auto pid_it = compact.waking_pid(&parse_error);
180   auto tcpu_it = compact.waking_target_cpu(&parse_error);
181   auto prio_it = compact.waking_prio(&parse_error);
182   auto comm_it = compact.waking_comm_index(&parse_error);
183 
184   for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
185        ++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
186     InlineSchedWaking event{};
187 
188     // delta-encoded timestamp
189     timestamp_acc += static_cast<int64_t>(*timestamp_it);
190     int64_t event_timestamp = timestamp_acc;
191 
192     // index into the interned string table
193     PERFETTO_DCHECK(*comm_it < string_table.size());
194     event.comm = string_table[*comm_it];
195 
196     event.pid = *pid_it;
197     event.target_cpu = *tcpu_it;
198     event.prio = *prio_it;
199 
200     context_->sorter->PushInlineFtraceEvent(cpu, event_timestamp, event);
201   }
202 
203   // Check that all packed buffers were decoded correctly, and fully.
204   bool sizes_match =
205       !timestamp_it && !pid_it && !tcpu_it && !prio_it && !comm_it;
206   if (parse_error || !sizes_match)
207     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
208 }
209 
210 }  // namespace trace_processor
211 }  // namespace perfetto
212