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#include <trace-reader/reader.h>
6
7#include <inttypes.h>
8
9#include <fbl/string_printf.h>
10#include <trace-engine/fields.h>
11
12namespace trace {
13
14TraceReader::TraceReader(RecordConsumer record_consumer,
15                         ErrorHandler error_handler)
16    : record_consumer_(fbl::move(record_consumer)),
17      error_handler_(fbl::move(error_handler)) {
18    // Provider ids begin at 1. We don't have a provider yet but we want to
19    // set the current provider. So set it to non-existent provider 0.
20    RegisterProvider(0u, "");
21}
22
23bool TraceReader::ReadRecords(Chunk& chunk) {
24    while (true) {
25        if (!pending_header_ && !chunk.ReadUint64(&pending_header_))
26            return true; // need more data
27
28        auto size = RecordFields::RecordSize::Get<size_t>(pending_header_);
29        if (size == 0) {
30            ReportError("Unexpected record of size 0");
31            return false; // fatal error
32        }
33        ZX_DEBUG_ASSERT(size <= RecordFields::kMaxRecordSizeWords);
34
35        Chunk record;
36        if (!chunk.ReadChunk(size - 1, &record))
37            return true; // need more data to decode record
38
39        auto type = RecordFields::Type::Get<RecordType>(pending_header_);
40        switch (type) {
41        case RecordType::kMetadata: {
42            if (!ReadMetadataRecord(record, pending_header_)) {
43                ReportError("Failed to read metadata record");
44            }
45            break;
46        }
47        case RecordType::kInitialization: {
48            if (!ReadInitializationRecord(record, pending_header_)) {
49                ReportError("Failed to read initialization record");
50            }
51            break;
52        }
53        case RecordType::kString: {
54            if (!ReadStringRecord(record, pending_header_)) {
55                ReportError("Failed to read string record");
56            }
57            break;
58        }
59        case RecordType::kThread: {
60            if (!ReadThreadRecord(record, pending_header_)) {
61                ReportError("Failed to read thread record");
62            }
63            break;
64        }
65        case RecordType::kEvent: {
66            if (!ReadEventRecord(record, pending_header_)) {
67                ReportError("Failed to read event record");
68            }
69            break;
70        }
71        case RecordType::kBlob: {
72            void* ptr;
73            if (!ReadBlobRecord(record, pending_header_, &ptr)) {
74                ReportError("Failed to read blob record");
75            }
76            break;
77        }
78        case RecordType::kKernelObject: {
79            if (!ReadKernelObjectRecord(record, pending_header_)) {
80                ReportError("Failed to read kernel object record");
81            }
82            break;
83        }
84        case RecordType::kContextSwitch: {
85            if (!ReadContextSwitchRecord(record, pending_header_)) {
86                ReportError("Failed to read context switch record");
87            }
88            break;
89        }
90        case RecordType::kLog: {
91            if (!ReadLogRecord(record, pending_header_)) {
92                ReportError("Failed to read log record");
93            }
94            break;
95        }
96        default: {
97            // Ignore unknown record types for forward compatibility.
98            ReportError(fbl::StringPrintf(
99                "Skipping record of unknown type %d", static_cast<uint32_t>(type)));
100            break;
101        }
102        }
103        pending_header_ = 0u;
104    }
105}
106
107bool TraceReader::ReadMetadataRecord(Chunk& record, RecordHeader header) {
108    auto type = MetadataRecordFields::MetadataType::Get<MetadataType>(header);
109
110    switch (type) {
111    case MetadataType::kProviderInfo: {
112        auto id = ProviderInfoMetadataRecordFields::Id::Get<ProviderId>(header);
113        auto name_length =
114            ProviderInfoMetadataRecordFields::NameLength::Get<size_t>(header);
115        fbl::StringPiece name_view;
116        if (!record.ReadString(name_length, &name_view))
117            return false;
118        fbl::String name(name_view);
119
120        RegisterProvider(id, name);
121        record_consumer_(Record(Record::Metadata{
122            MetadataContent(MetadataContent::ProviderInfo{id, name})}));
123        break;
124    }
125    case MetadataType::kProviderSection: {
126        auto id =
127            ProviderSectionMetadataRecordFields::Id::Get<ProviderId>(header);
128
129        SetCurrentProvider(id);
130        record_consumer_(Record(
131            Record::Metadata{MetadataContent(MetadataContent::ProviderSection{id})}));
132        break;
133    }
134    case MetadataType::kProviderEvent: {
135        auto id =
136            ProviderEventMetadataRecordFields::Id::Get<ProviderId>(header);
137        auto event = ProviderEventMetadataRecordFields::Event::Get<ProviderEventType>(header);
138        switch (event) {
139        case ProviderEventType::kBufferOverflow:
140            record_consumer_(Record(
141                Record::Metadata{MetadataContent(
142                    MetadataContent::ProviderEvent{id, event})}));
143            break;
144        default:
145            // Ignore unknown event types for forward compatibility.
146            ReportError(fbl::StringPrintf(
147                "Skipping provider event of unknown type %u",
148                static_cast<unsigned>(event)));
149            break;
150        }
151        break;
152    }
153    default: {
154        // Ignore unknown metadata types for forward compatibility.
155        ReportError(fbl::StringPrintf(
156            "Skipping metadata of unknown type %d", static_cast<uint32_t>(type)));
157        break;
158    }
159    }
160    return true;
161}
162
163bool TraceReader::ReadInitializationRecord(Chunk& record, RecordHeader header) {
164    trace_ticks_t ticks_per_second;
165    if (!record.ReadUint64(&ticks_per_second) || !ticks_per_second)
166        return false;
167
168    record_consumer_(Record(Record::Initialization{ticks_per_second}));
169    return true;
170}
171
172bool TraceReader::ReadStringRecord(Chunk& record, RecordHeader header) {
173    auto index = StringRecordFields::StringIndex::Get<trace_string_index_t>(header);
174    if (index < TRACE_ENCODED_STRING_REF_MIN_INDEX ||
175        index > TRACE_ENCODED_STRING_REF_MAX_INDEX) {
176        ReportError("Invalid string index");
177        return false;
178    }
179
180    auto length = StringRecordFields::StringLength::Get<size_t>(header);
181    fbl::StringPiece string_view;
182    if (!record.ReadString(length, &string_view))
183        return false;
184    fbl::String string(string_view);
185
186    RegisterString(index, string);
187    record_consumer_(Record(Record::String{index, fbl::move(string)}));
188    return true;
189}
190
191bool TraceReader::ReadThreadRecord(Chunk& record, RecordHeader header) {
192    auto index = ThreadRecordFields::ThreadIndex::Get<trace_thread_index_t>(header);
193    if (index < TRACE_ENCODED_THREAD_REF_MIN_INDEX ||
194        index > TRACE_ENCODED_THREAD_REF_MAX_INDEX) {
195        ReportError("Invalid thread index");
196        return false;
197    }
198
199    zx_koid_t process_koid, thread_koid;
200    if (!record.ReadUint64(&process_koid) ||
201        !record.ReadUint64(&thread_koid))
202        return false;
203
204    ProcessThread process_thread(process_koid, thread_koid);
205    RegisterThread(index, process_thread);
206    record_consumer_(Record(Record::Thread{index, process_thread}));
207    return true;
208}
209
210bool TraceReader::ReadEventRecord(Chunk& record, RecordHeader header) {
211    auto type = EventRecordFields::EventType::Get<EventType>(header);
212    auto argument_count = EventRecordFields::ArgumentCount::Get<size_t>(header);
213    auto thread_ref = EventRecordFields::ThreadRef::Get<trace_encoded_thread_ref_t>(header);
214    auto category_ref =
215        EventRecordFields::CategoryStringRef::Get<trace_encoded_string_ref_t>(header);
216    auto name_ref =
217        EventRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
218
219    trace_ticks_t timestamp;
220    ProcessThread process_thread;
221    fbl::String category;
222    fbl::String name;
223    fbl::Vector<Argument> arguments;
224    if (!record.ReadUint64(&timestamp) ||
225        !DecodeThreadRef(record, thread_ref, &process_thread) ||
226        !DecodeStringRef(record, category_ref, &category) ||
227        !DecodeStringRef(record, name_ref, &name) ||
228        !ReadArguments(record, argument_count, &arguments))
229        return false;
230
231    switch (type) {
232    case EventType::kInstant: {
233        uint64_t scope;
234        if (!record.ReadUint64(&scope))
235            return false;
236        record_consumer_(Record(Record::Event{
237            timestamp, process_thread, fbl::move(category), fbl::move(name),
238            fbl::move(arguments),
239            EventData(EventData::Instant{static_cast<EventScope>(scope)})}));
240        break;
241    }
242    case EventType::kCounter: {
243        trace_counter_id_t id;
244        if (!record.ReadUint64(&id))
245            return false;
246        record_consumer_(Record(Record::Event{
247            timestamp, process_thread, fbl::move(category), fbl::move(name),
248            fbl::move(arguments), EventData(EventData::Counter{id})}));
249        break;
250    }
251    case EventType::kDurationBegin: {
252        record_consumer_(Record(Record::Event{
253            timestamp, process_thread, fbl::move(category), fbl::move(name),
254            fbl::move(arguments), EventData(EventData::DurationBegin{})}));
255        break;
256    }
257    case EventType::kDurationEnd: {
258        record_consumer_(Record(Record::Event{
259            timestamp, process_thread, fbl::move(category), fbl::move(name),
260            fbl::move(arguments), EventData(EventData::DurationEnd{})}));
261        break;
262    }
263    case EventType::kAsyncBegin: {
264        trace_async_id_t id;
265        if (!record.ReadUint64(&id))
266            return false;
267        record_consumer_(Record(Record::Event{
268            timestamp, process_thread, fbl::move(category), fbl::move(name),
269            fbl::move(arguments), EventData(EventData::AsyncBegin{id})}));
270        break;
271    }
272    case EventType::kAsyncInstant: {
273        trace_async_id_t id;
274        if (!record.ReadUint64(&id))
275            return false;
276        record_consumer_(Record(Record::Event{
277            timestamp, process_thread, fbl::move(category), fbl::move(name),
278            fbl::move(arguments), EventData(EventData::AsyncInstant{id})}));
279        break;
280    }
281    case EventType::kAsyncEnd: {
282        trace_async_id_t id;
283        if (!record.ReadUint64(&id))
284            return false;
285        record_consumer_(Record(Record::Event{
286            timestamp, process_thread, fbl::move(category), fbl::move(name),
287            fbl::move(arguments), EventData(EventData::AsyncEnd{id})}));
288        break;
289    }
290    case EventType::kFlowBegin: {
291        trace_flow_id_t id;
292        if (!record.ReadUint64(&id))
293            return false;
294        record_consumer_(Record(Record::Event{
295            timestamp, process_thread, fbl::move(category), fbl::move(name),
296            fbl::move(arguments), EventData(EventData::FlowBegin{id})}));
297        break;
298    }
299    case EventType::kFlowStep: {
300        trace_flow_id_t id;
301        if (!record.ReadUint64(&id))
302            return false;
303        record_consumer_(Record(Record::Event{
304            timestamp, process_thread, fbl::move(category), fbl::move(name),
305            fbl::move(arguments), EventData(EventData::FlowStep{id})}));
306        break;
307    }
308    case EventType::kFlowEnd: {
309        trace_flow_id_t id;
310        if (!record.ReadUint64(&id))
311            return false;
312        record_consumer_(Record(Record::Event{
313            timestamp, process_thread, fbl::move(category), fbl::move(name),
314            fbl::move(arguments), EventData(EventData::FlowEnd{id})}));
315        break;
316    }
317    default: {
318        // Ignore unknown event types for forward compatibility.
319        ReportError(fbl::StringPrintf(
320            "Skipping event of unknown type %d", static_cast<uint32_t>(type)));
321        break;
322    }
323    }
324    return true;
325}
326
327bool TraceReader::ReadBlobRecord(Chunk& record, RecordHeader header, void** out_blob) {
328    auto blob_type =
329        BlobRecordFields::BlobType::Get<trace_blob_type_t>(header);
330    auto name_ref =
331        BlobRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
332    auto blob_size =
333        BlobRecordFields::BlobSize::Get<size_t>(header);
334    fbl::String name;
335    if (!DecodeStringRef(record, name_ref, &name))
336        return false;
337    const void* blob;
338    auto blob_words = BytesToWords(blob_size);
339    if (!record.ReadInPlace(blob_words, &blob))
340        return false;
341
342    record_consumer_(
343        Record(Record::Blob{blob_type, name, blob, blob_size}));
344    return true;
345}
346
347bool TraceReader::ReadKernelObjectRecord(Chunk& record, RecordHeader header) {
348    auto object_type =
349        KernelObjectRecordFields::ObjectType::Get<zx_obj_type_t>(header);
350    auto name_ref =
351        KernelObjectRecordFields::NameStringRef::Get<trace_encoded_string_ref_t>(header);
352    auto argument_count =
353        KernelObjectRecordFields::ArgumentCount::Get<size_t>(header);
354
355    zx_koid_t koid;
356    fbl::String name;
357    fbl::Vector<Argument> arguments;
358    if (!record.ReadUint64(&koid) ||
359        !DecodeStringRef(record, name_ref, &name) ||
360        !ReadArguments(record, argument_count, &arguments))
361        return false;
362
363    record_consumer_(
364        Record(Record::KernelObject{koid, object_type, name, fbl::move(arguments)}));
365    return true;
366}
367
368bool TraceReader::ReadContextSwitchRecord(Chunk& record, RecordHeader header) {
369    auto cpu_number =
370        ContextSwitchRecordFields::CpuNumber::Get<trace_cpu_number_t>(header);
371    auto outgoing_thread_state =
372        ContextSwitchRecordFields::OutgoingThreadState::Get<ThreadState>(header);
373    auto outgoing_thread_priority =
374        ContextSwitchRecordFields::OutgoingThreadPriority::Get<trace_thread_priority_t>(header);
375    auto incoming_thread_priority =
376        ContextSwitchRecordFields::IncomingThreadPriority::Get<trace_thread_priority_t>(header);
377    auto outgoing_thread_ref =
378        ContextSwitchRecordFields::OutgoingThreadRef::Get<trace_encoded_thread_ref_t>(
379            header);
380    auto incoming_thread_ref =
381        ContextSwitchRecordFields::IncomingThreadRef::Get<trace_encoded_thread_ref_t>(
382            header);
383
384    trace_ticks_t timestamp;
385    ProcessThread outgoing_thread;
386    ProcessThread incoming_thread;
387    if (!record.ReadUint64(&timestamp) ||
388        !DecodeThreadRef(record, outgoing_thread_ref,
389                         &outgoing_thread) ||
390        !DecodeThreadRef(record, incoming_thread_ref, &incoming_thread))
391        return false;
392
393    record_consumer_(
394        Record(Record::ContextSwitch{timestamp, cpu_number, outgoing_thread_state,
395                                     outgoing_thread, incoming_thread,
396                                     outgoing_thread_priority, incoming_thread_priority}));
397    return true;
398}
399
400bool TraceReader::ReadLogRecord(Chunk& record, RecordHeader header) {
401    auto log_message_length =
402        LogRecordFields::LogMessageLength::Get<uint16_t>(header);
403
404    if (log_message_length > LogRecordFields::kMaxMessageLength)
405        return false;
406
407    auto thread_ref = LogRecordFields::ThreadRef::Get<trace_encoded_thread_ref_t>(header);
408    trace_ticks_t timestamp;
409    ProcessThread process_thread;
410    fbl::StringPiece log_message;
411    if (!record.ReadUint64(&timestamp) ||
412        !DecodeThreadRef(record, thread_ref, &process_thread) ||
413        !record.ReadString(log_message_length, &log_message))
414        return false;
415    record_consumer_(
416        Record(Record::Log{timestamp, process_thread, fbl::String(log_message)}));
417    return true;
418}
419
420bool TraceReader::ReadArguments(Chunk& record,
421                                size_t count,
422                                fbl::Vector<Argument>* out_arguments) {
423    while (count-- > 0) {
424        ArgumentHeader header;
425        if (!record.ReadUint64(&header)) {
426            ReportError("Failed to read argument header");
427            return false;
428        }
429
430        auto size = ArgumentFields::ArgumentSize::Get<size_t>(header);
431        Chunk arg;
432        if (!size || !record.ReadChunk(size - 1, &arg)) {
433            ReportError("Invalid argument size");
434            return false;
435        }
436
437        auto name_ref = ArgumentFields::NameRef::Get<trace_encoded_string_ref_t>(header);
438        fbl::String name;
439        if (!DecodeStringRef(arg, name_ref, &name)) {
440            ReportError("Failed to read argument name");
441            return false;
442        }
443
444        auto type = ArgumentFields::Type::Get<ArgumentType>(header);
445        switch (type) {
446        case ArgumentType::kNull: {
447            out_arguments->push_back(Argument{fbl::move(name),
448                                              ArgumentValue::MakeNull()});
449            break;
450        }
451        case ArgumentType::kInt32: {
452            auto value = Int32ArgumentFields::Value::Get<int32_t>(header);
453            out_arguments->push_back(Argument{fbl::move(name),
454                                              ArgumentValue::MakeInt32(value)});
455            break;
456        }
457        case ArgumentType::kUint32: {
458            auto value = Uint32ArgumentFields::Value::Get<uint32_t>(header);
459            out_arguments->push_back(Argument{fbl::move(name),
460                                              ArgumentValue::MakeUint32(value)});
461            break;
462        }
463        case ArgumentType::kInt64: {
464            int64_t value;
465            if (!arg.ReadInt64(&value)) {
466                ReportError("Failed to read int64 argument value");
467                return false;
468            }
469            out_arguments->push_back(Argument{fbl::move(name),
470                                              ArgumentValue::MakeInt64(value)});
471            break;
472        }
473        case ArgumentType::kUint64: {
474            uint64_t value;
475            if (!arg.ReadUint64(&value)) {
476                ReportError("Failed to read uint64 argument value");
477                return false;
478            }
479            out_arguments->push_back(Argument{fbl::move(name),
480                                              ArgumentValue::MakeUint64(value)});
481            break;
482        }
483        case ArgumentType::kDouble: {
484            double value;
485            if (!arg.ReadDouble(&value)) {
486                ReportError("Failed to read double argument value");
487                return false;
488            }
489            out_arguments->push_back(Argument{fbl::move(name),
490                                              ArgumentValue::MakeDouble(value)});
491            break;
492        }
493        case ArgumentType::kString: {
494            auto string_ref =
495                StringArgumentFields::Index::Get<trace_encoded_string_ref_t>(header);
496            fbl::String value;
497            if (!DecodeStringRef(arg, string_ref, &value)) {
498                ReportError("Failed to read string argument value");
499                return false;
500            }
501            out_arguments->push_back(
502                Argument{fbl::move(name),
503                         ArgumentValue::MakeString(fbl::move(value))});
504            break;
505        }
506        case ArgumentType::kPointer: {
507            uint64_t value;
508            if (!arg.ReadUint64(&value)) {
509                ReportError("Failed to read pointer argument value");
510                return false;
511            }
512            out_arguments->push_back(Argument{fbl::move(name),
513                                              ArgumentValue::MakePointer(value)});
514            break;
515        }
516        case ArgumentType::kKoid: {
517            zx_koid_t value;
518            if (!arg.ReadUint64(&value)) {
519                ReportError("Failed to read koid argument value");
520                return false;
521            }
522            out_arguments->push_back(Argument{fbl::move(name),
523                                              ArgumentValue::MakeKoid(value)});
524            break;
525        }
526        default: {
527            // Ignore unknown argument types for forward compatibility.
528            ReportError(fbl::StringPrintf(
529                "Skipping argument of unknown type %d, argument name %s",
530                static_cast<uint32_t>(type), name.c_str()));
531            break;
532        }
533        }
534    }
535    return true;
536}
537
538fbl::String TraceReader::GetProviderName(ProviderId id) const {
539    auto it = providers_.find(id);
540    if (it != providers_.end())
541        return it->name;
542    return fbl::String();
543}
544
545void TraceReader::SetCurrentProvider(ProviderId id) {
546    auto it = providers_.find(id);
547    if (it != providers_.end()) {
548        current_provider_ = &*it;
549        return;
550    }
551    ReportError(fbl::StringPrintf("Registering non-existent provider %u\n", id));
552    RegisterProvider(id, "");
553}
554
555void TraceReader::RegisterProvider(ProviderId id, fbl::String name) {
556    auto provider = fbl::make_unique<ProviderInfo>();
557    provider->id = id;
558    provider->name = name;
559    current_provider_ = provider.get();
560
561    providers_.insert_or_replace(fbl::move(provider));
562}
563
564void TraceReader::RegisterString(trace_string_index_t index, fbl::String string) {
565    ZX_DEBUG_ASSERT(index >= TRACE_ENCODED_STRING_REF_MIN_INDEX &&
566                    index <= TRACE_ENCODED_STRING_REF_MAX_INDEX);
567
568    auto entry = fbl::make_unique<StringTableEntry>(index, string);
569    current_provider_->string_table.insert_or_replace(fbl::move(entry));
570}
571
572void TraceReader::RegisterThread(trace_thread_index_t index,
573                                 const ProcessThread& process_thread) {
574    ZX_DEBUG_ASSERT(index >= TRACE_ENCODED_THREAD_REF_MIN_INDEX &&
575                    index <= TRACE_ENCODED_THREAD_REF_MAX_INDEX);
576
577    auto entry = fbl::make_unique<ThreadTableEntry>(index, process_thread);
578    current_provider_->thread_table.insert_or_replace(fbl::move(entry));
579}
580
581bool TraceReader::DecodeStringRef(Chunk& chunk,
582                                  trace_encoded_string_ref_t string_ref,
583                                  fbl::String* out_string) const {
584    if (string_ref == TRACE_ENCODED_STRING_REF_EMPTY) {
585        out_string->clear();
586        return true;
587    }
588
589    if (string_ref & TRACE_ENCODED_STRING_REF_INLINE_FLAG) {
590        size_t length = string_ref & TRACE_ENCODED_STRING_REF_LENGTH_MASK;
591        fbl::StringPiece string_view;
592        if (length > TRACE_ENCODED_STRING_REF_MAX_LENGTH ||
593            !chunk.ReadString(length, &string_view)) {
594            ReportError("Could not read inline string");
595            return false;
596        }
597        *out_string = string_view;
598        return true;
599    }
600
601    auto it = current_provider_->string_table.find(string_ref);
602    if (it == current_provider_->string_table.end()) {
603        ReportError("String ref not in table");
604        return false;
605    }
606    *out_string = it->string;
607    return true;
608}
609
610bool TraceReader::DecodeThreadRef(Chunk& chunk,
611                                  trace_encoded_thread_ref_t thread_ref,
612                                  ProcessThread* out_process_thread) const {
613    if (thread_ref == TRACE_ENCODED_THREAD_REF_INLINE) {
614        zx_koid_t process_koid, thread_koid;
615        if (!chunk.ReadUint64(&process_koid) ||
616            !chunk.ReadUint64(&thread_koid)) {
617            ReportError("Could not read inline process and thread");
618            return false;
619        }
620        *out_process_thread = ProcessThread(process_koid, thread_koid);
621        return true;
622    }
623
624    auto it = current_provider_->thread_table.find(thread_ref);
625    if (it == current_provider_->thread_table.end()) {
626        ReportError(fbl::StringPrintf("Thread ref 0x%x not in table",
627                                      thread_ref));
628        return false;
629    }
630    *out_process_thread = it->process_thread;
631    return true;
632}
633
634void TraceReader::ReportError(fbl::String error) const {
635    if (error_handler_)
636        error_handler_(fbl::move(error));
637}
638
639Chunk::Chunk()
640    : begin_(nullptr), current_(nullptr), end_(nullptr) {}
641
642Chunk::Chunk(const uint64_t* begin, size_t num_words)
643    : begin_(begin), current_(begin), end_(begin_ + num_words) {}
644
645bool Chunk::ReadUint64(uint64_t* out_value) {
646    if (current_ < end_) {
647        *out_value = *current_++;
648        return true;
649    }
650    return false;
651}
652
653bool Chunk::ReadInt64(int64_t* out_value) {
654    if (current_ < end_) {
655        *out_value = *reinterpret_cast<const int64_t*>(current_++);
656        return true;
657    }
658    return false;
659}
660
661bool Chunk::ReadDouble(double* out_value) {
662    if (current_ < end_) {
663        *out_value = *reinterpret_cast<const double*>(current_++);
664        return true;
665    }
666    return false;
667}
668
669bool Chunk::ReadChunk(size_t num_words, Chunk* out_chunk) {
670    if (current_ + num_words > end_)
671        return false;
672
673    *out_chunk = Chunk(current_, num_words);
674    current_ += num_words;
675    return true;
676}
677
678bool Chunk::ReadString(size_t length, fbl::StringPiece* out_string) {
679    auto num_words = BytesToWords(length);
680    if (current_ + num_words > end_)
681        return false;
682
683    *out_string =
684        fbl::StringPiece(reinterpret_cast<const char*>(current_), length);
685    current_ += num_words;
686    return true;
687}
688
689bool Chunk::ReadInPlace(size_t num_words, const void** out_ptr) {
690    if (current_ + num_words > end_)
691        return false;
692    *out_ptr = current_;
693    current_ += num_words;
694    return true;
695}
696
697} // namespace trace
698