1 // Copyright 2016 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef TRACE_READER_READER_H_ 6 #define TRACE_READER_READER_H_ 7 8 #include <trace-reader/records.h> 9 10 #include <fbl/algorithm.h> 11 #include <fbl/function.h> 12 #include <fbl/intrusive_hash_table.h> 13 #include <fbl/macros.h> 14 #include <fbl/string.h> 15 #include <fbl/string_piece.h> 16 #include <fbl/unique_ptr.h> 17 18 #include <zircon/assert.h> 19 20 #include <utility> 21 22 namespace trace { 23 24 class Chunk; 25 26 // Reads trace records. 27 // The input is a collection of |Chunk| objects (see class Chunk below). 28 // 29 // One use-case is reading records across an entire trace, which means across 30 // multiple providers, and makes no assumptions about the ordering of records 31 // it receives other than requiring objects referenced by ID (threads, strings) 32 // are defined before they are used. Note that as a consequence of this, one 33 // |TraceReader| class will maintain state of reading the entire trace: it 34 // generally doesn't work to create multiple |TraceReader| classes for one 35 // trace. 36 class TraceReader { 37 public: 38 // Called once for each record read by |ReadRecords|. 39 // TODO(jeffbrown): It would be nice to get rid of this by making |ReadRecords| 40 // return std::optional<Record> as an out parameter. 41 using RecordConsumer = fbl::Function<void(Record)>; 42 43 // Callback invoked when decoding errors are detected in the trace. 44 using ErrorHandler = fbl::Function<void(fbl::String)>; 45 46 explicit TraceReader(RecordConsumer record_consumer, 47 ErrorHandler error_handler); 48 49 // Reads as many records as possible from the chunk, invoking the 50 // record consumer for each one. Returns true if the stream could possibly 51 // contain more records if the chunk were extended with new data. 52 // Returns false if the trace stream is unrecoverably corrupt and no 53 // further decoding is possible. May be called repeatedly with new 54 // chunks as they become available to resume decoding. 55 bool ReadRecords(Chunk& chunk); 56 57 // Gets the current trace provider id. 58 // Returns 0 if no providers have been registered yet. current_provider_id()59 ProviderId current_provider_id() const { return current_provider_->id; } 60 61 // Gets the name of the current trace provider. 62 // Returns an empty string if the current provider id is 0. current_provider_name()63 const fbl::String& current_provider_name() const { 64 return current_provider_->name; 65 } 66 67 // Gets the name of the specified provider, or an empty string if there is 68 // no such provider. 69 fbl::String GetProviderName(ProviderId id) const; 70 error_handler()71 const ErrorHandler& error_handler() const { return error_handler_; } 72 73 private: 74 bool ReadMetadataRecord(Chunk& record, 75 RecordHeader header); 76 bool ReadInitializationRecord(Chunk& record, 77 RecordHeader header); 78 bool ReadStringRecord(Chunk& record, 79 RecordHeader header); 80 bool ReadThreadRecord(Chunk& record, 81 RecordHeader header); 82 bool ReadEventRecord(Chunk& record, RecordHeader header); 83 bool ReadBlobRecord(Chunk& record, RecordHeader header, void** out_ptr); 84 bool ReadKernelObjectRecord(Chunk& record, 85 RecordHeader header); 86 bool ReadContextSwitchRecord(Chunk& record, 87 RecordHeader header); 88 bool ReadLogRecord(Chunk& record, RecordHeader header); 89 bool ReadArguments(Chunk& record, 90 size_t count, 91 fbl::Vector<Argument>* out_arguments); 92 93 void SetCurrentProvider(ProviderId id); 94 void RegisterProvider(ProviderId id, fbl::String name); 95 void RegisterString(trace_string_index_t index, fbl::String string); 96 void RegisterThread(trace_thread_index_t index, const ProcessThread& process_thread); 97 98 bool DecodeStringRef(Chunk& chunk, 99 trace_encoded_string_ref_t string_ref, 100 fbl::String* out_string) const; 101 bool DecodeThreadRef(Chunk& chunk, 102 trace_encoded_thread_ref_t thread_ref, 103 ProcessThread* out_process_thread) const; 104 105 void ReportError(fbl::String error) const; 106 107 RecordConsumer const record_consumer_; 108 ErrorHandler const error_handler_; 109 110 RecordHeader pending_header_ = 0u; 111 112 struct StringTableEntry : public fbl::SinglyLinkedListable< 113 fbl::unique_ptr<StringTableEntry>> { StringTableEntryStringTableEntry114 StringTableEntry(trace_string_index_t index, 115 fbl::String string) 116 : index(index), string(std::move(string)) {} 117 118 trace_string_index_t const index; 119 fbl::String const string; 120 121 // Used by the hash table. GetKeyStringTableEntry122 trace_string_index_t GetKey() const { return index; } GetHashStringTableEntry123 static size_t GetHash(trace_string_index_t key) { return key; } 124 }; 125 126 struct ThreadTableEntry : public fbl::SinglyLinkedListable< 127 fbl::unique_ptr<ThreadTableEntry>> { ThreadTableEntryThreadTableEntry128 ThreadTableEntry(trace_thread_index_t index, 129 const ProcessThread& process_thread) 130 : index(index), process_thread(process_thread) {} 131 132 trace_thread_index_t const index; 133 ProcessThread const process_thread; 134 135 // Used by the hash table. GetKeyThreadTableEntry136 trace_thread_index_t GetKey() const { return index; } GetHashThreadTableEntry137 static size_t GetHash(trace_thread_index_t key) { return key; } 138 }; 139 140 struct ProviderInfo : public fbl::SinglyLinkedListable<fbl::unique_ptr<ProviderInfo>> { 141 ProviderId id; 142 fbl::String name; 143 144 // TODO(ZX-1056): It would be more efficient to use something like 145 // std::unordered_map<> here. In particular, the table entries are 146 // small enough that it doesn't make sense to heap allocate them 147 // individually. 148 fbl::HashTable<trace_string_index_t, fbl::unique_ptr<StringTableEntry>> string_table; 149 fbl::HashTable<trace_thread_index_t, fbl::unique_ptr<ThreadTableEntry>> thread_table; 150 151 // Used by the hash table. GetKeyProviderInfo152 ProviderId GetKey() const { return id; } GetHashProviderInfo153 static size_t GetHash(ProviderId key) { return key; } 154 }; 155 156 fbl::HashTable<ProviderId, fbl::unique_ptr<ProviderInfo>> providers_; 157 ProviderInfo* current_provider_ = nullptr; 158 159 DISALLOW_COPY_ASSIGN_AND_MOVE(TraceReader); 160 }; 161 162 // Provides support for reading sequences of 64-bit words from a contiguous 163 // region of memory. The main use-case of this class is input to |TraceReader|. 164 class Chunk final { 165 public: 166 Chunk(); 167 explicit Chunk(const uint64_t* begin, size_t num_words); 168 current_byte_offset()169 uint64_t current_byte_offset() const { 170 return (reinterpret_cast<const uint8_t*>(current_) - 171 reinterpret_cast<const uint8_t*>(begin_)); 172 } remaining_words()173 uint64_t remaining_words() const { return end_ - current_; } 174 175 // Reads from the chunk, maintaining proper alignment. 176 // Returns true on success, false if the chunk has insufficient remaining 177 // words to satisfy the request. 178 bool ReadUint64(uint64_t* out_value); 179 bool ReadInt64(int64_t* out_value); 180 bool ReadDouble(double* out_value); 181 bool ReadString(size_t length, fbl::StringPiece* out_string); 182 bool ReadChunk(size_t num_words, Chunk* out_chunk); 183 bool ReadInPlace(size_t num_words, const void** out_ptr); 184 185 private: 186 const uint64_t* begin_; 187 const uint64_t* current_; 188 const uint64_t* end_; 189 }; 190 191 } // namespace trace 192 193 #endif // TRACE_READER_READER_H_ 194