223 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- llvm/unittest/XRay/FDRProducerConsumerTest.cpp -----------*- C++ -*-===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // Test for round-trip record writing and reading.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| #include "llvm/Support/DataExtractor.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include "llvm/XRay/FDRLogBuilder.h"
 | |
| #include "llvm/XRay/FDRRecordConsumer.h"
 | |
| #include "llvm/XRay/FDRRecordProducer.h"
 | |
| #include "llvm/XRay/FDRRecords.h"
 | |
| #include "llvm/XRay/FDRTraceWriter.h"
 | |
| #include "llvm/XRay/FileHeaderReader.h"
 | |
| #include "gmock/gmock.h"
 | |
| #include "gtest/gtest.h"
 | |
| #include <string>
 | |
| #include <tuple>
 | |
| 
 | |
| namespace llvm {
 | |
| namespace xray {
 | |
| namespace {
 | |
| 
 | |
| using ::testing::Eq;
 | |
| using ::testing::IsEmpty;
 | |
| using ::testing::Not;
 | |
| using ::testing::SizeIs;
 | |
| 
 | |
| template <class RecordType> std::unique_ptr<Record> MakeRecord();
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() {
 | |
|   return std::make_unique<NewBufferRecord>(1);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() {
 | |
|   return std::make_unique<NewCPUIDRecord>(1, 2);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() {
 | |
|   return std::make_unique<TSCWrapRecord>(1);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() {
 | |
|   return std::make_unique<WallclockRecord>(1, 2);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() {
 | |
|   return std::make_unique<CustomEventRecord>(4, 1, 2, "data");
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() {
 | |
|   return std::make_unique<CallArgRecord>(1);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() {
 | |
|   return std::make_unique<PIDRecord>(1);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() {
 | |
|   return std::make_unique<FunctionRecord>(RecordTypes::ENTER, 1, 2);
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() {
 | |
|   return std::make_unique<CustomEventRecordV5>(4, 1, "data");
 | |
| }
 | |
| 
 | |
| template <> std::unique_ptr<Record> MakeRecord<TypedEventRecord>() {
 | |
|   return std::make_unique<TypedEventRecord>(4, 1, 2, "data");
 | |
| }
 | |
| 
 | |
| template <class T> class RoundTripTest : public ::testing::Test {
 | |
| public:
 | |
|   RoundTripTest() : Data(), OS(Data) {
 | |
|     H.Version = 4;
 | |
|     H.Type = 1;
 | |
|     H.ConstantTSC = true;
 | |
|     H.NonstopTSC = true;
 | |
|     H.CycleFrequency = 3e9;
 | |
| 
 | |
|     Writer = std::make_unique<FDRTraceWriter>(OS, H);
 | |
|     Rec = MakeRecord<T>();
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   std::string Data;
 | |
|   raw_string_ostream OS;
 | |
|   XRayFileHeader H;
 | |
|   std::unique_ptr<FDRTraceWriter> Writer;
 | |
|   std::unique_ptr<Record> Rec;
 | |
| };
 | |
| 
 | |
| TYPED_TEST_CASE_P(RoundTripTest);
 | |
| 
 | |
| template <class T> class RoundTripTestV5 : public ::testing::Test {
 | |
| public:
 | |
|   RoundTripTestV5() : Data(), OS(Data) {
 | |
|     H.Version = 5;
 | |
|     H.Type = 1;
 | |
|     H.ConstantTSC = true;
 | |
|     H.NonstopTSC = true;
 | |
|     H.CycleFrequency = 3e9;
 | |
| 
 | |
|     Writer = std::make_unique<FDRTraceWriter>(OS, H);
 | |
|     Rec = MakeRecord<T>();
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   std::string Data;
 | |
|   raw_string_ostream OS;
 | |
|   XRayFileHeader H;
 | |
|   std::unique_ptr<FDRTraceWriter> Writer;
 | |
|   std::unique_ptr<Record> Rec;
 | |
| };
 | |
| 
 | |
| TYPED_TEST_CASE_P(RoundTripTestV5);
 | |
| 
 | |
| // This test ensures that the writing and reading implementations are in sync --
 | |
| // that given write(read(write(R))) == R.
 | |
| TYPED_TEST_P(RoundTripTest, RoundTripsSingleValue) {
 | |
|   // Always write a buffer extents record which will cover the correct size of
 | |
|   // the record, for version 3 and up.
 | |
|   BufferExtents BE(200);
 | |
|   ASSERT_FALSE(errorToBool(BE.apply(*this->Writer)));
 | |
|   auto &R = this->Rec;
 | |
|   ASSERT_FALSE(errorToBool(R->apply(*this->Writer)));
 | |
|   this->OS.flush();
 | |
| 
 | |
|   DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);
 | |
|   uint64_t OffsetPtr = 0;
 | |
|   auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr);
 | |
|   if (!HeaderOrErr)
 | |
|     FAIL() << HeaderOrErr.takeError();
 | |
| 
 | |
|   FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);
 | |
|   std::vector<std::unique_ptr<Record>> Records;
 | |
|   LogBuilderConsumer C(Records);
 | |
|   while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {
 | |
|     auto R = P.produce();
 | |
|     if (!R)
 | |
|       FAIL() << R.takeError();
 | |
|     if (auto E = C.consume(std::move(R.get())))
 | |
|       FAIL() << E;
 | |
|   }
 | |
|   ASSERT_THAT(Records, Not(IsEmpty()));
 | |
|   std::string Data2;
 | |
|   raw_string_ostream OS2(Data2);
 | |
|   FDRTraceWriter Writer2(OS2, this->H);
 | |
|   for (auto &P : Records)
 | |
|     ASSERT_FALSE(errorToBool(P->apply(Writer2)));
 | |
|   OS2.flush();
 | |
| 
 | |
|   EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),
 | |
|             this->Data.substr(sizeof(XRayFileHeader)));
 | |
|   ASSERT_THAT(Records, SizeIs(2));
 | |
|   EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));
 | |
| }
 | |
| 
 | |
| REGISTER_TYPED_TEST_CASE_P(RoundTripTest, RoundTripsSingleValue);
 | |
| 
 | |
| // We duplicate the above case for the V5 version using different types and
 | |
| // encodings.
 | |
| TYPED_TEST_P(RoundTripTestV5, RoundTripsSingleValue) {
 | |
|   BufferExtents BE(200);
 | |
|   ASSERT_FALSE(errorToBool(BE.apply(*this->Writer)));
 | |
|   auto &R = this->Rec;
 | |
|   ASSERT_FALSE(errorToBool(R->apply(*this->Writer)));
 | |
|   this->OS.flush();
 | |
| 
 | |
|   DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);
 | |
|   uint64_t OffsetPtr = 0;
 | |
|   auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr);
 | |
|   if (!HeaderOrErr)
 | |
|     FAIL() << HeaderOrErr.takeError();
 | |
| 
 | |
|   FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);
 | |
|   std::vector<std::unique_ptr<Record>> Records;
 | |
|   LogBuilderConsumer C(Records);
 | |
|   while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {
 | |
|     auto R = P.produce();
 | |
|     if (!R)
 | |
|       FAIL() << R.takeError();
 | |
|     if (auto E = C.consume(std::move(R.get())))
 | |
|       FAIL() << E;
 | |
|   }
 | |
|   ASSERT_THAT(Records, Not(IsEmpty()));
 | |
|   std::string Data2;
 | |
|   raw_string_ostream OS2(Data2);
 | |
|   FDRTraceWriter Writer2(OS2, this->H);
 | |
|   for (auto &P : Records)
 | |
|     ASSERT_FALSE(errorToBool(P->apply(Writer2)));
 | |
|   OS2.flush();
 | |
| 
 | |
|   EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),
 | |
|             this->Data.substr(sizeof(XRayFileHeader)));
 | |
|   ASSERT_THAT(Records, SizeIs(2));
 | |
|   EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));
 | |
| }
 | |
| 
 | |
| REGISTER_TYPED_TEST_CASE_P(RoundTripTestV5, RoundTripsSingleValue);
 | |
| 
 | |
| // These are the record types we support for v4 and below.
 | |
| using RecordTypes =
 | |
|     ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,
 | |
|                      WallclockRecord, CustomEventRecord, CallArgRecord,
 | |
|                      PIDRecord, FunctionRecord>;
 | |
| INSTANTIATE_TYPED_TEST_CASE_P(Records, RoundTripTest, RecordTypes);
 | |
| 
 | |
| // For V5, we have two new types we're supporting.
 | |
| using RecordTypesV5 =
 | |
|     ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,
 | |
|                      WallclockRecord, CustomEventRecordV5, TypedEventRecord,
 | |
|                      CallArgRecord, PIDRecord, FunctionRecord>;
 | |
| INSTANTIATE_TYPED_TEST_CASE_P(Records, RoundTripTestV5, RecordTypesV5);
 | |
| 
 | |
| } // namespace
 | |
| } // namespace xray
 | |
| } // namespace llvm
 |