diff --git a/zircon/system/ulib/trace-reader/BUILD.gn b/zircon/system/ulib/trace-reader/BUILD.gn index 1a1bdbe336c8073230a078026a7691921404c166..c4447a570eb8767286f292ad40a72a3c64b32b34 100644 --- a/zircon/system/ulib/trace-reader/BUILD.gn +++ b/zircon/system/ulib/trace-reader/BUILD.gn @@ -5,12 +5,14 @@ library("trace-reader") { sdk = "source" sdk_headers = [ + "trace-reader/file_reader.h", "trace-reader/reader.h", "trace-reader/reader_internal.h", "trace-reader/records.h", ] host = true sources = [ + "file_reader.cpp", "reader.cpp", "reader_internal.cpp", "records.cpp", @@ -30,6 +32,7 @@ library("trace-reader") { test("trace-reader-test") { sources = [ + "file_reader_tests.cpp", "reader_tests.cpp", "records_tests.cpp", ] diff --git a/zircon/system/ulib/trace-reader/file_reader.cpp b/zircon/system/ulib/trace-reader/file_reader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..664e07eef09237d8a60b89d23695d955b9388b72 --- /dev/null +++ b/zircon/system/ulib/trace-reader/file_reader.cpp @@ -0,0 +1,60 @@ +// Copyright 2019 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 <trace-reader/file_reader.h> + +namespace trace { + +// static +bool FileReader::Create(const char* file_path, + RecordConsumer record_consumer, + ErrorHandler error_handler, + std::unique_ptr<FileReader>* out_reader) { + ZX_DEBUG_ASSERT(out_reader != nullptr); + FILE* f = fopen(file_path, "rb"); + if (f == nullptr) { + return false; + } + + out_reader->reset(new FileReader(f, std::move(record_consumer), + std::move(error_handler))); + return true; +} + +FileReader::FileReader(FILE* file, RecordConsumer record_consumer, + ErrorHandler error_handler) + : TraceReader(std::move(record_consumer), + std::move(error_handler)), + file_(file) { +} + +void FileReader::ReadFile() { + for (;;) { + size_t to_read = buffer_.size() - buffer_end_; + size_t actual = fread(buffer_.data() + buffer_end_, 1u, to_read, file_); + + if (actual == 0) { + break; + } + + buffer_end_ += actual; + size_t bytes_available = buffer_end_; + + size_t bytes_consumed; + trace::Chunk chunk(reinterpret_cast<const uint64_t*>(buffer_.data()), + trace::BytesToWords(bytes_available)); + if (!ReadRecords(chunk)) { + ReportError("Trace stream is corrupted"); + break; + } + bytes_consumed = + bytes_available - trace::WordsToBytes(chunk.remaining_words()); + + bytes_available -= bytes_consumed; + memmove(buffer_.data(), buffer_.data() + bytes_consumed, bytes_available); + buffer_end_ = bytes_available; + } +} + +} // namespace trace diff --git a/zircon/system/ulib/trace-reader/file_reader_tests.cpp b/zircon/system/ulib/trace-reader/file_reader_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad0f87310eeb487ae0f6ca58e4b3d09541ffd042 --- /dev/null +++ b/zircon/system/ulib/trace-reader/file_reader_tests.cpp @@ -0,0 +1,67 @@ +// Copyright 2019 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 <trace-reader/file_reader.h> + +#include <memory> +#include <stdint.h> +#include <stdio.h> +#include <utility> + +#include <fbl/algorithm.h> +#include <fbl/vector.h> +#include <trace-engine/fields.h> +#include <trace-engine/types.h> +#include <zxtest/zxtest.h> + +#include "reader_tests.h" + +namespace trace { +namespace { + +const char kTestInputFile[] = "/tmp/trace-reader-test.fxt"; + +TEST(TraceFileReader, Records) { + FILE* f = fopen(kTestInputFile, "wb"); + ASSERT_NOT_NULL(f); + + constexpr zx_koid_t kProcessKoid = 42; + constexpr zx_koid_t kThreadKoid = 43; + constexpr trace_thread_index_t kThreadIndex = 99; + + uint64_t thread_record[3]{}; + ThreadRecordFields::Type::Set(thread_record[0], + static_cast<uint64_t>(RecordType::kThread)); + ThreadRecordFields::RecordSize::Set(thread_record[0], fbl::count_of(thread_record)); + ThreadRecordFields::ThreadIndex::Set(thread_record[0], kThreadIndex); + thread_record[1] = kProcessKoid; + thread_record[2] = kThreadKoid; + + ASSERT_EQ(fwrite(&thread_record[0], sizeof(thread_record[0]), + fbl::count_of(thread_record), f), + fbl::count_of(thread_record)); + ASSERT_EQ(fclose(f), 0); + + std::unique_ptr<trace::FileReader> reader; + fbl::Vector<trace::Record> records; + fbl::String error; + ASSERT_TRUE(trace::FileReader::Create( + kTestInputFile, test::MakeRecordConsumer(&records), + test::MakeErrorHandler(&error), &reader)); + + reader->ReadFile(); + EXPECT_TRUE(error.empty()); + ASSERT_EQ(records.size(), 1u); + const trace::Record& rec = records[0]; + EXPECT_EQ(rec.type(), RecordType::kThread); + const trace::Record::Thread& thread = rec.GetThread(); + EXPECT_EQ(thread.index, kThreadIndex); + EXPECT_EQ(thread.process_thread.process_koid(), kProcessKoid); + EXPECT_EQ(thread.process_thread.thread_koid(), kThreadKoid); +} + +// NOTE: Most of the reader is covered by the libtrace tests. + +} // namespace +} // namespace trace diff --git a/zircon/system/ulib/trace-reader/include/trace-reader/file_reader.h b/zircon/system/ulib/trace-reader/include/trace-reader/file_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..a696366634eab1c4b28f569195583334696ff815 --- /dev/null +++ b/zircon/system/ulib/trace-reader/include/trace-reader/file_reader.h @@ -0,0 +1,51 @@ +// Copyright 2019 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. + +#ifndef TRACE_READER_FILE_READER_H_ +#define TRACE_READER_FILE_READER_H_ + +#include <array> +#include <memory> +#include <stdio.h> + +#include <trace-engine/fields.h> +#include <trace-reader/reader.h> + +namespace trace { + +// Read records from a file, in fxt file format. + +class FileReader : public TraceReader { +public: + static bool Create(const char* file_path, + RecordConsumer record_consumer, + ErrorHandler error_handler, + std::unique_ptr<FileReader>* out_reader); + + void ReadFile(); + +private: + // Note: Buffer needs to be big enough to store records of maximum size. + static constexpr size_t kReadBufferSize = + trace::RecordFields::kMaxRecordSizeBytes * 4; + + explicit FileReader(FILE* file, RecordConsumer record_consumer, + ErrorHandler error_handler); + + FILE* const file_; + RecordConsumer const record_consumer_; + ErrorHandler const error_handler_; + + // We don't use a vector here to avoid the housekeeping necessary to keep + // checkers happy (e.g., asan). We use this buffer in an atypical way. + std::array<uint8_t, kReadBufferSize> buffer_; + // The amount of space in use in |buffer_|. + size_t buffer_end_ = 0u; + + DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FileReader); +}; + +} // namespace trace + +#endif // TRACE_READER_FILE_READER_H_ diff --git a/zircon/system/ulib/trace-reader/include/trace-reader/reader.h b/zircon/system/ulib/trace-reader/include/trace-reader/reader.h index 9143689304b1f769afdb7d50a9f2dd1c2b8ded7d..b33c3b06fcf241db8337aa744764ba1c9672c86d 100644 --- a/zircon/system/ulib/trace-reader/include/trace-reader/reader.h +++ b/zircon/system/ulib/trace-reader/include/trace-reader/reader.h @@ -70,6 +70,9 @@ public: const ErrorHandler& error_handler() const { return error_handler_; } +protected: + void ReportError(fbl::String error) const; + private: bool ReadMetadataRecord(Chunk& record, RecordHeader header); @@ -102,8 +105,6 @@ private: trace_encoded_thread_ref_t thread_ref, ProcessThread* out_process_thread) const; - void ReportError(fbl::String error) const; - RecordConsumer const record_consumer_; ErrorHandler const error_handler_; diff --git a/zircon/system/ulib/trace-reader/reader_tests.cpp b/zircon/system/ulib/trace-reader/reader_tests.cpp index f9af88b3728498e401c4297f015e0f5b2ffc4fd1..fa9bd8663f5728d9701b5d6d2bb982de5c00f297 100644 --- a/zircon/system/ulib/trace-reader/reader_tests.cpp +++ b/zircon/system/ulib/trace-reader/reader_tests.cpp @@ -12,27 +12,11 @@ #include <utility> +#include "reader_tests.h" + namespace trace { namespace { -template <typename T> -uint64_t ToWord(const T& value) { - return *reinterpret_cast<const uint64_t*>(&value); -} - -trace::TraceReader::RecordConsumer MakeRecordConsumer( - fbl::Vector<trace::Record>* out_records) { - return [out_records](trace::Record record) { - out_records->push_back(std::move(record)); - }; -} - -trace::TraceReader::ErrorHandler MakeErrorHandler(fbl::String* out_error) { - return [out_error](fbl::String error) { - *out_error = std::move(error); - }; -} - TEST(TraceReader, EmptyChunk) { uint64_t value; int64_t int64_value; @@ -70,11 +54,11 @@ TEST(TraceReader, NonEmptyChunk) { 0, UINT64_MAX, // int64 values - ToWord(INT64_MIN), - ToWord(INT64_MAX), + test::ToWord(INT64_MIN), + test::ToWord(INT64_MAX), // double values - ToWord(1.5), - ToWord(-3.14), + test::ToWord(1.5), + test::ToWord(-3.14), // string values (will be filled in) 0, 0, @@ -145,7 +129,8 @@ TEST(TraceReader, NonEmptyChunk) { TEST(TraceReader, InitialState) { fbl::Vector<trace::Record> records; fbl::String error; - trace::TraceReader reader(MakeRecordConsumer(&records), MakeErrorHandler(&error)); + trace::TraceReader reader( + test::MakeRecordConsumer(&records), test::MakeErrorHandler(&error)); EXPECT_EQ(0, reader.current_provider_id()); EXPECT_TRUE(reader.current_provider_name() == ""); @@ -157,7 +142,8 @@ TEST(TraceReader, InitialState) { TEST(TraceReader, EmptyBuffer) { fbl::Vector<trace::Record> records; fbl::String error; - trace::TraceReader reader(MakeRecordConsumer(&records), MakeErrorHandler(&error)); + trace::TraceReader reader( + test::MakeRecordConsumer(&records), test::MakeErrorHandler(&error)); trace::Chunk empty; EXPECT_TRUE(reader.ReadRecords(empty)); diff --git a/zircon/system/ulib/trace-reader/reader_tests.h b/zircon/system/ulib/trace-reader/reader_tests.h new file mode 100644 index 0000000000000000000000000000000000000000..b21ce8a9e4ac71e41cd42972b4dd7de00f75c0bc --- /dev/null +++ b/zircon/system/ulib/trace-reader/reader_tests.h @@ -0,0 +1,38 @@ +// Copyright 2019 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. + +#pragma once + +#include <trace-reader/reader.h> + +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> + +#include <zxtest/zxtest.h> + +namespace trace { +namespace test { + +template <typename T> +uint64_t ToWord(const T& value) { + return *reinterpret_cast<const uint64_t*>(&value); +} + +static inline trace::TraceReader::RecordConsumer MakeRecordConsumer( + fbl::Vector<trace::Record>* out_records) { + return [out_records](trace::Record record) { + out_records->push_back(std::move(record)); + }; +} + +static inline trace::TraceReader::ErrorHandler MakeErrorHandler(fbl::String* out_error) { + return [out_error](fbl::String error) { + *out_error = std::move(error); + }; +} + +} // namespace test +} // namespace trace