blob: c441fb124b25b18761f66bb74297b2180e418281 [file] [log] [blame]
/*
* Copyright (C) 2022 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 <memory>
#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/string_utils.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/flow_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/importers/ftrace/v4l2_tracker.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
#include "protos/perfetto/trace/ftrace/v4l2.pbzero.h"
#include "v4l2_tracker.h"
namespace perfetto {
namespace trace_processor {
namespace {
using protos::pbzero::FtraceEvent;
using protos::pbzero::V4l2DqbufFtraceEvent;
using protos::pbzero::V4l2QbufFtraceEvent;
using protos::pbzero::Vb2V4l2BufDoneFtraceEvent;
using protos::pbzero::Vb2V4l2BufQueueFtraceEvent;
using protos::pbzero::Vb2V4l2DqbufFtraceEvent;
using protos::pbzero::Vb2V4l2QbufFtraceEvent;
using protozero::ConstBytes;
} // namespace
V4l2Tracker::V4l2Tracker(TraceProcessorContext* context)
: context_(context),
buf_event_ids_(*context->storage),
buf_type_ids_(*context_->storage),
buf_field_ids_(*context->storage),
tc_type_ids_(*context->storage) {}
V4l2Tracker::~V4l2Tracker() = default;
void V4l2Tracker::ParseV4l2Event(uint64_t fld_id,
int64_t timestamp,
uint32_t pid,
const ConstBytes& bytes) {
switch (fld_id) {
case FtraceEvent::kV4l2QbufFieldNumber: {
V4l2QbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = pb_evt.index();
evt.type = pb_evt.type();
evt.bytesused = pb_evt.bytesused();
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name(
"VIDIOC_QBUF minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
" index=%" PRIu32,
evt.device_minor, evt.sequence, *evt.type, *evt.index);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
std::optional<SliceId> slice_id =
AddSlice(buf_name_id, timestamp, pid, evt);
uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
*evt.type, *evt.index);
QueuedBuffer queued_buffer;
queued_buffer.queue_slice_id = slice_id;
queued_buffers_.Insert(hash, std::move(queued_buffer));
break;
}
case FtraceEvent::kV4l2DqbufFieldNumber: {
V4l2DqbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = pb_evt.index();
evt.type = pb_evt.type();
evt.bytesused = pb_evt.bytesused();
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name(
"VIDIOC_DQBUF minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
" index=%" PRIu32,
evt.device_minor, evt.sequence, *evt.type, *evt.index);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
std::optional<SliceId> slice_id =
AddSlice(buf_name_id, timestamp, pid, evt);
uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
*evt.type, *evt.index);
const QueuedBuffer* queued_buffer = queued_buffers_.Find(hash);
if (queued_buffer) {
if (queued_buffer->queue_slice_id && slice_id) {
context_->flow_tracker->InsertFlow(*queued_buffer->queue_slice_id,
*slice_id);
}
queued_buffers_.Erase(hash);
}
break;
}
case FtraceEvent::kVb2V4l2BufQueueFieldNumber: {
Vb2V4l2BufQueueFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = std::nullopt;
evt.type = std::nullopt;
evt.bytesused = std::nullopt;
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name("vb2_v4l2_buf_queue minor=%" PRIu32
" seq=%" PRIu32 " type=0 index=0",
evt.device_minor, evt.sequence);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
AddSlice(buf_name_id, timestamp, pid, evt);
break;
}
case FtraceEvent::kVb2V4l2BufDoneFieldNumber: {
Vb2V4l2BufDoneFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = std::nullopt;
evt.type = std::nullopt;
evt.bytesused = std::nullopt;
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name("vb2_v4l2_buf_done minor=%" PRIu32
" seq=%" PRIu32 " type=0 index=0",
evt.device_minor, evt.sequence);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
AddSlice(buf_name_id, timestamp, pid, evt);
break;
}
case FtraceEvent::kVb2V4l2QbufFieldNumber: {
Vb2V4l2QbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = std::nullopt;
evt.type = std::nullopt;
evt.bytesused = std::nullopt;
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name("vb2_v4l2_qbuf minor=%" PRIu32
" seq=%" PRIu32 " type=0 index=0",
evt.device_minor, evt.sequence);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
AddSlice(buf_name_id, timestamp, pid, evt);
break;
}
case FtraceEvent::kVb2V4l2DqbufFieldNumber: {
Vb2V4l2DqbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
BufferEvent evt;
evt.device_minor = pb_evt.minor();
evt.index = std::nullopt;
evt.type = std::nullopt;
evt.bytesused = std::nullopt;
evt.flags = pb_evt.flags();
evt.field = pb_evt.field();
evt.timestamp = pb_evt.timestamp();
evt.sequence = pb_evt.sequence();
evt.timecode_flags = pb_evt.timecode_flags();
evt.timecode_frames = pb_evt.timecode_frames();
evt.timecode_hours = pb_evt.timecode_hours();
evt.timecode_minutes = pb_evt.timecode_minutes();
evt.timecode_seconds = pb_evt.timecode_seconds();
evt.timecode_type = pb_evt.timecode_type();
evt.timecode_userbits0 = pb_evt.timecode_userbits0();
evt.timecode_userbits1 = pb_evt.timecode_userbits1();
evt.timecode_userbits2 = pb_evt.timecode_userbits2();
evt.timecode_userbits3 = pb_evt.timecode_userbits3();
base::StackString<64> buf_name("vb2_v4l2_qbuf minor=%" PRIu32
" seq=%" PRIu32 " type=0 index=0",
evt.device_minor, evt.sequence);
StringId buf_name_id =
context_->storage->InternString(buf_name.string_view());
AddSlice(buf_name_id, timestamp, pid, evt);
break;
}
default:
break;
}
}
std::optional<SliceId> V4l2Tracker::AddSlice(StringId buf_name_id,
int64_t timestamp,
uint32_t pid,
const BufferEvent& evt) {
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
std::optional<SliceId> slice_id = context_->slice_tracker->Scoped(
timestamp, track_id, buf_event_ids_.v4l2, buf_name_id, 0,
[this, &evt](ArgsTracker::BoundInserter* inserter) {
this->AddArgs(evt, inserter);
});
return slice_id;
}
void V4l2Tracker::AddArgs(const BufferEvent& evt,
ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(buf_event_ids_.device_minor,
Variadic::Integer(evt.device_minor));
if (evt.index)
inserter->AddArg(buf_event_ids_.index, Variadic::Integer(*evt.index));
if (evt.type)
inserter->AddArg(buf_event_ids_.type,
Variadic::String(buf_type_ids_.Map(*evt.type)));
if (evt.bytesused)
inserter->AddArg(buf_event_ids_.bytesused,
Variadic::Integer(*evt.bytesused));
inserter->AddArg(buf_event_ids_.flags,
Variadic::String(InternBufFlags(evt.flags)));
inserter->AddArg(buf_event_ids_.field,
Variadic::String(buf_field_ids_.Map(evt.field)));
inserter->AddArg(buf_event_ids_.timestamp, Variadic::Integer(evt.timestamp));
inserter->AddArg(buf_event_ids_.timecode_type,
Variadic::String(tc_type_ids_.Map(evt.timecode_type)));
inserter->AddArg(buf_event_ids_.timecode_flags,
Variadic::String(InternTcFlags(evt.timecode_flags)));
inserter->AddArg(buf_event_ids_.timecode_frames,
Variadic::Integer(evt.timecode_frames));
inserter->AddArg(buf_event_ids_.timecode_seconds,
Variadic::Integer(evt.timecode_seconds));
inserter->AddArg(buf_event_ids_.timecode_minutes,
Variadic::Integer(evt.timecode_minutes));
inserter->AddArg(buf_event_ids_.timecode_hours,
Variadic::Integer(evt.timecode_hours));
inserter->AddArg(buf_event_ids_.timecode_userbits0,
Variadic::Integer(evt.timecode_userbits0));
inserter->AddArg(buf_event_ids_.timecode_userbits1,
Variadic::Integer(evt.timecode_userbits1));
inserter->AddArg(buf_event_ids_.timecode_userbits2,
Variadic::Integer(evt.timecode_userbits2));
inserter->AddArg(buf_event_ids_.timecode_userbits3,
Variadic::Integer(evt.timecode_userbits3));
inserter->AddArg(buf_event_ids_.sequence, Variadic::Integer(evt.sequence));
}
V4l2Tracker::BufferEventStringIds::BufferEventStringIds(TraceStorage& storage)
: v4l2(storage.InternString("Video 4 Linux 2")),
v4l2_qbuf(storage.InternString("v4l2_qbuf")),
v4l2_dqbuf(storage.InternString("v4l2_dqbuf")),
device_minor(storage.InternString("minor")),
index(storage.InternString("index")),
type(storage.InternString("type")),
bytesused(storage.InternString("bytesused")),
flags(storage.InternString("flags")),
field(storage.InternString("field")),
timestamp(storage.InternString("timestamp")),
timecode_type(storage.InternString("timecode_type")),
timecode_flags(storage.InternString("timecode_flags")),
timecode_frames(storage.InternString("timecode_frames")),
timecode_seconds(storage.InternString("timecode_seconds")),
timecode_minutes(storage.InternString("timecode_minutes")),
timecode_hours(storage.InternString("timecode_hours")),
timecode_userbits0(storage.InternString("timecode_userbits0")),
timecode_userbits1(storage.InternString("timecode_userbits1")),
timecode_userbits2(storage.InternString("timecode_userbits2")),
timecode_userbits3(storage.InternString("timecode_userbits3")),
sequence(storage.InternString("sequence")) {}
V4l2Tracker::BufferTypeStringIds::BufferTypeStringIds(TraceStorage& storage)
: video_capture(storage.InternString("VIDEO_CAPTURE")),
video_output(storage.InternString("VIDEO_OUTPUT")),
video_overlay(storage.InternString("VIDEO_OVERLAY")),
vbi_capture(storage.InternString("VBI_CAPTURE")),
vbi_output(storage.InternString("VBI_OUTPUT")),
sliced_vbi_capture(storage.InternString("SLICED_VBI_CAPTURE")),
sliced_vbi_output(storage.InternString("SLICED_VBI_OUTPUT")),
video_output_overlay(storage.InternString("VIDEO_OUTPUT_OVERLAY")),
video_capture_mplane(storage.InternString("VIDEO_CAPTURE_MPLANE")),
video_output_mplane(storage.InternString("VIDEO_OUTPUT_MPLANE")),
sdr_capture(storage.InternString("SDR_CAPTURE")),
sdr_output(storage.InternString("SDR_OUTPUT")),
meta_capture(storage.InternString("META_CAPTURE")),
meta_output(storage.InternString("META_OUTPUT")),
priv(storage.InternString("PRIVATE")) {}
StringId V4l2Tracker::BufferTypeStringIds::Map(uint32_t buf_type) {
// Values taken from linux/videodev2.h
switch (buf_type) {
case 1:
return video_capture;
case 2:
return video_output;
case 3:
return video_overlay;
case 4:
return vbi_capture;
case 5:
return vbi_output;
case 6:
return sliced_vbi_capture;
case 7:
return sliced_vbi_output;
case 8:
return video_output_overlay;
case 9:
return video_capture_mplane;
case 10:
return video_output_mplane;
case 11:
return sdr_capture;
case 12:
return sdr_output;
case 13:
return meta_capture;
case 14:
return meta_output;
case 0x80:
return priv;
default:
return kNullStringId;
}
}
V4l2Tracker::BufferFieldStringIds::BufferFieldStringIds(TraceStorage& storage)
: any(storage.InternString("ANY")),
none(storage.InternString("NONE")),
top(storage.InternString("TOP")),
bottom(storage.InternString("BOTTOM")),
interlaced(storage.InternString("INTERLACED")),
seq_tb(storage.InternString("SEQ_TB")),
seq_bt(storage.InternString("SEQ_BT")),
alternate(storage.InternString("ALTERNATE")),
interlaced_tb(storage.InternString("INTERLACED_TB")),
interlaced_bt(storage.InternString("INTERLACED_BT")) {}
StringId V4l2Tracker::BufferFieldStringIds::Map(uint32_t field) {
// Values taken from linux/videodev2.h
switch (field) {
case 0:
return any;
case 1:
return none;
case 2:
return top;
case 3:
return bottom;
case 4:
return interlaced;
case 5:
return seq_tb;
case 6:
return seq_bt;
case 7:
return alternate;
case 8:
return interlaced_tb;
case 9:
return interlaced_bt;
default:
return kNullStringId;
}
}
V4l2Tracker::TimecodeTypeStringIds::TimecodeTypeStringIds(TraceStorage& storage)
: type_24fps(storage.InternString("24FPS")),
type_25fps(storage.InternString("25FPS")),
type_30fps(storage.InternString("30FPS")),
type_50fps(storage.InternString("50FPS")),
type_60fps(storage.InternString("60FPS")) {}
StringId V4l2Tracker::TimecodeTypeStringIds::Map(uint32_t type) {
switch (type) {
case 1:
return type_24fps;
case 2:
return type_25fps;
case 3:
return type_30fps;
case 4:
return type_50fps;
case 5:
return type_60fps;
default:
return kNullStringId;
}
}
StringId V4l2Tracker::InternBufFlags(uint32_t flags) {
std::vector<std::string> present_flags;
if (flags & 0x00000001)
present_flags.push_back("MAPPED");
if (flags & 0x00000002)
present_flags.push_back("QUEUED");
if (flags & 0x00000004)
present_flags.push_back("DONE");
if (flags & 0x00000008)
present_flags.push_back("KEYFRAME");
if (flags & 0x00000010)
present_flags.push_back("PFRAME");
if (flags & 0x00000020)
present_flags.push_back("BFRAME");
if (flags & 0x00000040)
present_flags.push_back("ERROR");
if (flags & 0x00000080)
present_flags.push_back("IN_REQUEST");
if (flags & 0x00000100)
present_flags.push_back("TIMECODE");
if (flags & 0x00000200)
present_flags.push_back("M2M_HOLD_CAPTURE_BUF");
if (flags & 0x00000400)
present_flags.push_back("PREPARED");
if (flags & 0x00000800)
present_flags.push_back("NO_CACHE_INVALIDATE");
if (flags & 0x00001000)
present_flags.push_back("NO_CACHE_CLEAN");
if (flags & 0x0000e000)
present_flags.push_back("TIMESTAMP_MASK");
if (flags == 0x00000000)
present_flags.push_back("TIMESTAMP_UNKNOWN");
if (flags & 0x00002000)
present_flags.push_back("TIMESTAMP_MONOTONIC");
if (flags & 0x00004000)
present_flags.push_back("TIMESTAMP_COPY");
if (flags & 0x00070000)
present_flags.push_back("TSTAMP_SRC_MASK");
if (flags == 0x00000000)
present_flags.push_back("TSTAMP_SRC_EOF");
if (flags & 0x00010000)
present_flags.push_back("TSTAMP_SRC_SOE");
if (flags & 0x00100000)
present_flags.push_back("LAST");
if (flags & 0x00800000)
present_flags.push_back("REQUEST_FD");
return context_->storage->InternString(
base::Join(present_flags, "|").c_str());
}
StringId V4l2Tracker::InternTcFlags(uint32_t flags) {
std::vector<std::string> present_flags;
if (flags == 0x0000)
present_flags.push_back("USERBITS_USERDEFINED");
if (flags & 0x0001)
present_flags.push_back("FLAG_DROPFRAME");
if (flags & 0x0002)
present_flags.push_back("FLAG_COLORFRAME");
if ((flags & 0x000C) == 0x0004)
present_flags.push_back("USERBITS_field(01)");
if ((flags & 0x000C) == 0x0008)
present_flags.push_back("USERBITS_field(10)");
if ((flags & 0x000C) == 0x000C)
present_flags.push_back("USERBITS_field(11)");
if (flags & 0x0008)
present_flags.push_back("USERBITS_8BITCHARS");
return context_->storage->InternString(
base::Join(present_flags, "|").c_str());
}
} // namespace trace_processor
} // namespace perfetto