Index: compiler-rt/cmake/Modules/AddCompilerRT.cmake =================================================================== --- compiler-rt/cmake/Modules/AddCompilerRT.cmake +++ compiler-rt/cmake/Modules/AddCompilerRT.cmake @@ -357,6 +357,16 @@ -I${COMPILER_RT_GTEST_PATH} ) +# Mocking support. +set(COMPILER_RT_GMOCK_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock) +set(COMPILER_RT_GMOCK_SOURCE ${COMPILER_RT_GMOCK_PATH}/src/gmock-all.cc) +set(COMPILER_RT_GMOCK_CFLAGS + -DGTEST_NO_LLVM_RAW_OSTREAM=1 + -DGTEST_HAS_RTTI=0 + -I${COMPILER_RT_GMOCK_PATH}/include + -I${COMPILER_RT_GMOCK_PATH} +) + append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 COMPILER_RT_UNITTEST_CFLAGS) append_list_if(COMPILER_RT_HAS_WCOVERED_SWITCH_DEFAULT_FLAG -Wno-covered-switch-default COMPILER_RT_UNITTEST_CFLAGS) Index: compiler-rt/lib/xray/tests/CMakeLists.txt =================================================================== --- compiler-rt/lib/xray/tests/CMakeLists.txt +++ compiler-rt/lib/xray/tests/CMakeLists.txt @@ -19,9 +19,13 @@ ${XRAY_CFLAGS} ${COMPILER_RT_UNITTEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} + ${COMPILER_RT_GMOCK_CFLAGS} -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib/xray - -I${COMPILER_RT_SOURCE_DIR}/lib) + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${LLVM_MAIN_SRC_DIR}/include + -I${LLVM_INCLUDE_DIR} + ) function(add_xray_lib library) add_library(${library} STATIC ${ARGN}) @@ -68,11 +72,14 @@ COMPILE_DEPS ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} ${XRAY_HEADERS} ${XRAY_ALL_SOURCE_FILES_ABS_PATHS} RUNTIME "${XRAY_RUNTIME_LIBS}" - DEPS gtest xray llvm-xray + DEPS gtest xray llvm-xray LLVMXRay LLVMTestingSupport CFLAGS ${XRAY_UNITTEST_CFLAGS} - LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS}) + LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS} + -lLLVMXRay -lLLVMSupport -lLLVMTestingSupport -ltinfo + ) set_target_properties(XRayUnitTests - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endforeach() endif() endmacro() Index: compiler-rt/lib/xray/tests/unit/CMakeLists.txt =================================================================== --- compiler-rt/lib/xray/tests/unit/CMakeLists.txt +++ compiler-rt/lib/xray/tests/unit/CMakeLists.txt @@ -8,3 +8,7 @@ function_call_trie_test.cc xray_unit_test_main.cc) add_xray_unittest(XRayProfileCollectorTest SOURCES profile_collector_test.cc xray_unit_test_main.cc) + +add_xray_unittest(XRayFDRLoggingTest SOURCES + fdr_log_writer_test.cc + xray_unit_test_main.cc) Index: compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc =================================================================== --- /dev/null +++ compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc @@ -0,0 +1,87 @@ +//===-- fdr_log_writer_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#include + +#include "xray/xray_records.h" +#include "xray_fdr_log_writer.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Testing/Support/Error.h" +#include "llvm/XRay/Trace.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace __xray { +namespace { + +static constexpr size_t kSize = 4096; + +using ::llvm::HasValue; +using ::testing::SizeIs; + +// Exercise the common code path where we initialize a buffer and are able to +// write some records successfully. +TEST(FdrLogWriterTest, WriteSomeRecords) { + bool Success = false; + BufferQueue Buffers(kSize, 1, Success); + BufferQueue::Buffer B; + ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); + + FDRLogWriter Writer(B); + ASSERT_TRUE(Writer.writeMetadata(1)); + ASSERT_TRUE( + Writer.writeMetadata(1, 2)); + ASSERT_TRUE(Writer.writeMetadata(3)); + ASSERT_TRUE(Writer.writeMetadata(1)); + ASSERT_TRUE( + Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1)); + ASSERT_TRUE( + Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1)); + ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); + ASSERT_EQ(B.Data, nullptr); + ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); + + // We then need to go through each element of the Buffers, and re-create a + // flat buffer that we would see if they were laid out in a file. This also + // means we need to write out the header manually. + // TODO: Isolate the file header writing. + std::string Serialized; + std::aligned_storage::type + HeaderStorage; + auto *Header = reinterpret_cast(&HeaderStorage); + new (Header) XRayFileHeader(); + Header->Version = 3; + Header->Type = FileTypes::FDR_LOG; + Header->CycleFrequency = 3e9; + Header->ConstantTSC = 1; + Header->NonstopTSC = 1; + Serialized.append(reinterpret_cast(&HeaderStorage), + sizeof(XRayFileHeader)); + size_t BufferCount = 0; + Buffers.apply([&](const BufferQueue::Buffer &B) { + ++BufferCount; + auto Size = atomic_load_relaxed(&B.Extents); + auto Extents = + createMetadataRecord(Size); + Serialized.append(reinterpret_cast(&Extents), + sizeof(Extents)); + Serialized.append(reinterpret_cast(B.Data), Size); + }); + ASSERT_EQ(BufferCount, 1u); + + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(2))); +} + +} // namespace +} // namespace __xray Index: compiler-rt/lib/xray/xray_fdr_log_records.h =================================================================== --- compiler-rt/lib/xray/xray_fdr_log_records.h +++ compiler-rt/lib/xray/xray_fdr_log_records.h @@ -12,6 +12,9 @@ //===----------------------------------------------------------------------===// #ifndef XRAY_XRAY_FDR_LOG_RECORDS_H #define XRAY_XRAY_FDR_LOG_RECORDS_H +#include + +namespace __xray { enum class RecordType : uint8_t { Function, Metadata }; @@ -68,4 +71,6 @@ static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord."); +} // namespace __xray + #endif // XRAY_XRAY_FDR_LOG_RECORDS_H Index: compiler-rt/lib/xray/xray_fdr_log_writer.h =================================================================== --- /dev/null +++ compiler-rt/lib/xray/xray_fdr_log_writer.h @@ -0,0 +1,102 @@ +//===-- xray_fdr_log_writer.h ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_ +#define COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_ + +#include "xray_buffer_queue.h" +#include "xray_fdr_log_records.h" +#include +#include +#include + +namespace __xray { + +template struct SerializerImpl { + template ::type>::value, + int>::type = 0> static void serializeTo(char *Buffer, + Tuple &&T) { + auto P = reinterpret_cast(&std::get(T)); + constexpr auto Size = sizeof(std::get(T)); + auto Next = + reinterpret_cast(internal_memcpy(Buffer, P, Size)) + Size; + SerializerImpl::serializeTo(Next, std::move(T)); + } + + template = std::tuple_size::type>::value, + int>::type = 0> + static void serializeTo(char *, Tuple &&){}; +}; + +using Serializer = SerializerImpl<0>; + +template +MetadataRecord createMetadataRecord(Data &&... Ds) { + MetadataRecord R; + R.Type = 1; + R.RecordKind = static_cast(Kind); + Serializer::serializeTo(R.Data, std::make_tuple(std::forward(Ds)...)); + return R; +} + +class FDRLogWriter { + BufferQueue::Buffer &Buffer; + char *NextRecord = nullptr; + + template void writeRecord(const T &R) { + internal_memcpy(NextRecord, reinterpret_cast(&R), sizeof(T)); + NextRecord += sizeof(T); + atomic_fetch_add(&Buffer.Extents, sizeof(T), memory_order_acq_rel); + } + +public: + explicit FDRLogWriter(BufferQueue::Buffer &B) + : Buffer(B), NextRecord(static_cast(B.Data)) { + DCHECK_NE(Buffer.Data, nullptr); + } + + template + bool writeMetadata(Data &&... Ds) { + // TODO: Check boundary conditions: + // 1) Buffer is full, and cannot handle one metadata record. + // 2) Buffer queue is finalising. + writeRecord(createMetadataRecord(std::forward(Ds)...)); + return true; + } + + enum class FunctionRecordKind : uint8_t { + Enter = 0x00, + Exit = 0x01, + TailExit = 0x02, + }; + + bool writeFunction(FunctionRecordKind Kind, int32_t FuncId, int32_t Delta) { + FunctionRecord R; + R.Type = 0; + R.RecordKind = uint8_t(Kind); + R.FuncId = FuncId; + R.TSCDelta = Delta; + writeRecord(R); + return true; + } + +}; // namespace __xray + +} // namespace __xray + +#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_ Index: llvm/include/llvm/XRay/Trace.h =================================================================== --- llvm/include/llvm/XRay/Trace.h +++ llvm/include/llvm/XRay/Trace.h @@ -46,20 +46,25 @@ /// class Trace { XRayFileHeader FileHeader; - std::vector Records; + using RecordVector = std::vector; + RecordVector Records; typedef std::vector::const_iterator citerator; friend Expected loadTrace(const DataExtractor &, bool); public: + using size_type = RecordVector::size_type; + using value_type = RecordVector::value_type; + using const_iterator = RecordVector::const_iterator; + /// Provides access to the loaded XRay trace file header. const XRayFileHeader &getFileHeader() const { return FileHeader; } - citerator begin() const { return Records.begin(); } - citerator end() const { return Records.end(); } + const_iterator begin() const { return Records.begin(); } + const_iterator end() const { return Records.end(); } bool empty() const { return Records.empty(); } - size_t size() const { return Records.size(); } + size_type size() const { return Records.size(); } }; /// This function will attempt to load XRay trace records from the provided