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/proto/track_event_tokenizer.h"
18
19 #include "perfetto/base/logging.h"
20 #include "src/trace_processor/clock_tracker.h"
21 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
22 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
23 #include "src/trace_processor/process_tracker.h"
24 #include "src/trace_processor/storage/stats.h"
25 #include "src/trace_processor/storage/trace_storage.h"
26 #include "src/trace_processor/trace_blob_view.h"
27 #include "src/trace_processor/trace_sorter.h"
28 #include "src/trace_processor/track_tracker.h"
29
30 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
31 #include "protos/perfetto/trace/trace_packet.pbzero.h"
32 #include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
33 #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.h"
34 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
35 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
36 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
37 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
38 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
39
40 namespace perfetto {
41 namespace trace_processor {
42
TrackEventTokenizer(TraceProcessorContext * context)43 TrackEventTokenizer::TrackEventTokenizer(TraceProcessorContext* context)
44 : context_(context) {}
45
TokenizeTrackDescriptorPacket(PacketSequenceState * state,const protos::pbzero::TracePacket::Decoder & packet,int64_t packet_timestamp)46 ModuleResult TrackEventTokenizer::TokenizeTrackDescriptorPacket(
47 PacketSequenceState* state,
48 const protos::pbzero::TracePacket::Decoder& packet,
49 int64_t packet_timestamp) {
50 auto track_descriptor_field = packet.track_descriptor();
51 protos::pbzero::TrackDescriptor::Decoder track(track_descriptor_field.data,
52 track_descriptor_field.size);
53
54 if (!track.has_uuid()) {
55 PERFETTO_ELOG("TrackDescriptor packet without uuid");
56 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
57 return ModuleResult::Handled();
58 }
59
60 if (track.has_thread()) {
61 protos::pbzero::ThreadDescriptor::Decoder thread(track.thread());
62
63 if (!thread.has_pid() || !thread.has_tid()) {
64 PERFETTO_ELOG(
65 "No pid or tid in ThreadDescriptor for track with uuid %" PRIu64,
66 track.uuid());
67 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
68 return ModuleResult::Handled();
69 }
70
71 if (state->IsIncrementalStateValid()) {
72 TokenizeThreadDescriptor(state, thread);
73 }
74
75 context_->track_tracker->ReserveDescriptorThreadTrack(
76 track.uuid(), track.parent_uuid(), static_cast<uint32_t>(thread.pid()),
77 static_cast<uint32_t>(thread.tid()), packet_timestamp);
78 } else if (track.has_process()) {
79 protos::pbzero::ProcessDescriptor::Decoder process(track.process());
80
81 if (!process.has_pid()) {
82 PERFETTO_ELOG("No pid in ProcessDescriptor for track with uuid %" PRIu64,
83 track.uuid());
84 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
85 return ModuleResult::Handled();
86 }
87
88 context_->track_tracker->ReserveDescriptorProcessTrack(
89 track.uuid(), static_cast<uint32_t>(process.pid()), packet_timestamp);
90 } else if (track.has_counter()) {
91 protos::pbzero::CounterDescriptor::Decoder counter(track.counter());
92
93 StringId category_id = kNullStringId;
94 if (counter.has_categories()) {
95 // TODO(eseckler): Support multi-category events in the table schema.
96 std::string categories;
97 for (auto it = counter.categories(); it; ++it) {
98 if (!categories.empty())
99 categories += ",";
100 categories.append((*it).data, (*it).size);
101 }
102 if (!categories.empty()) {
103 category_id =
104 context_->storage->InternString(base::StringView(categories));
105 }
106 }
107
108 context_->track_tracker->ReserveDescriptorCounterTrack(
109 track.uuid(), track.parent_uuid(), category_id,
110 counter.unit_multiplier(), counter.is_incremental(),
111 packet.trusted_packet_sequence_id());
112 } else {
113 context_->track_tracker->ReserveDescriptorChildTrack(track.uuid(),
114 track.parent_uuid());
115 }
116
117 // Let ProtoTraceTokenizer forward the packet to the parser.
118 return ModuleResult::Ignored();
119 }
120
TokenizeThreadDescriptorPacket(PacketSequenceState * state,const protos::pbzero::TracePacket::Decoder & packet)121 ModuleResult TrackEventTokenizer::TokenizeThreadDescriptorPacket(
122 PacketSequenceState* state,
123 const protos::pbzero::TracePacket::Decoder& packet) {
124 if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
125 PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id");
126 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
127 return ModuleResult::Handled();
128 }
129
130 // TrackEvents will be ignored while incremental state is invalid. As a
131 // consequence, we should also ignore any ThreadDescriptors received in this
132 // state. Otherwise, any delta-encoded timestamps would be calculated
133 // incorrectly once we move out of the packet loss state. Instead, wait until
134 // the first subsequent descriptor after incremental state is cleared.
135 if (!state->IsIncrementalStateValid()) {
136 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
137 return ModuleResult::Handled();
138 }
139
140 protos::pbzero::ThreadDescriptor::Decoder thread(packet.thread_descriptor());
141 TokenizeThreadDescriptor(state, thread);
142
143 // Let ProtoTraceTokenizer forward the packet to the parser.
144 return ModuleResult::Ignored();
145 }
146
TokenizeThreadDescriptor(PacketSequenceState * state,const protos::pbzero::ThreadDescriptor::Decoder & thread)147 void TrackEventTokenizer::TokenizeThreadDescriptor(
148 PacketSequenceState* state,
149 const protos::pbzero::ThreadDescriptor::Decoder& thread) {
150 // TODO(eseckler): Remove support for legacy thread descriptor-based default
151 // tracks and delta timestamps.
152 state->SetThreadDescriptor(thread.pid(), thread.tid(),
153 thread.reference_timestamp_us() * 1000,
154 thread.reference_thread_time_us() * 1000,
155 thread.reference_thread_instruction_count());
156 }
157
TokenizeTrackEventPacket(PacketSequenceState * state,const protos::pbzero::TracePacket::Decoder & packet,TraceBlobView * packet_blob,int64_t packet_timestamp)158 void TrackEventTokenizer::TokenizeTrackEventPacket(
159 PacketSequenceState* state,
160 const protos::pbzero::TracePacket::Decoder& packet,
161 TraceBlobView* packet_blob,
162 int64_t packet_timestamp) {
163 if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
164 PERFETTO_ELOG("TrackEvent packet without trusted_packet_sequence_id");
165 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
166 return;
167 }
168
169 auto field = packet.track_event();
170 protos::pbzero::TrackEvent::Decoder event(field.data, field.size);
171
172 protos::pbzero::TrackEventDefaults::Decoder* defaults =
173 state->current_generation()->GetTrackEventDefaults();
174
175 int64_t timestamp;
176 std::unique_ptr<TrackEventData> data(
177 new TrackEventData(std::move(*packet_blob), state->current_generation()));
178
179 // TODO(eseckler): Remove handling of timestamps relative to ThreadDescriptors
180 // once all producers have switched to clock-domain timestamps (e.g.
181 // TracePacket's timestamp).
182
183 if (event.has_timestamp_delta_us()) {
184 // Delta timestamps require a valid ThreadDescriptor packet since the last
185 // packet loss.
186 if (!state->track_event_timestamps_valid()) {
187 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
188 return;
189 }
190 timestamp = state->IncrementAndGetTrackEventTimeNs(
191 event.timestamp_delta_us() * 1000);
192
193 // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
194 // trace time if we have a clock snapshot.
195 auto trace_ts = context_->clock_tracker->ToTraceTime(
196 protos::pbzero::ClockSnapshot::Clock::MONOTONIC, timestamp);
197 if (trace_ts.has_value())
198 timestamp = trace_ts.value();
199 } else if (int64_t ts_absolute_us = event.timestamp_absolute_us()) {
200 // One-off absolute timestamps don't affect delta computation.
201 timestamp = ts_absolute_us * 1000;
202
203 // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
204 // trace time if we have a clock snapshot.
205 auto trace_ts = context_->clock_tracker->ToTraceTime(
206 protos::pbzero::ClockSnapshot::Clock::MONOTONIC, timestamp);
207 if (trace_ts.has_value())
208 timestamp = trace_ts.value();
209 } else if (packet.has_timestamp()) {
210 timestamp = packet_timestamp;
211 } else {
212 PERFETTO_ELOG("TrackEvent without valid timestamp");
213 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
214 return;
215 }
216
217 if (event.has_thread_time_delta_us()) {
218 // Delta timestamps require a valid ThreadDescriptor packet since the last
219 // packet loss.
220 if (!state->track_event_timestamps_valid()) {
221 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
222 return;
223 }
224 data->thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs(
225 event.thread_time_delta_us() * 1000);
226 } else if (event.has_thread_time_absolute_us()) {
227 // One-off absolute timestamps don't affect delta computation.
228 data->thread_timestamp = event.thread_time_absolute_us() * 1000;
229 }
230
231 if (event.has_thread_instruction_count_delta()) {
232 // Delta timestamps require a valid ThreadDescriptor packet since the last
233 // packet loss.
234 if (!state->track_event_timestamps_valid()) {
235 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
236 return;
237 }
238 data->thread_instruction_count =
239 state->IncrementAndGetTrackEventThreadInstructionCount(
240 event.thread_instruction_count_delta());
241 } else if (event.has_thread_instruction_count_absolute()) {
242 // One-off absolute timestamps don't affect delta computation.
243 data->thread_instruction_count = event.thread_instruction_count_absolute();
244 }
245
246 // TODO(eseckler): Also convert & attach counter values from TYPE_COUNTER
247 // events and extra_counter_* fields.
248 if (event.type() == protos::pbzero::TrackEvent::TYPE_COUNTER) {
249 // Consider track_uuid from the packet and TrackEventDefaults.
250 uint64_t track_uuid;
251 if (event.has_track_uuid()) {
252 track_uuid = event.track_uuid();
253 } else if (defaults->has_track_uuid()) {
254 track_uuid = defaults->track_uuid();
255 } else {
256 PERFETTO_DLOG(
257 "Ignoring TrackEvent with counter_value but without track_uuid");
258 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
259 return;
260 }
261
262 if (!event.has_counter_value()) {
263 PERFETTO_DLOG(
264 "Ignoring TrackEvent with TYPE_COUNTER but without counter_value for "
265 "track_uuid %" PRIu64,
266 track_uuid);
267 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
268 return;
269 }
270
271 base::Optional<int64_t> value =
272 context_->track_tracker->ConvertToAbsoluteCounterValue(
273 track_uuid, packet.trusted_packet_sequence_id(),
274 event.counter_value());
275
276 if (!value) {
277 PERFETTO_DLOG("Ignoring TrackEvent with invalid track_uuid %" PRIu64,
278 track_uuid);
279 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
280 return;
281 }
282
283 data->counter_value = *value;
284 }
285
286 if (event.has_extra_counter_values()) {
287 // Consider extra_counter_track_uuids from the packet and
288 // TrackEventDefaults.
289 protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
290 if (event.has_extra_counter_track_uuids()) {
291 track_uuid_it = event.extra_counter_track_uuids();
292 } else if (defaults->has_extra_counter_track_uuids()) {
293 track_uuid_it = defaults->extra_counter_track_uuids();
294 } else {
295 PERFETTO_DLOG(
296 "Ignoring TrackEvent with extra_counter_values but without "
297 "extra_counter_track_uuids");
298 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
299 return;
300 }
301
302 size_t index = 0;
303 for (auto value_it = event.extra_counter_values(); value_it;
304 ++value_it, ++track_uuid_it, ++index) {
305 if (!track_uuid_it) {
306 PERFETTO_DLOG(
307 "Ignoring TrackEvent with more extra_counter_values than "
308 "extra_counter_track_uuids");
309 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
310 return;
311 }
312 if (index >= TrackEventData::kMaxNumExtraCounters) {
313 PERFETTO_ELOG(
314 "Ignoring TrackEvent with more extra_counter_values than "
315 "TrackEventData::kMaxNumExtraCounters");
316 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
317 return;
318 }
319 base::Optional<int64_t> value =
320 context_->track_tracker->ConvertToAbsoluteCounterValue(
321 *track_uuid_it, packet.trusted_packet_sequence_id(), *value_it);
322 if (!value) {
323 PERFETTO_DLOG("Ignoring TrackEvent with invalid extra counter track");
324 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
325 return;
326 }
327 data->extra_counter_values[index] = *value;
328 }
329 }
330
331 context_->sorter->PushTrackEventPacket(timestamp, std::move(data));
332 }
333
334 } // namespace trace_processor
335 } // namespace perfetto
336