| // 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_RECORDS_H_ |
| #define TRACE_READER_RECORDS_H_ |
| |
| #include <lib/stdcompat/variant.h> |
| #include <lib/trace-engine/types.h> |
| #include <stdint.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/types.h> |
| |
| #include <new> |
| #include <utility> |
| |
| #include <fbl/array.h> |
| #include <fbl/macros.h> |
| #include <fbl/string.h> |
| #include <fbl/vector.h> |
| |
| namespace trace { |
| |
| // Holds a process koid and thread koid as a pair. |
| // Sorts by process koid then by thread koid. |
| class ProcessThread final { |
| public: |
| constexpr ProcessThread() : process_koid_(ZX_KOID_INVALID), thread_koid_(ZX_KOID_INVALID) {} |
| constexpr explicit ProcessThread(zx_koid_t process_koid, zx_koid_t thread_koid) |
| : process_koid_(process_koid), thread_koid_(thread_koid) {} |
| constexpr ProcessThread(const ProcessThread& other) |
| : process_koid_(other.process_koid_), thread_koid_(other.thread_koid_) {} |
| |
| constexpr explicit operator bool() const { return thread_koid_ != 0u || process_koid_ != 0u; } |
| |
| constexpr bool operator==(const ProcessThread& other) const { |
| return process_koid_ == other.process_koid_ && thread_koid_ == other.thread_koid_; |
| } |
| |
| constexpr bool operator!=(const ProcessThread& other) const { return !(*this == other); } |
| |
| constexpr bool operator<(const ProcessThread& other) const { |
| if (process_koid_ != other.process_koid_) { |
| return process_koid_ < other.process_koid_; |
| } |
| return thread_koid_ < other.thread_koid_; |
| } |
| |
| constexpr zx_koid_t process_koid() const { return process_koid_; } |
| constexpr zx_koid_t thread_koid() const { return thread_koid_; } |
| |
| ProcessThread& operator=(const ProcessThread& other) { |
| process_koid_ = other.process_koid_; |
| thread_koid_ = other.thread_koid_; |
| return *this; |
| } |
| |
| fbl::String ToString() const; |
| |
| private: |
| zx_koid_t process_koid_; |
| zx_koid_t thread_koid_; |
| }; |
| |
| // A typed argument value. |
| class ArgumentValue final { |
| public: |
| static ArgumentValue MakeNull() { return ArgumentValue(); } |
| |
| static ArgumentValue MakeBool(bool value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeInt32(int32_t value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeUint32(uint32_t value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeInt64(int64_t value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeUint64(uint64_t value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeDouble(double value) { return ArgumentValue(value); } |
| |
| static ArgumentValue MakeString(fbl::String value) { return ArgumentValue(std::move(value)); } |
| |
| static ArgumentValue MakePointer(uint64_t value) { return ArgumentValue(PointerTag(), value); } |
| |
| static ArgumentValue MakeKoid(zx_koid_t value) { return ArgumentValue(KoidTag(), value); } |
| |
| ArgumentValue(ArgumentValue&& other) { MoveFrom(std::move(other)); } |
| |
| ~ArgumentValue() { Destroy(); } |
| |
| ArgumentValue& operator=(ArgumentValue&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| ArgumentType type() const { return type_; } |
| |
| uint32_t GetBool() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kBool); |
| return bool_; |
| } |
| |
| int32_t GetInt32() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kInt32); |
| return int32_; |
| } |
| |
| uint32_t GetUint32() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kUint32); |
| return uint32_; |
| } |
| |
| int64_t GetInt64() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kInt64); |
| return int64_; |
| } |
| |
| uint64_t GetUint64() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kUint64); |
| return uint64_; |
| } |
| |
| double GetDouble() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kDouble); |
| return double_; |
| } |
| |
| const fbl::String& GetString() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kString); |
| return string_; |
| } |
| |
| uint64_t GetPointer() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kPointer); |
| return pointer_; |
| } |
| |
| zx_koid_t GetKoid() const { |
| ZX_DEBUG_ASSERT(type_ == ArgumentType::kKoid); |
| return koid_; |
| } |
| |
| fbl::String ToString() const; |
| |
| private: |
| struct PointerTag {}; |
| struct KoidTag {}; |
| |
| ArgumentValue() : type_(ArgumentType::kNull) {} |
| |
| explicit ArgumentValue(bool b) : type_(ArgumentType::kBool), bool_(b) {} |
| |
| explicit ArgumentValue(int32_t int32) : type_(ArgumentType::kInt32), int32_(int32) {} |
| |
| explicit ArgumentValue(uint32_t uint32) : type_(ArgumentType::kUint32), uint32_(uint32) {} |
| |
| explicit ArgumentValue(int64_t int64) : type_(ArgumentType::kInt64), int64_(int64) {} |
| |
| explicit ArgumentValue(uint64_t uint64) : type_(ArgumentType::kUint64), uint64_(uint64) {} |
| |
| explicit ArgumentValue(double d) : type_(ArgumentType::kDouble), double_(d) {} |
| |
| explicit ArgumentValue(fbl::String string) : type_(ArgumentType::kString) { |
| new (&string_) fbl::String(std::move(string)); |
| } |
| |
| explicit ArgumentValue(PointerTag, uint64_t pointer) |
| : type_(ArgumentType::kPointer), pointer_(pointer) {} |
| |
| explicit ArgumentValue(KoidTag, zx_koid_t koid) : type_(ArgumentType::kKoid), koid_(koid) {} |
| |
| void Destroy(); |
| void MoveFrom(ArgumentValue&& other); |
| |
| ArgumentType type_; |
| union { |
| bool bool_; |
| int32_t int32_; |
| uint32_t uint32_; |
| int64_t int64_; |
| uint64_t uint64_; |
| double double_; |
| fbl::String string_; |
| uint64_t pointer_; |
| zx_koid_t koid_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ArgumentValue); |
| }; |
| |
| // Named argument and value. |
| class Argument final { |
| public: |
| explicit Argument(fbl::String name, ArgumentValue value) |
| : name_(std::move(name)), value_(std::move(value)) {} |
| |
| Argument(Argument&& other) : name_(std::move(other.name_)), value_(std::move(other.value_)) {} |
| |
| Argument& operator=(Argument&& other) { |
| name_ = std::move(other.name_); |
| value_ = std::move(other.value_); |
| return *this; |
| } |
| |
| const fbl::String& name() const { return name_; } |
| const ArgumentValue& value() const { return value_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| fbl::String name_; |
| ArgumentValue value_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Argument); |
| }; |
| |
| // Trace Info type specific data |
| class TraceInfoContent final { |
| public: |
| // Magic number record data |
| struct MagicNumberInfo { |
| uint32_t magic_value; |
| }; |
| |
| explicit TraceInfoContent(MagicNumberInfo magic_number_info) |
| : type_(TraceInfoType::kMagicNumber), magic_number_info_(std::move(magic_number_info)) {} |
| |
| const MagicNumberInfo& GetMagicNumberInfo() const { |
| ZX_DEBUG_ASSERT(type_ == TraceInfoType::kMagicNumber); |
| return magic_number_info_; |
| } |
| |
| TraceInfoContent(TraceInfoContent&& other) : type_(other.type_) { MoveFrom(std::move(other)); } |
| |
| ~TraceInfoContent() { Destroy(); } |
| |
| TraceInfoContent& operator=(TraceInfoContent&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| TraceInfoType type() const { return type_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| void Destroy(); |
| void MoveFrom(TraceInfoContent&& other); |
| |
| TraceInfoType type_; |
| union { |
| MagicNumberInfo magic_number_info_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TraceInfoContent); |
| }; |
| |
| // Metadata type specific data. |
| class MetadataContent final { |
| public: |
| // Provider info event data. |
| struct ProviderInfo { |
| ProviderId id; |
| fbl::String name; |
| }; |
| |
| // Provider section event data. |
| struct ProviderSection { |
| ProviderId id; |
| }; |
| |
| // Provider event event data. |
| struct ProviderEvent { |
| ProviderId id; |
| ProviderEventType event; |
| }; |
| |
| // Trace info record data |
| struct TraceInfo { |
| TraceInfoType type() const { return content.type(); } |
| TraceInfoContent content; |
| }; |
| |
| explicit MetadataContent(ProviderInfo provider_info) |
| : type_(MetadataType::kProviderInfo), provider_info_(std::move(provider_info)) {} |
| |
| explicit MetadataContent(ProviderSection provider_section) |
| : type_(MetadataType::kProviderSection), provider_section_(std::move(provider_section)) {} |
| |
| explicit MetadataContent(ProviderEvent provider_event) |
| : type_(MetadataType::kProviderEvent), provider_event_(std::move(provider_event)) {} |
| |
| explicit MetadataContent(TraceInfo trace_info) |
| : type_(MetadataType::kTraceInfo), trace_info_(std::move(trace_info)) {} |
| |
| const ProviderInfo& GetProviderInfo() const { |
| ZX_DEBUG_ASSERT(type_ == MetadataType::kProviderInfo); |
| return provider_info_; |
| } |
| |
| const ProviderSection& GetProviderSection() const { |
| ZX_DEBUG_ASSERT(type_ == MetadataType::kProviderSection); |
| return provider_section_; |
| } |
| |
| const ProviderEvent& GetProviderEvent() const { |
| ZX_DEBUG_ASSERT(type_ == MetadataType::kProviderEvent); |
| return provider_event_; |
| } |
| |
| MetadataContent(MetadataContent&& other) : type_(other.type_) { MoveFrom(std::move(other)); } |
| |
| ~MetadataContent() { Destroy(); } |
| |
| MetadataContent& operator=(MetadataContent&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| MetadataType type() const { return type_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| void Destroy(); |
| void MoveFrom(MetadataContent&& other); |
| |
| MetadataType type_; |
| union { |
| ProviderInfo provider_info_; |
| ProviderSection provider_section_; |
| ProviderEvent provider_event_; |
| TraceInfo trace_info_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(MetadataContent); |
| }; |
| |
| // Event type specific data. |
| class EventData final { |
| public: |
| // Instant event data. |
| struct Instant { |
| EventScope scope; |
| }; |
| |
| // Counter event data. |
| struct Counter { |
| trace_counter_id_t id; |
| }; |
| |
| // Duration begin event data. |
| struct DurationBegin {}; |
| |
| // Duration end event data. |
| struct DurationEnd {}; |
| |
| // Duration complete event data. |
| struct DurationComplete { |
| trace_ticks_t end_time; |
| }; |
| |
| // Async begin event data. |
| struct AsyncBegin { |
| trace_async_id_t id; |
| }; |
| |
| // Async instant event data. |
| struct AsyncInstant { |
| trace_async_id_t id; |
| }; |
| |
| // Async end event data. |
| struct AsyncEnd { |
| trace_async_id_t id; |
| }; |
| |
| // Flow begin event data. |
| struct FlowBegin { |
| trace_flow_id_t id; |
| }; |
| |
| // Flow step event data. |
| struct FlowStep { |
| trace_flow_id_t id; |
| }; |
| |
| // Flow end event data. |
| struct FlowEnd { |
| trace_flow_id_t id; |
| }; |
| |
| explicit EventData(Instant instant) : type_(EventType::kInstant), instant_(std::move(instant)) {} |
| |
| explicit EventData(Counter counter) : type_(EventType::kCounter), counter_(std::move(counter)) {} |
| |
| explicit EventData(DurationBegin duration_begin) |
| : type_(EventType::kDurationBegin), duration_begin_(std::move(duration_begin)) {} |
| |
| explicit EventData(DurationEnd duration_end) |
| : type_(EventType::kDurationEnd), duration_end_(std::move(duration_end)) {} |
| |
| explicit EventData(DurationComplete duration_complete) |
| : type_(EventType::kDurationComplete), duration_complete_(std::move(duration_complete)) {} |
| |
| explicit EventData(AsyncBegin async_begin) |
| : type_(EventType::kAsyncBegin), async_begin_(std::move(async_begin)) {} |
| |
| explicit EventData(AsyncInstant async_instant) |
| : type_(EventType::kAsyncInstant), async_instant_(std::move(async_instant)) {} |
| |
| explicit EventData(AsyncEnd async_end) |
| : type_(EventType::kAsyncEnd), async_end_(std::move(async_end)) {} |
| |
| explicit EventData(FlowBegin flow_begin) |
| : type_(EventType::kFlowBegin), flow_begin_(std::move(flow_begin)) {} |
| |
| explicit EventData(FlowStep flow_step) |
| : type_(EventType::kFlowStep), flow_step_(std::move(flow_step)) {} |
| |
| explicit EventData(FlowEnd flow_end) |
| : type_(EventType::kFlowEnd), flow_end_(std::move(flow_end)) {} |
| |
| EventData(EventData&& other) { MoveFrom(std::move(other)); } |
| |
| ~EventData() { Destroy(); } |
| |
| EventData& operator=(EventData&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| const Instant& GetInstant() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kInstant); |
| return instant_; |
| } |
| |
| const Counter& GetCounter() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kCounter); |
| return counter_; |
| } |
| |
| const DurationBegin& GetDurationBegin() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kDurationBegin); |
| return duration_begin_; |
| } |
| |
| const DurationEnd& GetDurationEnd() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kDurationEnd); |
| return duration_end_; |
| } |
| |
| const DurationComplete& GetDurationComplete() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kDurationComplete); |
| return duration_complete_; |
| } |
| |
| const AsyncBegin& GetAsyncBegin() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kAsyncBegin); |
| return async_begin_; |
| } |
| |
| const AsyncInstant& GetAsyncInstant() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kAsyncInstant); |
| return async_instant_; |
| } |
| |
| const AsyncEnd& GetAsyncEnd() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kAsyncEnd); |
| return async_end_; |
| } |
| |
| const FlowBegin& GetFlowBegin() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kFlowBegin); |
| return flow_begin_; |
| } |
| |
| const FlowStep& GetFlowStep() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kFlowStep); |
| return flow_step_; |
| } |
| |
| const FlowEnd& GetFlowEnd() const { |
| ZX_DEBUG_ASSERT(type_ == EventType::kFlowEnd); |
| return flow_end_; |
| } |
| |
| EventType type() const { return type_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| void Destroy(); |
| void MoveFrom(EventData&& other); |
| |
| EventType type_; |
| union { |
| Instant instant_; |
| Counter counter_; |
| DurationBegin duration_begin_; |
| DurationEnd duration_end_; |
| DurationComplete duration_complete_; |
| AsyncBegin async_begin_; |
| AsyncInstant async_instant_; |
| AsyncEnd async_end_; |
| FlowBegin flow_begin_; |
| FlowStep flow_step_; |
| FlowEnd flow_end_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(EventData); |
| }; |
| |
| // Large record specific data |
| class LargeRecordData final { |
| public: |
| struct BlobEvent { |
| fbl::String category; |
| fbl::String name; |
| trace_ticks_t timestamp; |
| ProcessThread process_thread; |
| fbl::Vector<Argument> arguments; |
| |
| const void* blob; |
| uint64_t blob_size; |
| }; |
| |
| struct BlobAttachment { |
| fbl::String category; |
| fbl::String name; |
| |
| const void* blob; |
| uint64_t blob_size; |
| }; |
| |
| // Large blob record data. |
| // The blob data pointer is actually just a pointer into the trace |
| // reader's buffer. As such, the record consumer should not attempt |
| // to free it. The record consumer must finish processing the |
| // blob data within the callback, as the pointer may not be valid |
| // after the completion of that callback. |
| using Blob = cpp17::variant<BlobEvent, BlobAttachment>; |
| |
| explicit LargeRecordData(Blob blob) : type_(LargeRecordType::kBlob), blob_(std::move(blob)) {} |
| |
| const Blob& GetBlob() const { |
| ZX_DEBUG_ASSERT(type_ == LargeRecordType::kBlob); |
| return blob_; |
| } |
| |
| LargeRecordData(LargeRecordData&& other) : type_(other.type_) { MoveFrom(std::move(other)); } |
| |
| ~LargeRecordData() { Destroy(); } |
| |
| LargeRecordData& operator=(LargeRecordData&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| LargeRecordType type() const { return type_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| void Destroy(); |
| void MoveFrom(LargeRecordData&& other); |
| |
| LargeRecordType type_; |
| union { |
| Blob blob_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LargeRecordData); |
| }; |
| |
| // A decoded record. |
| class Record final { |
| public: |
| // Metadata record data. |
| struct Metadata { |
| MetadataType type() const { return content.type(); } |
| MetadataContent content; |
| }; |
| |
| // Initialization record data. |
| struct Initialization { |
| trace_ticks_t ticks_per_second; |
| }; |
| |
| // String record data. |
| struct String { |
| trace_string_index_t index; |
| fbl::String string; |
| }; |
| |
| // Thread record data. |
| struct Thread { |
| trace_thread_index_t index; |
| ProcessThread process_thread; |
| }; |
| |
| // Event record data. |
| struct Event { |
| EventType type() const { return data.type(); } |
| trace_ticks_t timestamp; |
| ProcessThread process_thread; |
| fbl::String category; |
| fbl::String name; |
| fbl::Vector<Argument> arguments; |
| EventData data; |
| }; |
| |
| // Blob record data. |
| // Since blobs can be rather large we avoid unnecessary copying of them. |
| // This then means that the consumer must process the blob's payload |
| // before the next record is read. |
| struct Blob { |
| trace_blob_type_t type; |
| fbl::String name; |
| const void* blob; |
| size_t blob_size; |
| }; |
| |
| // Kernel Object record data. |
| struct KernelObject { |
| zx_koid_t koid; |
| zx_obj_type_t object_type; |
| fbl::String name; |
| fbl::Vector<Argument> arguments; |
| }; |
| |
| // Context Switch record data. |
| struct ContextSwitch { |
| trace_ticks_t timestamp; |
| trace_cpu_number_t cpu_number; |
| ThreadState outgoing_thread_state; |
| ProcessThread outgoing_thread; |
| ProcessThread incoming_thread; |
| trace_thread_priority_t outgoing_thread_priority; |
| trace_thread_priority_t incoming_thread_priority; |
| }; |
| |
| // Log record data. |
| struct Log { |
| trace_ticks_t timestamp; |
| ProcessThread process_thread; |
| fbl::String message; |
| }; |
| |
| // Large record data. |
| using Large = LargeRecordData; |
| |
| explicit Record(Metadata record) : type_(RecordType::kMetadata), metadata_(std::move(record)) {} |
| |
| explicit Record(Initialization record) |
| : type_(RecordType::kInitialization), initialization_(std::move(record)) {} |
| |
| explicit Record(String record) : type_(RecordType::kString) { |
| new (&string_) String(std::move(record)); |
| } |
| |
| explicit Record(Thread record) : type_(RecordType::kThread) { |
| new (&thread_) Thread(std::move(record)); |
| } |
| |
| explicit Record(Event record) : type_(RecordType::kEvent) { |
| new (&event_) Event(std::move(record)); |
| } |
| |
| explicit Record(Blob record) : type_(RecordType::kBlob) { new (&blob_) Blob(std::move(record)); } |
| |
| explicit Record(KernelObject record) : type_(RecordType::kKernelObject) { |
| new (&kernel_object_) KernelObject(std::move(record)); |
| } |
| |
| explicit Record(ContextSwitch record) : type_(RecordType::kContextSwitch) { |
| new (&context_switch_) ContextSwitch(std::move(record)); |
| } |
| |
| explicit Record(Log record) : type_(RecordType::kLog) { new (&log_) Log(std::move(record)); } |
| |
| explicit Record(Large record) : type_(RecordType::kLargeRecord) { |
| new (&large_) Large(std::move(record)); |
| } |
| |
| Record(Record&& other) { MoveFrom(std::move(other)); } |
| |
| ~Record() { Destroy(); } |
| |
| Record& operator=(Record&& other) { |
| Destroy(); |
| MoveFrom(std::move(other)); |
| return *this; |
| } |
| |
| const Metadata& GetMetadata() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kMetadata); |
| return metadata_; |
| } |
| |
| const Initialization& GetInitialization() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kInitialization); |
| return initialization_; |
| } |
| |
| const String& GetString() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kString); |
| return string_; |
| } |
| |
| const Thread& GetThread() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kThread); |
| return thread_; |
| } |
| |
| const Event& GetEvent() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kEvent); |
| return event_; |
| } |
| |
| const Blob& GetBlob() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kBlob); |
| return blob_; |
| } |
| |
| const KernelObject& GetKernelObject() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kKernelObject); |
| return kernel_object_; |
| } |
| |
| const ContextSwitch& GetContextSwitch() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kContextSwitch); |
| return context_switch_; |
| } |
| |
| const Log& GetLog() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kLog); |
| return log_; |
| } |
| |
| const Large& GetLargeRecord() const { |
| ZX_DEBUG_ASSERT(type_ == RecordType::kLargeRecord); |
| return large_; |
| } |
| |
| RecordType type() const { return type_; } |
| |
| fbl::String ToString() const; |
| |
| private: |
| void Destroy(); |
| void MoveFrom(Record&& other); |
| |
| RecordType type_; |
| union { |
| Metadata metadata_; |
| Initialization initialization_; |
| String string_; |
| Thread thread_; |
| Event event_; |
| Blob blob_; |
| KernelObject kernel_object_; |
| ContextSwitch context_switch_; |
| Log log_; |
| Large large_; |
| }; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Record); |
| }; |
| |
| } // namespace trace |
| |
| #endif // TRACE_READER_RECORDS_H_ |