blob: 81b59e678a0cf03e38aefc6a68b4277204d6204c [file] [log] [blame] [edit]
// 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 <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) {
if (!pending_header_ && !chunk.ReadUint64(&pending_header_))
return true; // need more data
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(fxbug.dev/23072): Here we assume that the entire blob payload can
// fit into the read buffer.
Chunk record;
if (!chunk.ReadChunk(size - 1, &record))
return true; // need more data to decode record
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::kContextSwitch: {
if (!ReadContextSwitchRecord(record, pending_header_)) {
ReportError("Failed to read context switch 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);
fbl::StringPiece name_view;
if (!record.ReadString(name_length, &name_view))
return false;
fbl::String name(name_view);
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) {
trace_ticks_t ticks_per_second;
if (!record.ReadUint64(&ticks_per_second) || !ticks_per_second)
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);
if (index < TRACE_ENCODED_STRING_REF_MIN_INDEX || index > TRACE_ENCODED_STRING_REF_MAX_INDEX) {
ReportError("Invalid string index");
return false;
}
auto length = StringRecordFields::StringLength::Get<size_t>(header);
fbl::StringPiece string_view;
if (!record.ReadString(length, &string_view))
return false;
fbl::String string(string_view);
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;
}
zx_koid_t process_koid, thread_koid;
if (!record.ReadUint64(&process_koid) || !record.ReadUint64(&thread_koid))
return false;
ProcessThread process_thread(process_koid, thread_koid);
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);
trace_ticks_t timestamp;
ProcessThread process_thread;
fbl::String category;
fbl::String name;
fbl::Vector<Argument> arguments;
if (!record.ReadUint64(&timestamp) || !DecodeThreadRef(record, thread_ref, &process_thread) ||
!DecodeStringRef(record, category_ref, &category) ||
!DecodeStringRef(record, name_ref, &name) ||
!ReadArguments(record, argument_count, &arguments))
return false;
switch (type) {
case EventType::kInstant: {
uint64_t scope;
if (!record.ReadUint64(&scope))
return false;
record_consumer_(Record(Record::Event{
timestamp, process_thread, std::move(category), std::move(name), std::move(arguments),
EventData(EventData::Instant{static_cast<EventScope>(scope)})}));
break;
}
case EventType::kCounter: {
trace_counter_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::Counter{id})}));
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: {
trace_ticks_t end_time;
if (!record.ReadUint64(&end_time))
return false;
record_consumer_(Record(Record::Event{timestamp, process_thread, std::move(category),
std::move(name), std::move(arguments),
EventData(EventData::DurationComplete{end_time})}));
break;
}
case EventType::kAsyncBegin: {
trace_async_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::AsyncBegin{id})}));
break;
}
case EventType::kAsyncInstant: {
trace_async_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::AsyncInstant{id})}));
break;
}
case EventType::kAsyncEnd: {
trace_async_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::AsyncEnd{id})}));
break;
}
case EventType::kFlowBegin: {
trace_flow_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowBegin{id})}));
break;
}
case EventType::kFlowStep: {
trace_flow_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowStep{id})}));
break;
}
case EventType::kFlowEnd: {
trace_flow_id_t id;
if (!record.ReadUint64(&id))
return false;
record_consumer_(
Record(Record::Event{timestamp, process_thread, std::move(category), std::move(name),
std::move(arguments), EventData(EventData::FlowEnd{id})}));
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;
const void* blob;
auto blob_words = BytesToWords(blob_size);
if (!record.ReadInPlace(blob_words, &blob))
return false;
record_consumer_(Record(Record::Blob{blob_type, name, blob, 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);
zx_koid_t koid;
fbl::String name;
fbl::Vector<Argument> arguments;
if (!record.ReadUint64(&koid) || !DecodeStringRef(record, name_ref, &name) ||
!ReadArguments(record, argument_count, &arguments))
return false;
record_consumer_(Record(Record::KernelObject{koid, object_type, name, std::move(arguments)}));
return true;
}
bool TraceReader::ReadContextSwitchRecord(Chunk& record, RecordHeader header) {
auto cpu_number = ContextSwitchRecordFields::CpuNumber::Get<trace_cpu_number_t>(header);
auto outgoing_thread_state =
ContextSwitchRecordFields::OutgoingThreadState::Get<ThreadState>(header);
auto outgoing_thread_priority =
ContextSwitchRecordFields::OutgoingThreadPriority::Get<trace_thread_priority_t>(header);
auto incoming_thread_priority =
ContextSwitchRecordFields::IncomingThreadPriority::Get<trace_thread_priority_t>(header);
auto outgoing_thread_ref =
ContextSwitchRecordFields::OutgoingThreadRef::Get<trace_encoded_thread_ref_t>(header);
auto incoming_thread_ref =
ContextSwitchRecordFields::IncomingThreadRef::Get<trace_encoded_thread_ref_t>(header);
trace_ticks_t timestamp;
ProcessThread outgoing_thread;
ProcessThread incoming_thread;
if (!record.ReadUint64(&timestamp) ||
!DecodeThreadRef(record, outgoing_thread_ref, &outgoing_thread) ||
!DecodeThreadRef(record, incoming_thread_ref, &incoming_thread))
return false;
record_consumer_(Record(
Record::ContextSwitch{timestamp, cpu_number, outgoing_thread_state, outgoing_thread,
incoming_thread, outgoing_thread_priority, incoming_thread_priority}));
return true;
}
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);
trace_ticks_t timestamp;
ProcessThread process_thread;
fbl::StringPiece log_message;
if (!record.ReadUint64(&timestamp) || !DecodeThreadRef(record, thread_ref, &process_thread) ||
!record.ReadString(log_message_length, &log_message))
return false;
record_consumer_(Record(Record::Log{timestamp, process_thread, fbl::String(log_message)}));
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: {
uint64_t format_header;
if (!record.ReadUint64(&format_header))
return false;
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;
fbl::String name;
trace_ticks_t timestamp;
ProcessThread process_thread;
fbl::Vector<Argument> arguments;
uint64_t blob_size;
const void* blob;
if (!DecodeStringRef(record, category_ref, &category) ||
!DecodeStringRef(record, name_ref, &name) || !record.ReadUint64(&timestamp) ||
!DecodeThreadRef(record, thread_ref, &process_thread) ||
!ReadArguments(record, argument_count, &arguments) || !record.ReadUint64(&blob_size) ||
!record.ReadInPlace(trace::BytesToWords(trace::Pad(blob_size)), &blob))
return false;
record_consumer_(Record(Record::Large(LargeRecordData::Blob(LargeRecordData::BlobEvent{
std::move(category),
std::move(name),
timestamp,
process_thread,
std::move(arguments),
blob,
blob_size,
}))));
break;
}
case TRACE_BLOB_FORMAT_ATTACHMENT: {
uint64_t format_header;
if (!record.ReadUint64(&format_header))
return false;
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;
fbl::String name;
uint64_t blob_size;
const void* blob;
if (!DecodeStringRef(record, category_ref, &category) ||
!DecodeStringRef(record, name_ref, &name) || !record.ReadUint64(&blob_size) ||
!record.ReadInPlace(trace::BytesToWords(trace::Pad(blob_size)), &blob))
return false;
record_consumer_(Record(Record::Large(LargeRecordData::Blob(LargeRecordData::BlobAttachment{
std::move(category),
std::move(name),
blob,
blob_size,
}))));
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, fbl::Vector<Argument>* out_arguments) {
while (count-- > 0) {
ArgumentHeader header;
if (!record.ReadUint64(&header)) {
ReportError("Failed to read argument header");
return false;
}
auto size = ArgumentFields::ArgumentSize::Get<size_t>(header);
Chunk arg;
if (!size || !record.ReadChunk(size - 1, &arg)) {
ReportError("Invalid argument size");
return false;
}
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: {
int64_t value;
if (!arg.ReadInt64(&value)) {
ReportError("Failed to read int64 argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeInt64(value)});
break;
}
case ArgumentType::kUint64: {
uint64_t value;
if (!arg.ReadUint64(&value)) {
ReportError("Failed to read uint64 argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeUint64(value)});
break;
}
case ArgumentType::kDouble: {
double value;
if (!arg.ReadDouble(&value)) {
ReportError("Failed to read double argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeDouble(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: {
uint64_t value;
if (!arg.ReadUint64(&value)) {
ReportError("Failed to read pointer argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakePointer(value)});
break;
}
case ArgumentType::kKoid: {
zx_koid_t value;
if (!arg.ReadUint64(&value)) {
ReportError("Failed to read koid argument value");
return false;
}
out_arguments->push_back(Argument{std::move(name), ArgumentValue::MakeKoid(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 = name;
current_provider_ = provider.get();
providers_.insert_or_replace(std::move(provider));
}
void TraceReader::RegisterString(trace_string_index_t index, fbl::String string) {
ZX_DEBUG_ASSERT(index >= TRACE_ENCODED_STRING_REF_MIN_INDEX &&
index <= TRACE_ENCODED_STRING_REF_MAX_INDEX);
auto entry = std::make_unique<StringTableEntry>(index, string);
current_provider_->string_table.insert_or_replace(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);
auto entry = std::make_unique<ThreadTableEntry>(index, process_thread);
current_provider_->thread_table.insert_or_replace(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;
fbl::StringPiece string_view;
if (length > TRACE_ENCODED_STRING_REF_MAX_LENGTH || !chunk.ReadString(length, &string_view)) {
ReportError("Could not read inline string");
return false;
}
*out_string = string_view;
return true;
}
auto it = current_provider_->string_table.find(string_ref);
if (it == current_provider_->string_table.end()) {
ReportError("String ref not in table");
return false;
}
*out_string = it->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) {
zx_koid_t process_koid, thread_koid;
if (!chunk.ReadUint64(&process_koid) || !chunk.ReadUint64(&thread_koid)) {
ReportError("Could not read inline process and thread");
return false;
}
*out_process_thread = ProcessThread(process_koid, thread_koid);
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->process_thread;
return true;
}
void TraceReader::ReportError(fbl::String error) const {
if (error_handler_)
error_handler_(std::move(error));
}
Chunk::Chunk() : begin_(nullptr), current_(nullptr), end_(nullptr) {}
Chunk::Chunk(const uint64_t* begin, size_t num_words)
: begin_(begin), current_(begin), end_(begin_ + num_words) {}
bool Chunk::ReadUint64(uint64_t* out_value) {
if (current_ < end_) {
*out_value = *current_++;
return true;
}
return false;
}
bool Chunk::ReadInt64(int64_t* out_value) {
if (current_ < end_) {
*out_value = *reinterpret_cast<const int64_t*>(current_++);
return true;
}
return false;
}
bool Chunk::ReadDouble(double* out_value) {
if (current_ < end_) {
*out_value = *reinterpret_cast<const double*>(current_++);
return true;
}
return false;
}
bool Chunk::ReadChunk(size_t num_words, Chunk* out_chunk) {
if (current_ + num_words > end_)
return false;
*out_chunk = Chunk(current_, num_words);
current_ += num_words;
return true;
}
bool Chunk::ReadString(size_t length, fbl::StringPiece* out_string) {
auto num_words = BytesToWords(length);
if (current_ + num_words > end_)
return false;
*out_string = fbl::StringPiece(reinterpret_cast<const char*>(current_), length);
current_ += num_words;
return true;
}
bool Chunk::ReadInPlace(size_t num_words, const void** out_ptr) {
if (current_ + num_words > end_)
return false;
*out_ptr = current_;
current_ += num_words;
return true;
}
} // namespace trace