/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "perfetto/tracing/track_event_state_tracker.h" #include "perfetto/ext/base/hash.h" #include "protos/perfetto/common/interceptor_descriptor.gen.h" #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" #include "protos/perfetto/trace/trace_packet.pbzero.h" #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h" #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h" #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h" #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h" #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h" #include "protos/perfetto/trace/track_event/track_event.pbzero.h" namespace perfetto { TrackEventStateTracker::~TrackEventStateTracker() = default; TrackEventStateTracker::Delegate::~Delegate() = default; // static void TrackEventStateTracker::ProcessTracePacket( Delegate& delegate, SequenceState& sequence_state, const protos::pbzero::TracePacket_Decoder& packet) { UpdateIncrementalState(delegate, sequence_state, packet); if (!packet.has_track_event()) return; perfetto::protos::pbzero::TrackEvent::Decoder track_event( packet.track_event()); // TODO(skyostil): Support incremental timestamps. uint64_t timestamp = packet.timestamp(); Track* track = &sequence_state.track; if (track_event.has_track_uuid()) { auto* session_state = delegate.GetSessionState(); if (!session_state) return; // Tracing must have ended. track = &session_state->tracks[track_event.track_uuid()]; } // We only log the first category of each event. protozero::ConstChars category{}; uint64_t category_iid = 0; if (auto iid_it = track_event.category_iids()) { category_iid = *iid_it; category.data = sequence_state.event_categories[category_iid].data(); category.size = sequence_state.event_categories[category_iid].size(); } else if (auto cat_it = track_event.categories()) { category.data = reinterpret_cast(cat_it->data()); category.size = cat_it->size(); } protozero::ConstChars name{}; uint64_t name_iid = track_event.name_iid(); uint64_t name_hash = 0; uint64_t duration = 0; if (name_iid) { name.data = sequence_state.event_names[name_iid].data(); name.size = sequence_state.event_names[name_iid].size(); } else if (track_event.has_name()) { name.data = track_event.name().data; name.size = track_event.name().size; } if (name.data) { base::Hash hash; hash.Update(name.data, name.size); name_hash = hash.digest(); } size_t depth = track->stack.size(); switch (track_event.type()) { case protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN: { StackFrame frame; frame.timestamp = timestamp; frame.name_hash = name_hash; if (track_event.has_track_uuid()) { frame.name = name.ToStdString(); frame.category = category.ToStdString(); } else { frame.name_iid = name_iid; frame.category_iid = category_iid; } track->stack.push_back(std::move(frame)); break; } case protos::pbzero::TrackEvent::TYPE_SLICE_END: if (!track->stack.empty()) { const auto& prev_frame = track->stack.back(); if (prev_frame.name_iid) { name.data = sequence_state.event_names[prev_frame.name_iid].data(); name.size = sequence_state.event_names[prev_frame.name_iid].size(); } else { name.data = prev_frame.name.data(); name.size = prev_frame.name.size(); } name_hash = prev_frame.name_hash; if (prev_frame.category_iid) { category.data = sequence_state.event_categories[prev_frame.category_iid].data(); category.size = sequence_state.event_categories[prev_frame.category_iid].size(); } else { category.data = prev_frame.category.data(); category.size = prev_frame.category.size(); } duration = timestamp - prev_frame.timestamp; depth--; } break; case protos::pbzero::TrackEvent::TYPE_INSTANT: break; case protos::pbzero::TrackEvent::TYPE_COUNTER: case protos::pbzero::TrackEvent::TYPE_UNSPECIFIED: // TODO(skyostil): Support counters. return; } ParsedTrackEvent parsed_event{track_event}; parsed_event.timestamp_ns = timestamp; parsed_event.duration_ns = duration; parsed_event.stack_depth = depth; parsed_event.category = category; parsed_event.name = name; parsed_event.name_hash = name_hash; delegate.OnTrackEvent(*track, parsed_event); if (track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END && !track->stack.empty()) { track->stack.pop_back(); } } // static void TrackEventStateTracker::UpdateIncrementalState( Delegate& delegate, SequenceState& sequence_state, const protos::pbzero::TracePacket_Decoder& packet) { #if PERFETTO_DCHECK_IS_ON() if (!sequence_state.sequence_id) { sequence_state.sequence_id = packet.trusted_packet_sequence_id(); } else { PERFETTO_DCHECK(sequence_state.sequence_id == packet.trusted_packet_sequence_id()); } #endif if (packet.sequence_flags() & perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) { // Convert any existing event names and categories on the stack to // non-interned strings so we can look up their names even after the // incremental state is gone. for (auto& frame : sequence_state.track.stack) { if (frame.name_iid) { frame.name = sequence_state.event_names[frame.name_iid]; frame.name_iid = 0u; } if (frame.category_iid) { frame.category = sequence_state.event_categories[frame.category_iid]; frame.category_iid = 0u; } } sequence_state.event_names.clear(); sequence_state.event_categories.clear(); sequence_state.debug_annotation_names.clear(); sequence_state.track.uuid = 0u; sequence_state.track.index = 0u; } if (packet.has_interned_data()) { perfetto::protos::pbzero::InternedData::Decoder interned_data( packet.interned_data()); for (auto it = interned_data.event_names(); it; it++) { perfetto::protos::pbzero::EventName::Decoder entry(*it); sequence_state.event_names[entry.iid()] = entry.name().ToStdString(); } for (auto it = interned_data.event_categories(); it; it++) { perfetto::protos::pbzero::EventCategory::Decoder entry(*it); sequence_state.event_categories[entry.iid()] = entry.name().ToStdString(); } for (auto it = interned_data.debug_annotation_names(); it; it++) { perfetto::protos::pbzero::DebugAnnotationName::Decoder entry(*it); sequence_state.debug_annotation_names[entry.iid()] = entry.name().ToStdString(); } } if (packet.has_trace_packet_defaults()) { perfetto::protos::pbzero::TracePacketDefaults::Decoder defaults( packet.trace_packet_defaults()); if (defaults.has_track_event_defaults()) { perfetto::protos::pbzero::TrackEventDefaults::Decoder track_event_defaults(defaults.track_event_defaults()); sequence_state.track.uuid = track_event_defaults.track_uuid(); } } if (packet.has_track_descriptor()) { perfetto::protos::pbzero::TrackDescriptor::Decoder track_descriptor( packet.track_descriptor()); auto* session_state = delegate.GetSessionState(); auto& track = session_state->tracks[track_descriptor.uuid()]; if (!track.index) track.index = static_cast(session_state->tracks.size() + 1); track.uuid = track_descriptor.uuid(); track.name = track_descriptor.name().ToStdString(); track.pid = 0; track.tid = 0; if (track_descriptor.has_process()) { perfetto::protos::pbzero::ProcessDescriptor::Decoder process( track_descriptor.process()); track.pid = process.pid(); if (track.name.empty()) track.name = process.process_name().ToStdString(); } else if (track_descriptor.has_thread()) { perfetto::protos::pbzero::ThreadDescriptor::Decoder thread( track_descriptor.thread()); track.pid = thread.pid(); track.tid = thread.tid(); if (track.name.empty()) track.name = thread.thread_name().ToStdString(); } delegate.OnTrackUpdated(track); // Mirror properties to the default track of the sequence. Note that // this does not catch updates to the default track written through other // sequences. if (track.uuid == sequence_state.track.uuid) { sequence_state.track.index = track.index; sequence_state.track.name = track.name; sequence_state.track.pid = track.pid; sequence_state.track.tid = track.tid; sequence_state.track.user_data = track.user_data; } } } TrackEventStateTracker::ParsedTrackEvent::ParsedTrackEvent( const perfetto::protos::pbzero::TrackEvent::Decoder& track_event_) : track_event(track_event_) {} } // namespace perfetto