| // Copyright 2018 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. |
| |
| #include <inttypes.h> |
| |
| #include <iterator> |
| #include <memory> |
| #include <utility> |
| |
| #include <fbl/string_printf.h> |
| #include <trace-reader/reader_internal.h> |
| |
| namespace trace { |
| namespace internal { |
| |
| fbl::String BufferHeaderReader::Create(const void* header, size_t buffer_size, |
| std::unique_ptr<BufferHeaderReader>* out_reader) { |
| if (buffer_size < sizeof(trace_buffer_header)) { |
| return "buffer too small for header"; |
| } |
| auto hdr = reinterpret_cast<const trace_buffer_header*>(header); |
| auto error = Validate(*hdr, buffer_size); |
| if (error != "") { |
| return error; |
| } |
| *out_reader = std::unique_ptr<BufferHeaderReader>(new BufferHeaderReader(hdr)); |
| return ""; |
| } |
| |
| BufferHeaderReader::BufferHeaderReader(const trace_buffer_header* header) : header_(header) {} |
| |
| fbl::String BufferHeaderReader::Validate(const trace_buffer_header& header, size_t buffer_size) { |
| if (header.magic != TRACE_BUFFER_HEADER_MAGIC) { |
| return fbl::StringPrintf("bad magic: 0x%" PRIx64, header.magic); |
| } |
| if (header.version != TRACE_BUFFER_HEADER_V0) { |
| return fbl::StringPrintf("bad version: %u", header.version); |
| } |
| |
| if (buffer_size & 7u) { |
| return fbl::StringPrintf("buffer size not multiple of 64-bit words: 0x%zx", buffer_size); |
| } |
| |
| switch (header.buffering_mode) { |
| case TRACE_BUFFERING_MODE_ONESHOT: |
| case TRACE_BUFFERING_MODE_CIRCULAR: |
| case TRACE_BUFFERING_MODE_STREAMING: |
| break; |
| default: |
| return fbl::StringPrintf("bad buffering mode: %u", header.buffering_mode); |
| } |
| |
| if (header.total_size != buffer_size) { |
| return fbl::StringPrintf("bad total buffer size: 0x%" PRIx64, header.total_size); |
| } |
| |
| auto rolling_buffer_size = header.rolling_buffer_size; |
| auto durable_buffer_size = header.durable_buffer_size; |
| |
| if ((rolling_buffer_size & 7) != 0) { |
| return fbl::StringPrintf("bad rolling buffer size: 0x%" PRIx64, rolling_buffer_size); |
| } |
| if ((durable_buffer_size & 7) != 0) { |
| return fbl::StringPrintf("bad durable buffer size: 0x%" PRIx64, durable_buffer_size); |
| } |
| |
| if (header.buffering_mode == TRACE_BUFFERING_MODE_ONESHOT) { |
| if (rolling_buffer_size != buffer_size - sizeof(trace_buffer_header)) { |
| return fbl::StringPrintf("bad rolling buffer size: 0x%" PRIx64, rolling_buffer_size); |
| } |
| if (durable_buffer_size != 0) { |
| return fbl::StringPrintf("bad durable buffer size: 0x%" PRIx64, durable_buffer_size); |
| } |
| } else { |
| if (rolling_buffer_size >= buffer_size / 2) { |
| return fbl::StringPrintf("bad rolling buffer size: 0x%" PRIx64, rolling_buffer_size); |
| } |
| if (durable_buffer_size >= rolling_buffer_size) { |
| return fbl::StringPrintf("bad durable buffer size: 0x%" PRIx64, durable_buffer_size); |
| } |
| if ((sizeof(trace_buffer_header) + durable_buffer_size + 2 * rolling_buffer_size) != |
| buffer_size) { |
| return fbl::StringPrintf( |
| "buffer sizes don't add up:" |
| " 0x%" PRIx64 ", 0x%" PRIx64, |
| durable_buffer_size, rolling_buffer_size); |
| } |
| } |
| |
| for (size_t i = 0; i < std::size(header.rolling_data_end); ++i) { |
| auto data_end = header.rolling_data_end[i]; |
| if (data_end > rolling_buffer_size || (data_end & 7) != 0) { |
| return fbl::StringPrintf("bad data end for buffer %zu: 0x%" PRIx64, i, data_end); |
| } |
| } |
| |
| auto durable_data_end = header.durable_data_end; |
| if (durable_data_end > durable_buffer_size || (durable_data_end & 7) != 0) { |
| return fbl::StringPrintf("bad durable_data_end: 0x%" PRIx64, durable_data_end); |
| } |
| |
| return ""; |
| } |
| |
| TraceBufferReader::TraceBufferReader(ChunkConsumer chunk_consumer, ErrorHandler error_handler) |
| : chunk_consumer_(std::move(chunk_consumer)), error_handler_(std::move(error_handler)) {} |
| |
| bool TraceBufferReader::ReadChunks(const void* buffer, size_t buffer_size) { |
| std::unique_ptr<BufferHeaderReader> header; |
| auto error = BufferHeaderReader::Create(buffer, buffer_size, &header); |
| if (error != "") { |
| error_handler_(error); |
| return false; |
| } |
| |
| CallChunkConsumerIfNonEmpty(header->GetDurableBuffer(buffer), header->durable_data_end()); |
| |
| // There's only two buffers, thus the earlier one is not the current one. |
| // It's important to process them in chronological order on the off |
| // chance that the earlier buffer provides a stringref or threadref |
| // referenced by the later buffer. |
| int later_buffer = header->GetBufferNumber(header->wrapped_count()); |
| int earlier_buffer = 0; |
| if (header->wrapped_count() > 0) |
| earlier_buffer = header->GetBufferNumber(header->wrapped_count() - 1); |
| |
| if (earlier_buffer != later_buffer) { |
| CallChunkConsumerIfNonEmpty(header->GetRollingBuffer(buffer, earlier_buffer), |
| header->rolling_data_end(earlier_buffer)); |
| } |
| |
| CallChunkConsumerIfNonEmpty(header->GetRollingBuffer(buffer, later_buffer), |
| header->rolling_data_end(later_buffer)); |
| |
| return true; |
| } |
| |
| void TraceBufferReader::CallChunkConsumerIfNonEmpty(const void* ptr, size_t size) { |
| if (size != 0) { |
| auto word_size = sizeof(uint64_t); |
| ZX_DEBUG_ASSERT((reinterpret_cast<uintptr_t>(ptr) & (word_size - 1)) == 0); |
| ZX_DEBUG_ASSERT((size & (word_size - 1)) == 0); |
| Chunk chunk(reinterpret_cast<const uint64_t*>(ptr), size / word_size); |
| chunk_consumer_(chunk); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace trace |