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