1 /* 2 * Copyright (C) 2018 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/proto_trace_tokenizer.h" 18 19 #include <string> 20 21 #include "perfetto/base/logging.h" 22 #include "perfetto/base/utils.h" 23 #include "perfetto/protozero/proto_decoder.h" 24 #include "perfetto/protozero/proto_utils.h" 25 #include "src/trace_processor/event_tracker.h" 26 #include "src/trace_processor/process_tracker.h" 27 #include "src/trace_processor/stats.h" 28 #include "src/trace_processor/trace_blob_view.h" 29 #include "src/trace_processor/trace_sorter.h" 30 #include "src/trace_processor/trace_storage.h" 31 32 #include "perfetto/trace/ftrace/ftrace_event.pbzero.h" 33 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h" 34 #include "perfetto/trace/interned_data/interned_data.pbzero.h" 35 #include "perfetto/trace/trace.pbzero.h" 36 #include "perfetto/trace/track_event/task_execution.pbzero.h" 37 #include "perfetto/trace/track_event/thread_descriptor.pbzero.h" 38 #include "perfetto/trace/track_event/track_event.pbzero.h" 39 40 namespace perfetto { 41 namespace trace_processor { 42 43 using protozero::ProtoDecoder; 44 using protozero::proto_utils::MakeTagLengthDelimited; 45 using protozero::proto_utils::MakeTagVarInt; 46 using protozero::proto_utils::ParseVarInt; 47 48 namespace { 49 50 template <typename MessageType> 51 void InternMessage(TraceProcessorContext* context, 52 ProtoIncrementalState::PacketSequenceState* state, 53 TraceBlobView message) { 54 constexpr auto kIidFieldNumber = MessageType::kIidFieldNumber; 55 56 uint32_t iid = 0; 57 auto message_start = message.data(); 58 auto message_size = message.length(); 59 protozero::ProtoDecoder decoder(message_start, message_size); 60 61 auto field = decoder.FindField(kIidFieldNumber); 62 if (PERFETTO_UNLIKELY(!field)) { 63 PERFETTO_ELOG("Interned message without interning_id"); 64 context->storage->IncrementStats(stats::interned_data_tokenizer_errors); 65 return; 66 } 67 iid = field.as_uint32(); 68 69 auto res = state->GetInternedDataMap<MessageType>()->emplace( 70 iid, 71 ProtoIncrementalState::InternedDataView<MessageType>(std::move(message))); 72 // If a message with this ID is already interned, its data should not have 73 // changed (this is forbidden by the InternedData proto). 74 // TODO(eseckler): This DCHECK assumes that the message is encoded the 75 // same way whenever it is re-emitted. 76 PERFETTO_DCHECK(res.second || 77 (res.first->second.message.length() == message_size && 78 memcmp(res.first->second.message.data(), message_start, 79 message_size) == 0)); 80 } 81 82 } // namespace 83 84 // static 85 TraceType ProtoTraceTokenizer::GuessProtoTraceType(const uint8_t* data, 86 size_t size) { 87 // Scan at most the first 128MB for a track event packet. 88 constexpr size_t kMaxScanSize = 128 * 1024 * 1024; 89 protos::pbzero::Trace::Decoder decoder(data, std::min(size, kMaxScanSize)); 90 if (!decoder.has_packet()) 91 return TraceType::kUnknownTraceType; 92 for (auto it = decoder.packet(); it; ++it) { 93 ProtoDecoder packet_decoder(it->data(), it->size()); 94 if (PERFETTO_UNLIKELY(packet_decoder.FindField( 95 protos::pbzero::TracePacket::kTrackEventFieldNumber))) { 96 return TraceType::kProtoWithTrackEventsTraceType; 97 } 98 } 99 return TraceType::kProtoTraceType; 100 } 101 102 ProtoTraceTokenizer::ProtoTraceTokenizer(TraceProcessorContext* ctx) 103 : context_(ctx) {} 104 ProtoTraceTokenizer::~ProtoTraceTokenizer() = default; 105 106 bool ProtoTraceTokenizer::Parse(std::unique_ptr<uint8_t[]> owned_buf, 107 size_t size) { 108 uint8_t* data = &owned_buf[0]; 109 if (!partial_buf_.empty()) { 110 // It takes ~5 bytes for a proto preamble + the varint size. 111 const size_t kHeaderBytes = 5; 112 if (PERFETTO_UNLIKELY(partial_buf_.size() < kHeaderBytes)) { 113 size_t missing_len = std::min(kHeaderBytes - partial_buf_.size(), size); 114 partial_buf_.insert(partial_buf_.end(), &data[0], &data[missing_len]); 115 if (partial_buf_.size() < kHeaderBytes) 116 return true; 117 data += missing_len; 118 size -= missing_len; 119 } 120 121 // At this point we have enough data in |partial_buf_| to read at least the 122 // field header and know the size of the next TracePacket. 123 constexpr uint8_t kTracePacketTag = 124 MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber); 125 const uint8_t* pos = &partial_buf_[0]; 126 uint8_t proto_field_tag = *pos; 127 uint64_t field_size = 0; 128 const uint8_t* next = ParseVarInt(++pos, &*partial_buf_.end(), &field_size); 129 bool parse_failed = next == pos; 130 pos = next; 131 if (proto_field_tag != kTracePacketTag || field_size == 0 || parse_failed) { 132 PERFETTO_ELOG("Failed parsing a TracePacket from the partial buffer"); 133 return false; // Unrecoverable error, stop parsing. 134 } 135 136 // At this point we know how big the TracePacket is. 137 size_t hdr_size = static_cast<size_t>(pos - &partial_buf_[0]); 138 size_t size_incl_header = static_cast<size_t>(field_size + hdr_size); 139 PERFETTO_DCHECK(size_incl_header > partial_buf_.size()); 140 141 // There is a good chance that between the |partial_buf_| and the new |data| 142 // of the current call we have enough bytes to parse a TracePacket. 143 if (partial_buf_.size() + size >= size_incl_header) { 144 // Create a new buffer for the whole TracePacket and copy into that: 145 // 1) The beginning of the TracePacket (including the proto header) from 146 // the partial buffer. 147 // 2) The rest of the TracePacket from the current |data| buffer (note 148 // that we might have consumed already a few bytes form |data| earlier 149 // in this function, hence we need to keep |off| into account). 150 std::unique_ptr<uint8_t[]> buf(new uint8_t[size_incl_header]); 151 memcpy(&buf[0], partial_buf_.data(), partial_buf_.size()); 152 // |size_missing| is the number of bytes for the rest of the TracePacket 153 // in |data|. 154 size_t size_missing = size_incl_header - partial_buf_.size(); 155 memcpy(&buf[partial_buf_.size()], &data[0], size_missing); 156 data += size_missing; 157 size -= size_missing; 158 partial_buf_.clear(); 159 uint8_t* buf_start = &buf[0]; // Note that buf is std::moved below. 160 ParseInternal(std::move(buf), buf_start, size_incl_header); 161 } else { 162 partial_buf_.insert(partial_buf_.end(), data, &data[size]); 163 return true; 164 } 165 } 166 ParseInternal(std::move(owned_buf), data, size); 167 return true; 168 } 169 170 void ProtoTraceTokenizer::ParseInternal(std::unique_ptr<uint8_t[]> owned_buf, 171 uint8_t* data, 172 size_t size) { 173 PERFETTO_DCHECK(data >= &owned_buf[0]); 174 const uint8_t* start = &owned_buf[0]; 175 const size_t data_off = static_cast<size_t>(data - start); 176 TraceBlobView whole_buf(std::move(owned_buf), data_off, size); 177 178 protos::pbzero::Trace::Decoder decoder(data, size); 179 for (auto it = decoder.packet(); it; ++it) { 180 size_t field_offset = whole_buf.offset_of(it->data()); 181 ParsePacket(whole_buf.slice(field_offset, it->size())); 182 } 183 184 const size_t bytes_left = decoder.bytes_left(); 185 if (bytes_left > 0) { 186 PERFETTO_DCHECK(partial_buf_.empty()); 187 partial_buf_.insert(partial_buf_.end(), &data[decoder.read_offset()], 188 &data[decoder.read_offset() + bytes_left]); 189 } 190 } 191 192 void ProtoTraceTokenizer::ParsePacket(TraceBlobView packet) { 193 protos::pbzero::TracePacket::Decoder decoder(packet.data(), packet.length()); 194 PERFETTO_DCHECK(!decoder.bytes_left()); 195 196 auto timestamp = decoder.has_timestamp() 197 ? static_cast<int64_t>(decoder.timestamp()) 198 : latest_timestamp_; 199 latest_timestamp_ = std::max(timestamp, latest_timestamp_); 200 201 if (decoder.incremental_state_cleared()) { 202 HandleIncrementalStateCleared(decoder); 203 } else if (decoder.previous_packet_dropped()) { 204 HandlePreviousPacketDropped(decoder); 205 } 206 207 if (decoder.has_interned_data()) { 208 auto field = decoder.interned_data(); 209 const size_t offset = packet.offset_of(field.data); 210 ParseInternedData(decoder, packet.slice(offset, field.size)); 211 } 212 213 if (decoder.has_ftrace_events()) { 214 auto ftrace_field = decoder.ftrace_events(); 215 const size_t fld_off = packet.offset_of(ftrace_field.data); 216 ParseFtraceBundle(packet.slice(fld_off, ftrace_field.size)); 217 return; 218 } 219 220 if (decoder.has_track_event()) { 221 ParseTrackEventPacket(decoder, std::move(packet)); 222 return; 223 } 224 225 if (decoder.has_thread_descriptor()) { 226 ParseThreadDescriptorPacket(decoder); 227 return; 228 } 229 230 // Use parent data and length because we want to parse this again 231 // later to get the exact type of the packet. 232 context_->sorter->PushTracePacket(timestamp, std::move(packet)); 233 } 234 235 void ProtoTraceTokenizer::HandleIncrementalStateCleared( 236 const protos::pbzero::TracePacket::Decoder& packet_decoder) { 237 if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) { 238 PERFETTO_ELOG( 239 "incremental_state_cleared without trusted_packet_sequence_id"); 240 context_->storage->IncrementStats(stats::interned_data_tokenizer_errors); 241 return; 242 } 243 GetIncrementalStateForPacketSequence( 244 packet_decoder.trusted_packet_sequence_id()) 245 ->OnIncrementalStateCleared(); 246 } 247 248 void ProtoTraceTokenizer::HandlePreviousPacketDropped( 249 const protos::pbzero::TracePacket::Decoder& packet_decoder) { 250 if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) { 251 PERFETTO_ELOG("previous_packet_dropped without trusted_packet_sequence_id"); 252 context_->storage->IncrementStats(stats::interned_data_tokenizer_errors); 253 return; 254 } 255 GetIncrementalStateForPacketSequence( 256 packet_decoder.trusted_packet_sequence_id()) 257 ->OnPacketLoss(); 258 } 259 260 void ProtoTraceTokenizer::ParseInternedData( 261 const protos::pbzero::TracePacket::Decoder& packet_decoder, 262 TraceBlobView interned_data) { 263 if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) { 264 PERFETTO_ELOG("InternedData packet without trusted_packet_sequence_id"); 265 context_->storage->IncrementStats(stats::interned_data_tokenizer_errors); 266 return; 267 } 268 269 auto* state = GetIncrementalStateForPacketSequence( 270 packet_decoder.trusted_packet_sequence_id()); 271 272 protos::pbzero::InternedData::Decoder interned_data_decoder( 273 interned_data.data(), interned_data.length()); 274 275 // Store references to interned data submessages into the sequence's state. 276 for (auto it = interned_data_decoder.event_categories(); it; ++it) { 277 size_t offset = interned_data.offset_of(it->data()); 278 InternMessage<protos::pbzero::EventCategory>( 279 context_, state, interned_data.slice(offset, it->size())); 280 } 281 282 for (auto it = interned_data_decoder.legacy_event_names(); it; ++it) { 283 size_t offset = interned_data.offset_of(it->data()); 284 InternMessage<protos::pbzero::LegacyEventName>( 285 context_, state, interned_data.slice(offset, it->size())); 286 } 287 288 for (auto it = interned_data_decoder.debug_annotation_names(); it; ++it) { 289 size_t offset = interned_data.offset_of(it->data()); 290 InternMessage<protos::pbzero::DebugAnnotationName>( 291 context_, state, interned_data.slice(offset, it->size())); 292 } 293 294 for (auto it = interned_data_decoder.source_locations(); it; ++it) { 295 size_t offset = interned_data.offset_of(it->data()); 296 InternMessage<protos::pbzero::SourceLocation>( 297 context_, state, interned_data.slice(offset, it->size())); 298 } 299 } 300 301 void ProtoTraceTokenizer::ParseThreadDescriptorPacket( 302 const protos::pbzero::TracePacket::Decoder& packet_decoder) { 303 if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) { 304 PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id"); 305 context_->storage->IncrementStats(stats::track_event_tokenizer_errors); 306 return; 307 } 308 309 auto* state = GetIncrementalStateForPacketSequence( 310 packet_decoder.trusted_packet_sequence_id()); 311 312 // TrackEvents will be ignored while incremental state is invalid. As a 313 // consequence, we should also ignore any ThreadDescriptors received in this 314 // state. Otherwise, any delta-encoded timestamps would be calculated 315 // incorrectly once we move out of the packet loss state. Instead, wait until 316 // the first subsequent descriptor after incremental state is cleared. 317 if (!state->IsIncrementalStateValid()) { 318 context_->storage->IncrementStats( 319 stats::track_event_tokenizer_skipped_packets); 320 return; 321 } 322 323 auto thread_descriptor_field = packet_decoder.thread_descriptor(); 324 protos::pbzero::ThreadDescriptor::Decoder thread_descriptor_decoder( 325 thread_descriptor_field.data, thread_descriptor_field.size); 326 327 state->SetThreadDescriptor( 328 thread_descriptor_decoder.pid(), thread_descriptor_decoder.tid(), 329 thread_descriptor_decoder.reference_timestamp_us() * 1000, 330 thread_descriptor_decoder.reference_thread_time_us() * 1000); 331 // TODO(eseckler): Handle other thread_descriptor fields (e.g. thread 332 // name/type). 333 } 334 335 void ProtoTraceTokenizer::ParseTrackEventPacket( 336 const protos::pbzero::TracePacket::Decoder& packet_decoder, 337 TraceBlobView packet) { 338 constexpr auto kTimestampDeltaUsFieldNumber = 339 protos::pbzero::TrackEvent::kTimestampDeltaUsFieldNumber; 340 constexpr auto kTimestampAbsoluteUsFieldNumber = 341 protos::pbzero::TrackEvent::kTimestampAbsoluteUsFieldNumber; 342 constexpr auto kThreadTimeDeltaUsFieldNumber = 343 protos::pbzero::TrackEvent::kThreadTimeDeltaUsFieldNumber; 344 constexpr auto kThreadTimeAbsoluteUsFieldNumber = 345 protos::pbzero::TrackEvent::kThreadTimeAbsoluteUsFieldNumber; 346 347 if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) { 348 PERFETTO_ELOG("TrackEvent packet without trusted_packet_sequence_id"); 349 context_->storage->IncrementStats(stats::track_event_tokenizer_errors); 350 return; 351 } 352 353 auto* state = GetIncrementalStateForPacketSequence( 354 packet_decoder.trusted_packet_sequence_id()); 355 356 // TrackEvents can only be parsed correctly while incremental state for their 357 // sequence is valid and after a ThreadDescriptor has been parsed. 358 if (!state->IsTrackEventStateValid()) { 359 context_->storage->IncrementStats( 360 stats::track_event_tokenizer_skipped_packets); 361 return; 362 } 363 364 auto field = packet_decoder.track_event(); 365 ProtoDecoder event_decoder(field.data, field.size); 366 367 int64_t timestamp; 368 int64_t thread_timestamp = 0; 369 370 if (auto ts_delta_field = 371 event_decoder.FindField(kTimestampDeltaUsFieldNumber)) { 372 timestamp = state->IncrementAndGetTrackEventTimeNs( 373 ts_delta_field.as_int64() * 1000); 374 } else if (auto ts_absolute_field = 375 event_decoder.FindField(kTimestampAbsoluteUsFieldNumber)) { 376 // One-off absolute timestamps don't affect delta computation. 377 timestamp = ts_absolute_field.as_int64() * 1000; 378 } else { 379 PERFETTO_ELOG("TrackEvent without timestamp"); 380 context_->storage->IncrementStats(stats::track_event_tokenizer_errors); 381 return; 382 } 383 384 if (auto tt_delta_field = 385 event_decoder.FindField(kThreadTimeDeltaUsFieldNumber)) { 386 thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs( 387 tt_delta_field.as_int64() * 1000); 388 } else if (auto tt_absolute_field = 389 event_decoder.FindField(kThreadTimeAbsoluteUsFieldNumber)) { 390 // One-off absolute timestamps don't affect delta computation. 391 thread_timestamp = tt_absolute_field.as_int64() * 1000; 392 } 393 394 context_->sorter->PushTrackEventPacket(timestamp, thread_timestamp, state, 395 std::move(packet)); 396 } 397 398 PERFETTO_ALWAYS_INLINE 399 void ProtoTraceTokenizer::ParseFtraceBundle(TraceBlobView bundle) { 400 protos::pbzero::FtraceEventBundle::Decoder decoder(bundle.data(), 401 bundle.length()); 402 403 if (PERFETTO_UNLIKELY(!decoder.has_cpu())) { 404 PERFETTO_ELOG("CPU field not found in FtraceEventBundle"); 405 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors); 406 return; 407 } 408 409 uint32_t cpu = decoder.cpu(); 410 if (PERFETTO_UNLIKELY(cpu > base::kMaxCpus)) { 411 PERFETTO_ELOG("CPU larger than kMaxCpus (%u > %zu)", cpu, base::kMaxCpus); 412 return; 413 } 414 415 for (auto it = decoder.event(); it; ++it) { 416 size_t off = bundle.offset_of(it->data()); 417 ParseFtraceEvent(cpu, bundle.slice(off, it->size())); 418 } 419 context_->sorter->FinalizeFtraceEventBatch(cpu); 420 } 421 422 PERFETTO_ALWAYS_INLINE 423 void ProtoTraceTokenizer::ParseFtraceEvent(uint32_t cpu, TraceBlobView event) { 424 constexpr auto kTimestampFieldNumber = 425 protos::pbzero::FtraceEvent::kTimestampFieldNumber; 426 const uint8_t* data = event.data(); 427 const size_t length = event.length(); 428 ProtoDecoder decoder(data, length); 429 uint64_t raw_timestamp = 0; 430 bool timestamp_found = false; 431 432 // Speculate on the fact that the timestamp is often the 1st field of the 433 // event. 434 constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber); 435 if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) { 436 // Fastpath. 437 const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp); 438 timestamp_found = next != data + 1; 439 decoder.Reset(next); 440 } else { 441 // Slowpath. 442 if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) { 443 timestamp_found = true; 444 raw_timestamp = ts_field.as_uint64(); 445 } 446 } 447 448 if (PERFETTO_UNLIKELY(!timestamp_found)) { 449 PERFETTO_ELOG("Timestamp field not found in FtraceEvent"); 450 context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors); 451 return; 452 } 453 454 int64_t timestamp = static_cast<int64_t>(raw_timestamp); 455 latest_timestamp_ = std::max(timestamp, latest_timestamp_); 456 457 // We don't need to parse this packet, just push it to be sorted with 458 // the timestamp. 459 context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event)); 460 } 461 462 } // namespace trace_processor 463 } // namespace perfetto 464