blob: b4455e0241a7414f247938f1b2bd2bb7391e1dd8 [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.
#ifndef TRACE_READER_READER_H_
#define TRACE_READER_READER_H_
#include <zircon/assert.h>
#include <memory>
#include <string_view>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/function.h>
#include <fbl/intrusive_hash_table.h>
#include <fbl/macros.h>
#include <fbl/string.h>
#include <trace-reader/records.h>
namespace trace {
class Chunk;
// Reads trace records.
// The input is a collection of |Chunk| objects (see class Chunk below).
//
// One use-case is reading records across an entire trace, which means across
// multiple providers, and makes no assumptions about the ordering of records
// it receives other than requiring objects referenced by ID (threads, strings)
// are defined before they are used. Note that as a consequence of this, one
// |TraceReader| class will maintain state of reading the entire trace: it
// generally doesn't work to create multiple |TraceReader| classes for one
// trace.
class TraceReader {
public:
// Called once for each record read by |ReadRecords|.
// TODO(jeffbrown): It would be nice to get rid of this by making |ReadRecords|
// return std::optional<Record> as an out parameter.
using RecordConsumer = fbl::Function<void(Record)>;
// Callback invoked when decoding errors are detected in the trace.
using ErrorHandler = fbl::Function<void(fbl::String)>;
explicit TraceReader(RecordConsumer record_consumer, ErrorHandler error_handler);
// Reads as many records as possible from the chunk, invoking the
// record consumer for each one. Returns true if the stream could possibly
// contain more records if the chunk were extended with new data.
// Returns false if the trace stream is unrecoverably corrupt and no
// further decoding is possible. May be called repeatedly with new
// chunks as they become available to resume decoding.
bool ReadRecords(Chunk& chunk);
// Gets the current trace provider id.
// Returns 0 if no providers have been registered yet.
ProviderId current_provider_id() const { return current_provider_->id; }
// Gets the name of the current trace provider.
// Returns an empty string if the current provider id is 0.
const fbl::String& current_provider_name() const { return current_provider_->name; }
// Gets the name of the specified provider, or an empty string if there is
// no such provider.
fbl::String GetProviderName(ProviderId id) const;
const ErrorHandler& error_handler() const { return error_handler_; }
protected:
void ReportError(fbl::String error) const;
private:
bool ReadMetadataRecord(Chunk& record, RecordHeader header);
bool ReadInitializationRecord(Chunk& record, RecordHeader header);
bool ReadStringRecord(Chunk& record, RecordHeader header);
bool ReadThreadRecord(Chunk& record, RecordHeader header);
bool ReadEventRecord(Chunk& record, RecordHeader header);
bool ReadBlobRecord(Chunk& record, RecordHeader header, void** out_ptr);
bool ReadKernelObjectRecord(Chunk& record, RecordHeader header);
bool ReadContextSwitchRecord(Chunk& record, RecordHeader header);
bool ReadLogRecord(Chunk& record, RecordHeader header);
bool ReadArguments(Chunk& record, size_t count, fbl::Vector<Argument>* out_arguments);
bool ReadLargeRecord(Chunk& record, RecordHeader header);
bool ReadLargeBlob(Chunk& record, RecordHeader header);
void SetCurrentProvider(ProviderId id);
void RegisterProvider(ProviderId id, fbl::String name);
void RegisterString(trace_string_index_t index, fbl::String string);
void RegisterThread(trace_thread_index_t index, const ProcessThread& process_thread);
bool DecodeStringRef(Chunk& chunk, trace_encoded_string_ref_t string_ref,
fbl::String* out_string) const;
bool DecodeThreadRef(Chunk& chunk, trace_encoded_thread_ref_t thread_ref,
ProcessThread* out_process_thread) const;
RecordConsumer const record_consumer_;
ErrorHandler const error_handler_;
RecordHeader pending_header_ = 0u;
struct StringTableEntry : public fbl::SinglyLinkedListable<std::unique_ptr<StringTableEntry>> {
StringTableEntry(trace_string_index_t index, fbl::String string)
: index(index), string(std::move(string)) {}
trace_string_index_t const index;
fbl::String const string;
// Used by the hash table.
trace_string_index_t GetKey() const { return index; }
static size_t GetHash(trace_string_index_t key) { return key; }
};
struct ThreadTableEntry : public fbl::SinglyLinkedListable<std::unique_ptr<ThreadTableEntry>> {
ThreadTableEntry(trace_thread_index_t index, const ProcessThread& process_thread)
: index(index), process_thread(process_thread) {}
trace_thread_index_t const index;
ProcessThread const process_thread;
// Used by the hash table.
trace_thread_index_t GetKey() const { return index; }
static size_t GetHash(trace_thread_index_t key) { return key; }
};
struct ProviderInfo : public fbl::SinglyLinkedListable<std::unique_ptr<ProviderInfo>> {
ProviderId id;
fbl::String name;
// TODO(fxbug.dev/30999): It would be more efficient to use something like
// std::unordered_map<> here. In particular, the table entries are
// small enough that it doesn't make sense to heap allocate them
// individually.
fbl::HashTable<trace_string_index_t, std::unique_ptr<StringTableEntry>> string_table;
fbl::HashTable<trace_thread_index_t, std::unique_ptr<ThreadTableEntry>> thread_table;
// Used by the hash table.
ProviderId GetKey() const { return id; }
static size_t GetHash(ProviderId key) { return key; }
};
fbl::HashTable<ProviderId, std::unique_ptr<ProviderInfo>> providers_;
ProviderInfo* current_provider_ = nullptr;
DISALLOW_COPY_ASSIGN_AND_MOVE(TraceReader);
};
// Provides support for reading sequences of 64-bit words from a contiguous
// region of memory. The main use-case of this class is input to |TraceReader|.
class Chunk final {
public:
Chunk();
explicit Chunk(const uint64_t* begin, size_t num_words);
uint64_t current_byte_offset() const {
return (reinterpret_cast<const uint8_t*>(current_) - reinterpret_cast<const uint8_t*>(begin_));
}
uint64_t remaining_words() const { return end_ - current_; }
// Reads from the chunk, maintaining proper alignment.
// Returns true on success, false if the chunk has insufficient remaining
// words to satisfy the request.
bool ReadUint64(uint64_t* out_value);
bool ReadInt64(int64_t* out_value);
bool ReadDouble(double* out_value);
bool ReadString(size_t length, std::string_view* out_string);
bool ReadChunk(size_t num_words, Chunk* out_chunk);
bool ReadInPlace(size_t num_words, const void** out_ptr);
private:
const uint64_t* begin_;
const uint64_t* current_;
const uint64_t* end_;
};
} // namespace trace
#endif // TRACE_READER_READER_H_