From f5133da999d9758f8d301144970df26d82551df7 Mon Sep 17 00:00:00 2001 From: Dean Michael Berris Date: Thu, 6 Sep 2018 05:55:57 +0000 Subject: [PATCH] [XRay] Add a BlockIndexer visitor for FDR Records. Summary: This change adds a `BlockIndexer` type which maintains pointers to records that belong to the same process+thread pairs. The indexing happens with order of appearance of records as they are visited. This version of the indexer currently only supports FDR version 3 logs, which contain `BufferExtent` records. We will add support for v2 and v1 logs in follow-up patches. This is another part of D50441. Reviewers: eizan, kpw, mboerger Reviewed By: mboerger Subscribers: mboerger, mgorny, hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D51673 llvm-svn: 341518 --- include/llvm/XRay/BlockIndexer.h | 69 ++++++++++++++++++ lib/XRay/BlockIndexer.cpp | 97 ++++++++++++++++++++++++++ lib/XRay/CMakeLists.txt | 1 + unittests/XRay/CMakeLists.txt | 1 + unittests/XRay/FDRBlockIndexerTest.cpp | 83 ++++++++++++++++++++++ 5 files changed, 251 insertions(+) create mode 100644 include/llvm/XRay/BlockIndexer.h create mode 100644 lib/XRay/BlockIndexer.cpp create mode 100644 unittests/XRay/FDRBlockIndexerTest.cpp diff --git a/include/llvm/XRay/BlockIndexer.h b/include/llvm/XRay/BlockIndexer.h new file mode 100644 index 00000000000..07ee6a72a81 --- /dev/null +++ b/include/llvm/XRay/BlockIndexer.h @@ -0,0 +1,69 @@ +//===- BlockIndexer.h - FDR Block Indexing Visitor ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An implementation of the RecordVisitor which generates a mapping between a +// thread and a range of records representing a block. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIB_XRAY_BLOCKINDEXER_H_ +#define LLVM_LIB_XRAY_BLOCKINDEXER_H_ + +#include "llvm/ADT/DenseMap.h" +#include "llvm/XRay/FDRRecords.h" +#include +#include + +namespace llvm { +namespace xray { + +// The BlockIndexer will gather all related records associated with a +// process+thread and group them by 'Block'. +class BlockIndexer : public RecordVisitor { +public: + struct Block { + uint64_t ProcessID; + int32_t ThreadID; + std::vector Records; + }; + + // This maps the process + thread combination to a sequence of blocks. + using Index = DenseMap, std::vector>; + +private: + Index &Indices; + + enum class State : unsigned { SeekExtents, ExtentsFound, ThreadIDFound }; + + State CurrentState = State::SeekExtents; + Block CurrentBlock{0, 0, {}}; + +public: + explicit BlockIndexer(Index &I) : RecordVisitor(), Indices(I) {} + + Error visit(BufferExtents &) override; + Error visit(WallclockRecord &) override; + Error visit(NewCPUIDRecord &) override; + Error visit(TSCWrapRecord &) override; + Error visit(CustomEventRecord &) override; + Error visit(CallArgRecord &) override; + Error visit(PIDRecord &) override; + Error visit(NewBufferRecord &) override; + Error visit(EndBufferRecord &) override; + Error visit(FunctionRecord &) override; + + /// The flush() function will clear out the current state of the visitor, to + /// allow for explicitly flushing a block's records to the currently + /// recognized thread and process combination. + Error flush(); +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_LIB_XRAY_BLOCKINDEXER_H_ diff --git a/lib/XRay/BlockIndexer.cpp b/lib/XRay/BlockIndexer.cpp new file mode 100644 index 00000000000..4b76ad9ecdc --- /dev/null +++ b/lib/XRay/BlockIndexer.cpp @@ -0,0 +1,97 @@ +//===- BlockIndexer.cpp - FDR Block Indexing VIsitor ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An implementation of the RecordVisitor which generates a mapping between a +// thread and a range of records representing a block. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockIndexer.h" + +namespace llvm { +namespace xray { + +Error BlockIndexer::visit(BufferExtents &) { + if (CurrentState == State::ThreadIDFound) { + Index::iterator It; + std::tie(It, std::ignore) = + Indices.insert({{CurrentBlock.ProcessID, CurrentBlock.ThreadID}, {}}); + It->second.push_back({CurrentBlock.ProcessID, CurrentBlock.ThreadID, + std::move(CurrentBlock.Records)}); + CurrentBlock.ProcessID = 0; + CurrentBlock.ThreadID = 0; + CurrentBlock.Records = {}; + } + CurrentState = State::ExtentsFound; + return Error::success(); +} + +Error BlockIndexer::visit(WallclockRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(NewCPUIDRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(TSCWrapRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(CustomEventRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(CallArgRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +}; + +Error BlockIndexer::visit(PIDRecord &R) { + CurrentBlock.ProcessID = R.pid(); + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(NewBufferRecord &R) { + CurrentState = State::ThreadIDFound; + CurrentBlock.ThreadID = R.tid(); + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(EndBufferRecord &R) { + CurrentState = State::SeekExtents; + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(FunctionRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::flush() { + CurrentState = State::SeekExtents; + Index::iterator It; + std::tie(It, std::ignore) = + Indices.insert({{CurrentBlock.ProcessID, CurrentBlock.ThreadID}, {}}); + It->second.push_back({CurrentBlock.ProcessID, CurrentBlock.ThreadID, + std::move(CurrentBlock.Records)}); + CurrentBlock.ProcessID = 0; + CurrentBlock.ThreadID = 0; + CurrentBlock.Records = {}; + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/lib/XRay/CMakeLists.txt b/lib/XRay/CMakeLists.txt index 26e12ed73f3..ab6e0373c9e 100644 --- a/lib/XRay/CMakeLists.txt +++ b/lib/XRay/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_library(LLVMXRay + BlockIndexer.cpp FDRRecordProducer.cpp FDRRecords.cpp FDRTraceWriter.cpp diff --git a/unittests/XRay/CMakeLists.txt b/unittests/XRay/CMakeLists.txt index 37f7cb9fb3e..5f2453388f4 100644 --- a/unittests/XRay/CMakeLists.txt +++ b/unittests/XRay/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(XRayTests ProfileTest.cpp + FDRBlockIndexerTest.cpp FDRProducerConsumerTest.cpp FDRRecordPrinterTest.cpp FDRTraceWriterTest.cpp diff --git a/unittests/XRay/FDRBlockIndexerTest.cpp b/unittests/XRay/FDRBlockIndexerTest.cpp new file mode 100644 index 00000000000..6f9d3eed33f --- /dev/null +++ b/unittests/XRay/FDRBlockIndexerTest.cpp @@ -0,0 +1,83 @@ +//===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockIndexer.h" +#include "llvm/XRay/FDRLogBuilder.h" +#include "llvm/XRay/FDRRecords.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace llvm { +namespace xray { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Field; +using ::testing::Not; +using ::testing::SizeIs; + +// This test ensures that we can index blocks that follow version 3 of the log +// format. +TEST(FDRBlockIndexerTest, IndexBlocksV3) { + auto Block0 = LogBuilder() + .add(80) + .add(1) + .add(1, 2) + .add(1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block1 = LogBuilder() + .add(80) + .add(1) + .add(1, 2) + .add(1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block2 = LogBuilder() + .add(80) + .add(2) + .add(1, 2) + .add(1) + .add(2) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + BlockIndexer::Index Index; + BlockIndexer Indexer(Index); + // Iterate through the contrived blocks we have created above. + for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) { + // For each record in the block, we apply the indexer. + for (auto &R : B.get()) + ASSERT_FALSE(errorToBool(R->apply(Indexer))); + ASSERT_FALSE(errorToBool(Indexer.flush())); + } + + ASSERT_THAT(Index.size(), Eq(2u)); + auto T1Blocks = Index.find({1, 1}); + ASSERT_THAT(T1Blocks, Not(Eq(Index.end()))); + + // Expect only six records, because we're ignoring the BufferExtents record. + EXPECT_THAT(T1Blocks->second, + ElementsAre(Field(&BlockIndexer::Block::Records, SizeIs(6u)), + Field(&BlockIndexer::Block::Records, SizeIs(6u)))); + auto T2Blocks = Index.find({1, 2}); + ASSERT_THAT(T2Blocks, Not(Eq(Index.end()))); + EXPECT_THAT(T2Blocks->second, ElementsAre(Field(&BlockIndexer::Block::Records, + SizeIs(Eq(6u))))); +} + +// FIXME: Support indexing V2 and V1 blocks. + +} // namespace +} // namespace xray +} // namespace llvm