blob: 06e01830dfda1e74095d9c3b9b5bb91995e06f7d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <inttypes.h>
#include <lib/trace-engine/fields.h>
#include <string_view>
#include <utility>
#include <fbl/string_printf.h>
#include <trace-reader/reader.h>
namespace trace {
TraceReader::TraceReader(RecordConsumer record_consumer, ErrorHandler error_handler)
: record_consumer_(std::move(record_consumer)), error_handler_(std::move(error_handler)) {
// Provider ids begin at 1. We don't have a provider yet but we want to
// set the current provider. So set it to non-existent provider 0.
RegisterProvider(0u, "");
}
bool TraceReader::ReadRecords(Chunk& chunk) {
while (true) {
while (pending_header_ == 0) {
std::optional next = chunk.ReadUint64();
if (!next.has_value()) {
return true; // need more data
}
pending_header_ = next.value();
}
auto type = RecordFields::Type::Get<RecordType>(pending_header_);
size_t size;
if (type != RecordType::kLargeRecord) {
size = RecordFields::RecordSize::Get<size_t>(pending_header_);
ZX_DEBUG_ASSERT(size <= RecordFields::kMaxRecordSizeWords);
static_assert(RecordFields::kMaxRecordSizeBytes <=
TRACE_ENCODED_INLINE_LARGE_RECORD_MAX_SIZE);
} else {
size = LargeBlobFields::RecordSize::Get<size_t>(pending_header_);
ZX_DEBUG_ASSERT(size <= BytesToWords(TRACE_ENCODED_INLINE_LARGE_RECORD_MAX_SIZE));
}
if (size == 0) {
ReportError("Unexpected record of size 0");
return false; // fatal error
}
// TODO(https://fxbug.dev/42097117): Here we assume that the entire blob payload can
// fit into the read buffer.
std::optional record_opt = chunk.ReadChunk(size - 1);
if (!record_opt.has_value()) {
return true; // need more data to decode record
}
Chunk& record = record_opt.value();
switch (type) {
case RecordType::kMetadata: {
if (!ReadMetadataRecord(record, pending_header_)) {
ReportError("Failed to read metadata record");
}
break;
}
case RecordType::kInitialization: {
if (!ReadInitializationRecord(record, pending_header_)) {
ReportError("Failed to read initialization record");
}
break;
}
case RecordType::kString: {
if (!ReadStringRecord(record, pending_header_)) {
ReportError("Failed to read string record");
}
break;
}
case RecordType::kThread: {
if (!ReadThreadRecord(record, pending_header_)) {
ReportError("Failed to read thread record");
}
break;
}
case RecordType::kEvent: {
if (!ReadEventRecord(record, pending_header_)) {
ReportError("Failed to read event record");
}
break;
}
case RecordType::kBlob: {
void* ptr;
if (!ReadBlobRecord(record, pending_header_, &ptr)) {
ReportError("Failed to read blob record");
}
break;
}
case RecordType::kKernelObject: {
if (!ReadKernelObjectRecord(record, pending_header_)) {
ReportError("Failed to read kernel object record");
}
break;
}
case RecordType::kScheduler: {
if (!ReadSchedulerRecord(record, pending_header_)) {
ReportError("Failed to read scheduler event record");
}
break;
}
case RecordType::kLog: {
if (!ReadLogRecord(record, pending_header_)) {
ReportError("Failed to read log record");
}
break;
}
case RecordType::kLargeRecord: {
if (!ReadLargeRecord(record, pending_header_)) {
ReportError("Failed to read large record");
}
break;
}
default: {
// Ignore unknown record types for forward compatibility.
ReportError(
fbl::StringPrintf("Skipping record of unknown type %d", static_cast<uint32_t>(type)));
break;
}
}
pending_header_ = 0u;
}
}
bool TraceReader::ReadMetadataRecord(Chunk& record, RecordHeader header) {
auto type = MetadataRecordFields::MetadataType::Get<MetadataType>(header);
switch (type) {
case MetadataType::kProviderInfo: {
auto id = ProviderInfoMetadataRecordFields::Id::Get<ProviderId>(header);
auto name_length = ProviderInfoMetadataRecordFields::NameLength::Get<size_t>(header);
std::optional name_view = record.ReadString(name_length);
if (!name_view.has_value()) {
return false;
}
fbl::String name(name_view.value());
RegisterProvider(id, name);
record_consumer_(
Record(Record::Metadata{MetadataContent(MetadataContent::ProviderInfo{id, name})}));
break;
}
case MetadataType::kProviderSection: {
auto id = ProviderSectionMetadataRecordFields::Id::Get<ProviderId>(header);
SetCurrentProvider(id);
record_consumer_(
Record(Record::Metadata{MetadataContent(MetadataContent::ProviderSection{id})}));
break;
}
case MetadataType::kProviderEvent: {
auto id = ProviderEventMetadataRecordFields::Id::Get<ProviderId>(header);
auto event = ProviderEventMetadataRecordFields::Event::Get<ProviderEventType>(header);
switch (event) {
case ProviderEventType::kBufferOverflow:
record_consumer_(
Record(Record::Metadata{MetadataContent(MetadataContent::ProviderEvent{id, event})}));
break;
default:
// Ignore unknown event types for forward compatibility.
ReportError(fbl::StringPrintf("Skipping provider event of unknown type %u",
static_cast<unsigned>(event)));
break;
}
break;
}
case MetadataType::kTraceInfo: {
auto info_type = TraceInfoMetadataRecordFields::TraceInfoType::Get<TraceInfoType>(header);
switch (info_type) {
case TraceInfoType::kMagicNumber: {
auto record_magic = MagicNumberRecordFields::Magic::Get<uint32_t>(header);
record_consumer_(Record(Record::Metadata{MetadataContent(MetadataContent::TraceInfo{
TraceInfoContent(TraceInfoContent::MagicNumberInfo{record_magic})})}));
break;
}
default: {
// Ignore unknown trace info types for forward compatibility.
ReportError(fbl::StringPrintf("Skipping trace info record of unknown type %u",
static_cast<unsigned>(info_type)));
break;
}
}
break;
}
default: {
// Ignore unknown metadata types for forward compatibility.
ReportError(
fbl::StringPrintf("Skipping metadata of unknown type %d", static_cast<uint32_t>(type)));
break;
}
}
return true;
}
bool TraceReader::ReadInitializationRecord(Chunk& record, RecordHeader header) {
std::optional ticks_per_second_opt = record.ReadUint64();
if (!ticks_per_second_opt.has_value()) {
return false;
}
trace_ticks_t ticks_per_second = ticks_per_second_opt.value();
if (ticks_per_second == 0) {
return false;
}
record_consumer_(Record(Record::Initialization{ticks_per_second}));
return true;
}
bool TraceReader::ReadStringRecord(Chunk& record, RecordHeader header) {
auto index = StringRecordFields::StringIndex::Get<trace_string_index_t>(header);
auto length = StringRecordFields::StringLength::Get<size_t>(header);
std::optional string_view = record.ReadString(length);
if (!string_view.has_value()) {
fbl::String errorMessage = fbl::StringPrintf(
"Failed to parse a string of length %lu registered with string index: '%d'", length, index);
ReportError(errorMessage);
return false;
}
fbl::String string(string_view.value());
if (index < TRACE_ENCODED_STRING_REF_MIN_INDEX || index > TRACE_ENCODED_STRING_REF_MAX_INDEX) {
fbl::String errorMessage = fbl::StringPrintf(
"String: '%s' registered with invalid string index: '%d'", string.c_str(), index);
ReportError(errorMessage);
return false;
}
RegisterString(index, string);
record_consumer_(Record(Record::String{index, std::move(string)}));
return true;
}
bool TraceReader::ReadThreadRecord(Chunk& record, RecordHeader header) {
auto index = ThreadRecordFields::ThreadIndex::Get<trace_thread_index_t>(header);
if (index < TRACE_ENCODED_THREAD_REF_MIN_INDEX || index > TRACE_ENCODED_THREAD_REF_MAX_INDEX) {
ReportError("Invalid thread index");
return false;
}
std::optional process_koid = record.ReadUint64();
if (!process_koid.has_value()) {
ReportError("Thread record has invalid process koid");
return false;
}
std::optional thread_koid = record.ReadUint64();
if (!thread_koid.has_value()) {
ReportError("Thread record has invalid thread koid");
return false;
}
ProcessThread process_thread(process_koid.value(), thread_koid.value());
RegisterThread(index, process_thread);
record_consumer_(Record(Record::Thread{index, process_thread}));
return true;
}
bool TraceReader::ReadEventRecord(Chunk& record, RecordHeader header) {
auto type = EventRecordFields::EventType::Get<EventType>(header);
auto argument_count = EventRecordFields::ArgumentCount::Get<size_t>(header);
auto thread_ref = EventRecordFields::ThreadRef::Get<trace_encoded_thread_ref_t>(header);
auto category_ref = EventRecordFields::CategoryStringRef::Get<trace_encoded_string_ref_t>(header);
auto name_ref = EventRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
std::optional timestamp_opt = record.ReadUint64();
if (!timestamp_opt.has_value()) {
return false;
}
const trace_ticks_t timestamp = timestamp_opt.value();
ProcessThread process_thread;
fbl::String category;
fbl::String name;
std::vector<Argument> arguments;
if (!DecodeThreadRef(record, thread_ref, &process_thread) ||
!DecodeStringRef(record, category_ref, &category) ||
!DecodeStringRef(record, name_ref, &name) ||
!ReadArguments(record, argument_count, &arguments)) {
auto errorMessage = fbl::StringPrintf(
"Invalid event: Event {\n"
" Type: %d\n"
" ArgumentCount: %lu\n"
" ThreadRef: %u (%s)\n"
" CategoryRef: %u (%s)\n"
" NameRef: %u (%s)\n}",
static_cast<uint32_t>(type), argument_count, thread_ref, process_thread.ToString().c_str(),
category_ref, category.c_str(), name_ref, name.c_str());
ReportError(errorMessage);
return false;
}
switch (type) {
case EventType::kInstant: {
std::optional scope = record.ReadUint64();
record_consumer_(Record(Record::Event{
timestamp, process_thread, std::move(category), std::move(name), std::move(arguments),
EventData(EventData::Instant{static_cast<EventScope>(
scope.value_or(static_cast<uint64_t>(EventScope::kThread)))})}));
break;
}
case EventType::kCounter: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::Counter{id.value()})}));
break;
}
case EventType::kDurationBegin: {
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::DurationBegin{})}));
break;
}
case EventType::kDurationEnd: {
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::DurationEnd{})}));
break;
}
case EventType::kDurationComplete: {
std::optional end_time = record.ReadUint64();
if (!end_time.has_value()) {
return false;
}
record_consumer_(Record(Record::Event{
timestamp, process_thread, std::move(category), std::move(name), std::move(arguments),
EventData(EventData::DurationComplete{end_time.value()})}));
break;
}
case EventType::kAsyncBegin: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(Record(Record::Event{timestamp, process_thread, std::move(category),
std::move(name), std::move(arguments),
EventData(EventData::AsyncBegin{id.value()})}));
break;
}
case EventType::kAsyncInstant: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(Record(Record::Event{timestamp, process_thread, std::move(category),
std::move(name), std::move(arguments),
EventData(EventData::AsyncInstant{id.value()})}));
break;
}
case EventType::kAsyncEnd: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::AsyncEnd{id.value()})}));
break;
}
case EventType::kFlowBegin: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowBegin{id.value()})}));
break;
}
case EventType::kFlowStep: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowStep{id.value()})}));
break;
}
case EventType::kFlowEnd: {
std::optional id = record.ReadUint64();
if (!id.has_value()) {
return false;
}
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowEnd{id.value()})}));
break;
}
default: {
// Ignore unknown event types for forward compatibility.
ReportError(
fbl::StringPrintf("Skipping event of unknown type %d", static_cast<uint32_t>(type)));
break;
}
}
return true;
}
bool TraceReader::ReadBlobRecord(Chunk& record, RecordHeader header, void** out_blob) {
auto blob_type = BlobRecordFields::BlobType::Get<trace_blob_type_t>(header);
auto name_ref = BlobRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
auto blob_size = BlobRecordFields::BlobSize::Get<size_t>(header);
fbl::String name;
if (!DecodeStringRef(record, name_ref, &name))
return false;
auto blob_words = BytesToWords(blob_size);
std::optional blob = record.ReadInPlace(blob_words);
if (!blob.has_value()) {
return false;
}
record_consumer_(Record(Record::Blob{blob_type, name, blob.value(), blob_size}));
return true;
}
bool TraceReader::ReadKernelObjectRecord(Chunk& record, RecordHeader header) {
auto object_type = KernelObjectRecordFields::ObjectType::Get<zx_obj_type_t>(header);
auto name_ref = KernelObjectRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
auto argument_count = KernelObjectRecordFields::ArgumentCount::Get<size_t>(header);
std::optional koid_opt = record.ReadUint64();
if (!koid_opt.has_value()) {
return false;
}
zx_koid_t koid = koid_opt.value();
fbl::String name;
if (!DecodeStringRef(record, name_ref, &name)) {
return false;
}
std::vector<Argument> arguments;
if (!ReadArguments(record, argument_count, &arguments)) {
return false;
}
record_consumer_(Record(Record::KernelObject{koid, object_type, name, std::move(arguments)}));
return true;
}
bool TraceReader::ReadSchedulerRecord(Chunk& record, RecordHeader header) {
auto event_type = SchedulerRecordFields::EventType::Get<SchedulerEventType>(header);
if (event_type == SchedulerEventType::kLegacyContextSwitch) {
auto cpu_number = LegacyContextSwitchRecordFields::CpuNumber::Get<trace_cpu_number_t>(header);
auto outgoing_thread_state =
LegacyContextSwitchRecordFields::OutgoingThreadState::Get<ThreadState>(header);
auto outgoing_thread_priority =
LegacyContextSwitchRecordFields::OutgoingThreadPriority::Get<trace_thread_priority_t>(
header);
auto incoming_thread_priority =
LegacyContextSwitchRecordFields::IncomingThreadPriority::Get<trace_thread_priority_t>(
header);
auto outgoing_thread_ref =
LegacyContextSwitchRecordFields::OutgoingThreadRef::Get<trace_encoded_thread_ref_t>(header);
auto incoming_thread_ref =
LegacyContextSwitchRecordFields::IncomingThreadRef::Get<trace_encoded_thread_ref_t>(header);
std::optional timestamp_opt = record.ReadUint64();
if (!timestamp_opt.has_value()) {
return false;
}
trace_ticks_t timestamp = timestamp_opt.value();
ProcessThread outgoing_thread;
if (!DecodeThreadRef(record, outgoing_thread_ref, &outgoing_thread)) {
return false;
}
ProcessThread incoming_thread;
if (!DecodeThreadRef(record, incoming_thread_ref, &incoming_thread)) {
return false;
}
record_consumer_(Record(Record::SchedulerEvent{
{timestamp, cpu_number, outgoing_thread_state, outgoing_thread, incoming_thread,
outgoing_thread_priority, incoming_thread_priority}}));
return true;
}
if (event_type == SchedulerEventType::kContextSwitch) {
auto argument_count = ContextSwitchRecordFields::ArgumentCount::Get<size_t>(header);
auto cpu_number = ContextSwitchRecordFields::CpuNumber::Get<trace_cpu_number_t>(header);
auto thread_state = ContextSwitchRecordFields::ThreadState::Get<ThreadState>(header);
std::optional timestamp = record.ReadUint64();
if (!timestamp.has_value()) {
return false;
}
std::optional outgoing_tid = record.ReadUint64();
if (!outgoing_tid.has_value()) {
return false;
}
std::optional incoming_tid = record.ReadUint64();
if (!incoming_tid.has_value()) {
return false;
}
std::vector<Argument> arguments;
if (!ReadArguments(record, argument_count, &arguments)) {
return false;
}
record_consumer_(
Record(Record::SchedulerEvent{{*timestamp, cpu_number, thread_state, *outgoing_tid,
*incoming_tid, std::move(arguments)}}));
return true;
}
if (event_type == SchedulerEventType::kThreadWakeup) {
auto argument_count = ContextSwitchRecordFields::ArgumentCount::Get<size_t>(header);
auto cpu_number = ContextSwitchRecordFields::CpuNumber::Get<trace_cpu_number_t>(header);
std::optional timestamp = record.ReadUint64();
if (!timestamp.has_value()) {
return false;
}
std::optional incoming_tid = record.ReadUint64();
if (!incoming_tid.has_value()) {
return false;
}
std::vector<Argument> arguments;
if (!ReadArguments(record, argument_count, &arguments)) {
return false;
}
record_consumer_(Record(
Record::SchedulerEvent{{*timestamp, cpu_number, *incoming_tid, std::move(arguments)}}));
return true;
}
return false;
}
bool TraceReader::ReadLogRecord(Chunk& record, RecordHeader header) {
auto log_message_length = LogRecordFields::LogMessageLength::Get<uint16_t>(header);
if (log_message_length > LogRecordFields::kMaxMessageLength)
return false;
auto thread_ref = LogRecordFields::ThreadRef::Get<trace_encoded_thread_ref_t>(header);
std::optional timestamp_opt = record.ReadUint64();
if (!timestamp_opt.has_value()) {
return false;
}
trace_ticks_t timestamp = timestamp_opt.value();
ProcessThread process_thread;
if (!DecodeThreadRef(record, thread_ref, &process_thread)) {
return false;
}
std::optional log_message = record.ReadString(log_message_length);
if (!log_message.has_value()) {
return false;
}
record_consumer_(
Record(Record::Log{timestamp, process_thread, fbl::String(log_message.value())}));
return true;
}
bool TraceReader::ReadLargeRecord(trace::Chunk& record, trace::RecordHeader header) {
auto large_type = LargeRecordFields::LargeType::Get<LargeRecordType>(header);
switch (large_type) {
case LargeRecordType::kBlob:
return ReadLargeBlob(record, header);
default:
ReportError(
fbl::StringPrintf("Skipping unknown large record type %d", ToUnderlyingType(large_type)));
}
return true;
}
bool TraceReader::ReadLargeBlob(trace::Chunk& record, trace::RecordHeader header) {
auto format_type = LargeBlobFields::BlobFormat::Get<trace_blob_format_t>(header);
switch (format_type) {
case TRACE_BLOB_FORMAT_EVENT: {
std::optional format_header_opt = record.ReadUint64();
if (!format_header_opt.has_value()) {
return false;
}
uint64_t format_header = format_header_opt.value();
using Format = BlobFormatEventFields;
auto category_ref = Format::CategoryStringRef::Get<trace_encoded_string_ref_t>(format_header);
auto name_ref = Format::NameStringRef::Get<trace_encoded_string_ref_t>(format_header);
auto argument_count = Format::ArgumentCount::Get<size_t>(format_header);
auto thread_ref = Format::ThreadRef::Get<trace_encoded_thread_ref_t>(format_header);
fbl::String category;
if (!DecodeStringRef(record, category_ref, &category)) {
return false;
}
fbl::String name;
if (!DecodeStringRef(record, name_ref, &name)) {
return false;
}
std::optional timestamp_opt = record.ReadUint64();
if (!timestamp_opt.has_value()) {
return false;
}
trace_ticks_t timestamp = timestamp_opt.value();
ProcessThread process_thread;
if (!DecodeThreadRef(record, thread_ref, &process_thread)) {
return false;
}
std::vector<Argument> arguments;
if (!ReadArguments(record, argument_count, &arguments)) {
return false;
}
std::optional blob_size = record.ReadUint64();
if (!blob_size.has_value()) {
return false;
}
std::optional blob = record.ReadInPlace(trace::BytesToWords(trace::Pad(blob_size.value())));
if (!blob.has_value()) {
return false;
}
record_consumer_(Record(Record::Large(LargeRecordData::Blob(LargeRecordData::BlobEvent{
std::move(category),
std::move(name),
timestamp,
process_thread,
std::move(arguments),
blob.value(),
blob_size.value(),
}))));
break;
}
case TRACE_BLOB_FORMAT_ATTACHMENT: {
std::optional format_header_opt = record.ReadUint64();
if (!format_header_opt.has_value()) {
return false;
}
uint64_t format_header = format_header_opt.value();
using Format = BlobFormatAttachmentFields;
auto category_ref = Format::CategoryStringRef::Get<trace_encoded_string_ref_t>(format_header);
auto name_ref = Format::NameStringRef::Get<trace_encoded_string_ref_t>(format_header);
fbl::String category;
if (!DecodeStringRef(record, category_ref, &category)) {
return false;
}
fbl::String name;
if (!DecodeStringRef(record, name_ref, &name)) {
return false;
}
std::optional blob_size = record.ReadUint64();
if (!blob_size.has_value()) {
return false;
}
std::optional blob = record.ReadInPlace(trace::BytesToWords(trace::Pad(blob_size.value())));
if (!blob.has_value()) {
return false;
}
record_consumer_(Record(Record::Large(LargeRecordData::Blob(LargeRecordData::BlobAttachment{
std::move(category),
std::move(name),
blob.value(),
blob_size.value(),
}))));
break;
}
default:
ReportError(fbl::StringPrintf("Skipping unknown large blob record format %d",
ToUnderlyingType(format_type)));
}
return true;
}
bool TraceReader::ReadArguments(Chunk& record, size_t count, std::vector<Argument>* out_arguments) {
while (count-- > 0) {
std::optional header_opt = record.ReadUint64();
if (!header_opt.has_value()) {
ReportError("Failed to read argument header");
return false;
}
ArgumentHeader header = header_opt.value();
auto size = ArgumentFields::ArgumentSize::Get<size_t>(header);
if (size == 0) {
ReportError("Invalid argument size");
return false;
}
std::optional arg_opt = record.ReadChunk(size - 1);
if (!arg_opt.has_value()) {
ReportError("Failed to read argument");
return false;
}
Chunk& arg = arg_opt.value();
auto name_ref = ArgumentFields::NameRef::Get<trace_encoded_string_ref_t>(header);
fbl::String name;
if (!DecodeStringRef(arg, name_ref, &name)) {
ReportError("Failed to read argument name");
return false;
}
auto type = ArgumentFields::Type::Get<ArgumentType>(header);
switch (type) {
case ArgumentType::kNull: {
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeNull()});
break;
}
case ArgumentType::kBool: {
auto value = BoolArgumentFields::Value::Get<bool>(header);
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeBool(value)});
break;
}
case ArgumentType::kInt32: {
auto value = Int32ArgumentFields::Value::Get<int32_t>(header);
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeInt32(value)});
break;
}
case ArgumentType::kUint32: {
auto value = Uint32ArgumentFields::Value::Get<uint32_t>(header);
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeUint32(value)});
break;
}
case ArgumentType::kInt64: {
std::optional value = arg.ReadInt64();
if (!value.has_value()) {
ReportError("Failed to read int64 argument value");
return false;
}
out_arguments->push_back(
Argument{std::move(name), ArgumentValue::MakeInt64(value.value())});
break;
}
case ArgumentType::kUint64: {
std::optional value = arg.ReadUint64();
if (!value.has_value()) {
ReportError("Failed to read uint64 argument value");
return false;
}
out_arguments->push_back(
Argument{std::move(name), ArgumentValue::MakeUint64(value.value())});
break;
}
case ArgumentType::kDouble: {
std::optional value = arg.ReadDouble();
if (!value.has_value()) {
ReportError("Failed to read double argument value");
return false;
}
out_arguments->push_back(
Argument{std::move(name), ArgumentValue::MakeDouble(value.value())});
break;
}
case ArgumentType::kString: {
auto string_ref = StringArgumentFields::Index::Get<trace_encoded_string_ref_t>(header);
fbl::String value;
if (!DecodeStringRef(arg, string_ref, &value)) {
ReportError("Failed to read string argument value");
return false;
}
out_arguments->push_back(
Argument{std::move(name), ArgumentValue::MakeString(std::move(value))});
break;
}
case ArgumentType::kPointer: {
std::optional value = arg.ReadUint64();
if (!value.has_value()) {
ReportError("Failed to read pointer argument value");
return false;
}
out_arguments->push_back(
Argument{std::move(name), ArgumentValue::MakePointer(value.value())});
break;
}
case ArgumentType::kKoid: {
std::optional value = arg.ReadUint64();
if (!value.has_value()) {
ReportError("Failed to read koid argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeKoid(value.value())});
break;
}
default: {
// Ignore unknown argument types for forward compatibility.
ReportError(fbl::StringPrintf("Skipping argument of unknown type %d, argument name %s",
static_cast<uint32_t>(type), name.c_str()));
break;
}
}
}
return true;
}
fbl::String TraceReader::GetProviderName(ProviderId id) const {
auto it = providers_.find(id);
if (it != providers_.end())
return it->name;
return fbl::String();
}
void TraceReader::SetCurrentProvider(ProviderId id) {
auto it = providers_.find(id);
if (it != providers_.end()) {
current_provider_ = &*it;
return;
}
ReportError(fbl::StringPrintf("Registering non-existent provider %u\n", id));
RegisterProvider(id, "");
}
void TraceReader::RegisterProvider(ProviderId id, fbl::String name) {
auto provider = std::make_unique<ProviderInfo>();
provider->id = id;
provider->name = std::move(name);
current_provider_ = provider.get();
providers_.insert_or_replace(std::move(provider));
}
void TraceReader::RegisterString(trace_string_index_t index, const fbl::String& string) {
ZX_DEBUG_ASSERT(index >= TRACE_ENCODED_STRING_REF_MIN_INDEX &&
index <= TRACE_ENCODED_STRING_REF_MAX_INDEX);
StringTableEntry entry(index, string);
current_provider_->string_table.emplace(index, std::move(entry));
}
void TraceReader::RegisterThread(trace_thread_index_t index, const ProcessThread& process_thread) {
ZX_DEBUG_ASSERT(index >= TRACE_ENCODED_THREAD_REF_MIN_INDEX &&
index <= TRACE_ENCODED_THREAD_REF_MAX_INDEX);
ThreadTableEntry entry(index, process_thread);
current_provider_->thread_table.emplace(index, std::move(entry));
}
bool TraceReader::DecodeStringRef(Chunk& chunk, trace_encoded_string_ref_t string_ref,
fbl::String* out_string) const {
if (string_ref == TRACE_ENCODED_STRING_REF_EMPTY) {
out_string->clear();
return true;
}
if (string_ref & TRACE_ENCODED_STRING_REF_INLINE_FLAG) {
size_t length = string_ref & TRACE_ENCODED_STRING_REF_LENGTH_MASK;
if (length > TRACE_ENCODED_STRING_REF_MAX_LENGTH) {
ReportError("Could not read inline string");
return false;
}
std::optional string_view = chunk.ReadString(length);
if (!string_view.has_value()) {
ReportError("Could not read inline string");
return false;
}
*out_string = string_view.value();
return true;
}
auto it = current_provider_->string_table.find(string_ref);
if (it == current_provider_->string_table.end()) {
fbl::String errorMessage = fbl::StringPrintf("String ref: '%d' not in table", string_ref);
ReportError(errorMessage);
return false;
}
*out_string = it->second.string;
return true;
}
bool TraceReader::DecodeThreadRef(Chunk& chunk, trace_encoded_thread_ref_t thread_ref,
ProcessThread* out_process_thread) const {
if (thread_ref == TRACE_ENCODED_THREAD_REF_INLINE) {
std::optional process_koid = chunk.ReadUint64();
if (!process_koid.has_value()) {
ReportError("Could not read inline process");
return false;
}
std::optional thread_koid = chunk.ReadUint64();
if (!thread_koid.has_value()) {
ReportError("Could not read inline thread");
return false;
}
*out_process_thread = ProcessThread(process_koid.value(), thread_koid.value());
return true;
}
auto it = current_provider_->thread_table.find(thread_ref);
if (it == current_provider_->thread_table.end()) {
ReportError(fbl::StringPrintf("Thread ref 0x%x not in table", thread_ref));
return false;
}
*out_process_thread = it->second.process_thread;
return true;
}
void TraceReader::ReportError(fbl::String error) const {
if (error_handler_)
error_handler_(std::move(error));
}
Chunk::Chunk(const uint64_t* begin, size_t num_words)
: begin_(begin), current_(begin), end_(begin_ + num_words) {}
std::optional<uint64_t> Chunk::ReadUint64() {
if (current_ < end_) {
return *current_++;
}
return std::nullopt;
}
std::optional<int64_t> Chunk::ReadInt64() {
if (current_ < end_) {
return *reinterpret_cast<const int64_t*>(current_++);
}
return std::nullopt;
}
std::optional<double> Chunk::ReadDouble() {
if (current_ < end_) {
return *reinterpret_cast<const double*>(current_++);
}
return std::nullopt;
}
std::optional<Chunk> Chunk::ReadChunk(size_t num_words) {
if (current_ + num_words > end_)
return std::nullopt;
Chunk chunk(current_, num_words);
current_ += num_words;
return chunk;
}
std::optional<std::string_view> Chunk::ReadString(size_t length) {
auto num_words = BytesToWords(length);
if (current_ + num_words > end_)
return std::nullopt;
std::string_view view(reinterpret_cast<const char*>(current_), length);
current_ += num_words;
return view;
}
std::optional<void const*> Chunk::ReadInPlace(size_t num_words) {
if (current_ + num_words > end_)
return std::nullopt;
void const* current = current_;
current_ += num_words;
return current;
}
} // namespace trace