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(×tamp) || 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(×tamp) || 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(×tamp) || 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