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