blob: 11808a45f1398d37aee2b0d75d490c102ea6ccdf [file] [log] [blame]
/*
* Copyright (C) 2019 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 "src/trace_processor/importers/proto/graphics_frame_event_parser.h"
#include <cinttypes>
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/field.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
namespace perfetto {
namespace trace_processor {
constexpr char kQueueLostMessage[] =
"Missing queue event. The slice is now a bit extended than it might "
"actually have been";
GraphicsFrameEventParser::GraphicsFrameEventParser(
TraceProcessorContext* context)
: context_(context),
graphics_event_scope_id_(
context->storage->InternString("graphics_frame_event")),
unknown_event_name_id_(context->storage->InternString("unknown_event")),
no_layer_name_name_id_(context->storage->InternString("no_layer_name")),
layer_name_key_id_(context->storage->InternString("layer_name")),
event_type_name_ids_{
{context->storage->InternString(
"unspecified_event") /* UNSPECIFIED */,
context->storage->InternString("Dequeue") /* DEQUEUE */,
context->storage->InternString("Queue") /* QUEUE */,
context->storage->InternString("Post") /* POST */,
context->storage->InternString(
"AcquireFenceSignaled") /* ACQUIRE_FENCE */,
context->storage->InternString("Latch") /* LATCH */,
context->storage->InternString(
"HWCCompositionQueued") /* HWC_COMPOSITION_QUEUED */,
context->storage->InternString(
"FallbackComposition") /* FALLBACK_COMPOSITION */,
context->storage->InternString(
"PresentFenceSignaled") /* PRESENT_FENCE */,
context->storage->InternString(
"ReleaseFenceSignaled") /* RELEASE_FENCE */,
context->storage->InternString("Modify") /* MODIFY */,
context->storage->InternString("Detach") /* DETACH */,
context->storage->InternString("Attach") /* ATTACH */,
context->storage->InternString("Cancel") /* CANCEL */}},
queue_lost_message_id_(
context->storage->InternString(kQueueLostMessage)) {}
bool GraphicsFrameEventParser::CreateBufferEvent(
int64_t timestamp,
GraphicsFrameEventDecoder& event) {
if (!event.has_buffer_id()) {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing buffer id field.");
return false;
}
// Use buffer id + layer name as key because sometimes the same buffer can be
// used by different layers.
char event_key_buffer[4096];
base::StringWriter event_key_str(event_key_buffer,
base::ArraySize(event_key_buffer));
const uint32_t buffer_id = event.buffer_id();
StringId layer_name_id;
event_key_str.AppendUnsignedInt(buffer_id);
if (event.has_layer_name()) {
layer_name_id = context_->storage->InternString(event.layer_name());
event_key_str.AppendString(base::StringView(event.layer_name()));
} else {
layer_name_id = no_layer_name_name_id_;
}
StringId event_key =
context_->storage->InternString(event_key_str.GetStringView());
StringId event_name_id = unknown_event_name_id_;
if (event.has_type()) {
const auto type = static_cast<size_t>(event.type());
if (type < event_type_name_ids_.size()) {
event_name_id = event_type_name_ids_[type];
graphics_frame_stats_map_[event_key][type] = timestamp;
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with unknown type %zu.", type);
}
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing type field.");
}
char buffer[4096];
base::StringWriter track_name(buffer, base::ArraySize(buffer));
track_name.AppendLiteral("Buffer: ");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendLiteral(" ");
track_name.AppendString(base::StringView(event.layer_name()));
const StringId track_name_id =
context_->storage->InternString(track_name.GetStringView());
const int64_t duration =
event.has_duration_ns() ? static_cast<int64_t>(event.duration_ns()) : 0;
uint32_t frame_number = event.has_frame_number() ? event.frame_number() : 0;
tables::GpuTrackTable::Row track(track_name_id);
track.scope = graphics_event_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
auto* graphics_frame_slice_table =
context_->storage->mutable_graphics_frame_slice_table();
{
tables::GraphicsFrameSliceTable::Row row;
row.ts = timestamp;
row.track_id = track_id;
row.name = event_name_id;
row.dur = duration;
row.frame_number = frame_number;
row.layer_name = layer_name_id;
if (event.type() == GraphicsFrameEvent::PRESENT_FENCE) {
auto acquire_ts =
graphics_frame_stats_map_[event_key]
[GraphicsFrameEvent::ACQUIRE_FENCE];
auto queue_ts =
graphics_frame_stats_map_[event_key][GraphicsFrameEvent::QUEUE];
auto latch_ts =
graphics_frame_stats_map_[event_key][GraphicsFrameEvent::LATCH];
row.queue_to_acquire_time =
std::max(acquire_ts - queue_ts, static_cast<int64_t>(0));
row.acquire_to_latch_time = latch_ts - acquire_ts;
row.latch_to_present_time = timestamp - latch_ts;
}
std::optional<SliceId> opt_slice_id =
context_->slice_tracker->ScopedTyped(graphics_frame_slice_table, row);
if (event.type() == GraphicsFrameEvent::DEQUEUE) {
if (opt_slice_id) {
dequeue_slice_ids_[event_key] = *opt_slice_id;
}
} else if (event.type() == GraphicsFrameEvent::QUEUE) {
auto it = dequeue_slice_ids_.find(event_key);
if (it != dequeue_slice_ids_.end()) {
auto dequeue_slice_id = it->second;
uint32_t row_idx =
*graphics_frame_slice_table->id().IndexOf(dequeue_slice_id);
graphics_frame_slice_table->mutable_frame_number()->Set(row_idx,
frame_number);
}
}
}
return true;
}
void GraphicsFrameEventParser::InvalidatePhaseEvent(int64_t timestamp,
TrackId track_id,
bool reset_name) {
const auto opt_slice_id = context_->slice_tracker->End(timestamp, track_id);
if (opt_slice_id) {
auto* graphics_frame_slice_table =
context_->storage->mutable_graphics_frame_slice_table();
uint32_t row_idx = *graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
if (reset_name) {
// Set the name (frame_number) to be 0 since there is no frame number
// associated, example : dequeue event.
StringId frame_name_id = context_->storage->InternString("0");
graphics_frame_slice_table->mutable_name()->Set(row_idx, frame_name_id);
graphics_frame_slice_table->mutable_frame_number()->Set(row_idx, 0);
}
// Set the duration to -1 so that this slice will be ignored by the
// UI. Setting any other duration results in wrong data which we want
// to avoid at all costs.
graphics_frame_slice_table->mutable_dur()->Set(row_idx, -1);
}
}
// Here we convert the buffer events into Phases(slices)
// APP: Dequeue to Queue
// Wait for GPU: Queue to Acquire
// SurfaceFlinger (SF): Latch to Present
// Display: Present to next Present (of the same layer)
void GraphicsFrameEventParser::CreatePhaseEvent(
int64_t timestamp,
GraphicsFrameEventDecoder& event) {
// Use buffer id + layer name as key because sometimes the same buffer can be
// used by different layers.
char event_key_buffer[4096];
base::StringWriter event_key_str(event_key_buffer,
base::ArraySize(event_key_buffer));
const uint32_t buffer_id = event.buffer_id();
uint32_t frame_number = event.has_frame_number() ? event.frame_number() : 0;
event_key_str.AppendUnsignedInt(buffer_id);
StringId layer_name_id;
if (event.has_layer_name()) {
layer_name_id = context_->storage->InternString(event.layer_name());
event_key_str.AppendString(base::StringView(event.layer_name()));
} else {
layer_name_id = no_layer_name_name_id_;
}
StringId event_key =
context_->storage->InternString(event_key_str.GetStringView());
char track_buffer[4096];
char slice_buffer[4096];
// We'll be using the name StringWriter and name_id for writing track names
// and slice names.
base::StringWriter track_name(track_buffer, base::ArraySize(track_buffer));
base::StringWriter slice_name(slice_buffer, base::ArraySize(slice_buffer));
StringId track_name_id;
TrackId track_id;
bool start_slice = true;
// Close the previous phase before starting the new phase
switch (event.type()) {
case GraphicsFrameEvent::DEQUEUE: {
track_name.reset();
track_name.AppendLiteral("APP_");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendLiteral(" ");
track_name.AppendString(base::StringView(event.layer_name()));
track_name_id =
context_->storage->InternString(track_name.GetStringView());
tables::GpuTrackTable::Row app_track(track_name_id);
app_track.scope = graphics_event_scope_id_;
track_id = context_->track_tracker->InternGpuTrack(app_track);
// Error handling
auto dequeue_time = dequeue_map_.find(event_key);
if (dequeue_time != dequeue_map_.end()) {
InvalidatePhaseEvent(timestamp, dequeue_time->second, true);
dequeue_map_.erase(dequeue_time);
}
auto queue_time = queue_map_.find(event_key);
if (queue_time != queue_map_.end()) {
InvalidatePhaseEvent(timestamp, queue_time->second);
queue_map_.erase(queue_time);
}
dequeue_map_[event_key] = track_id;
last_dequeued_[event_key] = timestamp;
break;
}
case GraphicsFrameEvent::QUEUE: {
auto dequeue_time = dequeue_map_.find(event_key);
if (dequeue_time != dequeue_map_.end()) {
const auto opt_slice_id =
context_->slice_tracker->End(timestamp, dequeue_time->second);
slice_name.reset();
slice_name.AppendUnsignedInt(frame_number);
if (opt_slice_id) {
auto* graphics_frame_slice_table =
context_->storage->mutable_graphics_frame_slice_table();
// Set the name of the slice to be the frame number since dequeue did
// not have a frame number at that time.
uint32_t row_idx =
*graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
StringId frame_name_id =
context_->storage->InternString(slice_name.GetStringView());
graphics_frame_slice_table->mutable_name()->Set(row_idx,
frame_name_id);
graphics_frame_slice_table->mutable_frame_number()->Set(row_idx,
frame_number);
dequeue_map_.erase(dequeue_time);
}
}
// The AcquireFence might be signaled before receiving a QUEUE event
// sometimes. In that case, we shouldn't start a slice.
if (last_acquired_[event_key] > last_dequeued_[event_key] &&
last_acquired_[event_key] < timestamp) {
start_slice = false;
break;
}
track_name.reset();
track_name.AppendLiteral("GPU_");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendLiteral(" ");
track_name.AppendString(base::StringView(event.layer_name()));
track_name_id =
context_->storage->InternString(track_name.GetStringView());
tables::GpuTrackTable::Row gpu_track(track_name_id);
gpu_track.scope = graphics_event_scope_id_;
track_id = context_->track_tracker->InternGpuTrack(gpu_track);
queue_map_[event_key] = track_id;
break;
}
case GraphicsFrameEvent::ACQUIRE_FENCE: {
auto queue_time = queue_map_.find(event_key);
if (queue_time != queue_map_.end()) {
context_->slice_tracker->End(timestamp, queue_time->second);
queue_map_.erase(queue_time);
}
last_acquired_[event_key] = timestamp;
start_slice = false;
break;
}
case GraphicsFrameEvent::LATCH: {
// b/157578286 - Sometimes Queue event goes missing. To prevent having a
// wrong slice info, we try to close any existing APP slice.
auto dequeue_time = dequeue_map_.find(event_key);
if (dequeue_time != dequeue_map_.end()) {
InvalidatePhaseEvent(timestamp, dequeue_time->second, true);
dequeue_map_.erase(dequeue_time);
}
track_name.reset();
track_name.AppendLiteral("SF_");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendLiteral(" ");
track_name.AppendString(base::StringView(event.layer_name()));
track_name_id =
context_->storage->InternString(track_name.GetStringView());
tables::GpuTrackTable::Row sf_track(track_name_id);
sf_track.scope = graphics_event_scope_id_;
track_id = context_->track_tracker->InternGpuTrack(sf_track);
latch_map_[event_key] = track_id;
break;
}
case GraphicsFrameEvent::PRESENT_FENCE: {
auto latch_time = latch_map_.find(event_key);
if (latch_time != latch_map_.end()) {
context_->slice_tracker->End(timestamp, latch_time->second);
latch_map_.erase(latch_time);
}
auto display_time = display_map_.find(layer_name_id);
if (display_time != display_map_.end()) {
context_->slice_tracker->End(timestamp, display_time->second);
display_map_.erase(display_time);
}
base::StringView layerName(event.layer_name());
track_name.reset();
track_name.AppendLiteral("Display_");
track_name.AppendString(layerName.substr(0, 10));
track_name_id =
context_->storage->InternString(track_name.GetStringView());
tables::GpuTrackTable::Row display_track(track_name_id);
display_track.scope = graphics_event_scope_id_;
track_id = context_->track_tracker->InternGpuTrack(display_track);
display_map_[layer_name_id] = track_id;
break;
}
default:
start_slice = false;
}
// Start the new phase if needed.
if (start_slice) {
tables::GraphicsFrameSliceTable::Row slice;
slice.ts = timestamp;
slice.track_id = track_id;
slice.layer_name = layer_name_id;
slice_name.reset();
// If the frame_number is known, set it as the name of the slice.
// If not known (DEQUEUE), set the name as the timestamp.
// Timestamp is chosen here because the stack_id is hashed based on the name
// of the slice. To not have any conflicting stack_id with any of the
// existing slices, we use timestamp as the temporary name.
if (frame_number != 0) {
slice_name.AppendUnsignedInt(frame_number);
} else {
slice_name.AppendInt(timestamp);
}
slice.name = context_->storage->InternString(slice_name.GetStringView());
slice.frame_number = frame_number;
context_->slice_tracker->BeginTyped(
context_->storage->mutable_graphics_frame_slice_table(), slice);
}
}
void GraphicsFrameEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
ConstBytes blob) {
protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
if (!frame_event.has_buffer_event()) {
return;
}
ConstBytes bufferBlob = frame_event.buffer_event();
protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder event(bufferBlob.data,
bufferBlob.size);
if (CreateBufferEvent(timestamp, event)) {
// Create a phase event only if the buffer event finishes successfully
CreatePhaseEvent(timestamp, event);
}
}
} // namespace trace_processor
} // namespace perfetto