From 1f6020798493b536304dd36b393682b062ef1fbd Mon Sep 17 00:00:00 2001 From: Dean Michael Berris Date: Thu, 20 Sep 2018 05:22:37 +0000 Subject: [PATCH] [XRay][compiler-rt] FDRLogWriter Abstraction Summary: This change introduces an `FDRLogWriter` type which is responsible for serialising metadata and function records to character buffers. This is the first step in a refactoring of the implementation of the FDR runtime to allow for more granular testing of the individual components of the implementation. The main contribution of this change is a means of hiding the details of how specific records are written to a buffer, and for managing the extents of these buffers. We make use of C++ features (templates and some metaprogramming) to reduce repetition in the act of writing out specific kinds of records to the buffer. In this process, we make a number of changes across both LLVM and compiler-rt to allow us to use the `Trace` abstraction defined in the LLVM project in the testing of the runtime implementation. This gives us a closer end-to-end test which version-locks the runtime implementation with the loading implementation in LLVM. We also allow using gmock in compiler-rt unit tests, by adding the requisite definitions in the `AddCompilerRT.cmake` module. We also add the terminfo library detection along with inclusion of the appropriate compiler flags for header include lookup. Finally, we've gone ahead and updated the FDR logging implementation to use the FDRLogWriter for the lowest-level record-writing details. Following patches will isolate the state machine transitions which manage the set-up and tear-down of the buffers we're using in multiple threads. Reviewers: mboerger, eizan Subscribers: mgorny, jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D52220 llvm-svn: 342617 --- compiler-rt/cmake/Modules/AddCompilerRT.cmake | 10 ++ .../cmake/Modules/CompilerRTUtils.cmake | 6 +- compiler-rt/cmake/config-ix.cmake | 13 ++ compiler-rt/lib/xray/tests/CMakeLists.txt | 26 +++- .../lib/xray/tests/unit/CMakeLists.txt | 4 + .../xray/tests/unit/fdr_log_writer_test.cc | 92 +++++++++++++ compiler-rt/lib/xray/xray_fdr_log_records.h | 5 + compiler-rt/lib/xray/xray_fdr_log_writer.h | 119 ++++++++++++++++ compiler-rt/lib/xray/xray_fdr_logging.cc | 127 +++++++----------- llvm/include/llvm/XRay/Trace.h | 13 +- 10 files changed, 323 insertions(+), 92 deletions(-) create mode 100644 compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc create mode 100644 compiler-rt/lib/xray/xray_fdr_log_writer.h diff --git a/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/compiler-rt/cmake/Modules/AddCompilerRT.cmake index cd4c704fc824..4efbd3a4a430 100644 --- a/compiler-rt/cmake/Modules/AddCompilerRT.cmake +++ b/compiler-rt/cmake/Modules/AddCompilerRT.cmake @@ -357,6 +357,16 @@ set(COMPILER_RT_GTEST_CFLAGS -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) diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake index d8996a675719..e2884e23e376 100644 --- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake +++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake @@ -217,7 +217,7 @@ macro(load_llvm_config) endif() if (LLVM_CONFIG_PATH) execute_process( - COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root" + COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root" "--includedir" RESULT_VARIABLE HAD_ERROR OUTPUT_VARIABLE CONFIG_OUTPUT) if (HAD_ERROR) @@ -228,11 +228,13 @@ macro(load_llvm_config) list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR) list(GET CONFIG_OUTPUT 2 LIBRARY_DIR) list(GET CONFIG_OUTPUT 3 MAIN_SRC_DIR) + list(GET CONFIG_OUTPUT 4 INCLUDE_DIR) set(LLVM_BINARY_DIR ${BINARY_DIR} CACHE PATH "Path to LLVM build tree") - set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin") set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib") set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") + set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin") + set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Paths to LLVM headers") # Make use of LLVM CMake modules. # --cmakedir is supported since llvm r291218 (4.0 release) diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index 1c60c3d03145..7332cfd8b7cb 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -105,6 +105,19 @@ check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL) check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT) check_library_exists(m pow "" COMPILER_RT_HAS_LIBM) check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD) + +# Look for terminfo library, used in unittests that depend on LLVMSupport. +if(LLVM_ENABLE_TERMINFO) + foreach(library tinfo terminfo curses ncurses ncursesw) + check_library_exists( + ${library} setupterm "" COMPILER_RT_HAS_TERMINFO) + if(COMPILER_RT_HAS_TERMINFO) + set(COMPILER_RT_TERMINFO_LIB "${library}") + break() + endif() + endforeach() +endif() + if (ANDROID AND COMPILER_RT_HAS_LIBDL) # Android's libstdc++ has a dependency on libdl. list(APPEND CMAKE_REQUIRED_LIBRARIES dl) diff --git a/compiler-rt/lib/xray/tests/CMakeLists.txt b/compiler-rt/lib/xray/tests/CMakeLists.txt index 11f373167d24..d639303da431 100644 --- a/compiler-rt/lib/xray/tests/CMakeLists.txt +++ b/compiler-rt/lib/xray/tests/CMakeLists.txt @@ -19,9 +19,16 @@ set(XRAY_UNITTEST_CFLAGS ${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 + ) + +# We add the include directories one at a time in our CFLAGS. +foreach (DIR ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR}) + list(APPEND XRAY_UNITTEST_CFLAGS -I${DIR}) +endforeach() function(add_xray_lib library) add_library(${library} STATIC ${ARGN}) @@ -42,7 +49,7 @@ endfunction() set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH}) set(XRAY_UNITTEST_LINK_FLAGS ${CMAKE_THREAD_LIBS_INIT} - -l${SANITIZER_CXX_ABI_LIBRARY} + -l${SANITIZER_CXX_ABI_LIBRARY} -fxray-instrument ) if (NOT APPLE) @@ -50,6 +57,11 @@ if (NOT APPLE) append_list_if(COMPILER_RT_HAS_LIBRT -lrt XRAY_UNITTEST_LINK_FLAGS) append_list_if(COMPILER_RT_HAS_LIBDL -ldl XRAY_UNITTEST_LINK_FLAGS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread XRAY_UNITTEST_LINK_FLAGS) + + # Needed by LLVMSupport. + append_list_if( + COMPILER_RT_HAS_TERMINFO + -l${COMPILER_RT_TERMINFO_LIB} XRAY_UNITTEST_LINK_FLAGS) endif() macro(add_xray_unittest testname) @@ -62,17 +74,21 @@ macro(add_xray_unittest testname) generate_compiler_rt_tests(TEST_OBJECTS XRayUnitTests "${testname}-${arch}-Test" "${arch}" SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} + ${COMPILER_RT_GMOCK_SOURCE} + # Note that any change in the implementations will cause all the unit # tests to be re-built. This is by design, but may be cumbersome during # the build/test cycle. 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) set_target_properties(XRayUnitTests - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endforeach() endif() endmacro() diff --git a/compiler-rt/lib/xray/tests/unit/CMakeLists.txt b/compiler-rt/lib/xray/tests/unit/CMakeLists.txt index 63c46e41c4d3..b1ead627e8c3 100644 --- a/compiler-rt/lib/xray/tests/unit/CMakeLists.txt +++ b/compiler-rt/lib/xray/tests/unit/CMakeLists.txt @@ -8,3 +8,7 @@ add_xray_unittest(XRayFunctionCallTrieTest SOURCES 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) diff --git a/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc b/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc new file mode 100644 index 000000000000..3a2138cd8215 --- /dev/null +++ b/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc @@ -0,0 +1,92 @@ +//===-- 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::Eq; +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); + MetadataRecord Preamble[] = { + createMetadataRecord(int32_t{1}), + createMetadataRecord( + int64_t{1}, int32_t{2}), + createMetadataRecord(int32_t{1}), + }; + ASSERT_THAT(Writer.writeMetadataRecords(Preamble), + Eq(sizeof(MetadataRecord) * 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 diff --git a/compiler-rt/lib/xray/xray_fdr_log_records.h b/compiler-rt/lib/xray/xray_fdr_log_records.h index 87096d4fc29e..e7b1ee562e1b 100644 --- a/compiler-rt/lib/xray/xray_fdr_log_records.h +++ b/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 @@ struct alignas(8) FunctionRecord { static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord."); +} // namespace __xray + #endif // XRAY_XRAY_FDR_LOG_RECORDS_H diff --git a/compiler-rt/lib/xray/xray_fdr_log_writer.h b/compiler-rt/lib/xray/xray_fdr_log_writer.h new file mode 100644 index 000000000000..28af356e72ed --- /dev/null +++ b/compiler-rt/lib/xray/xray_fdr_log_writer.h @@ -0,0 +1,119 @@ +//===-- 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 +#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)); + internal_memcpy(Buffer, P, Size); + SerializerImpl::serializeTo(Buffer + Size, + std::forward(T)); + } + + template = std::tuple_size::type>::value, + int>::type = 0> + static void serializeTo(char *, Tuple &&){}; +}; + +using Serializer = SerializerImpl<0>; + +template +MetadataRecord createMetadataRecord(DataTypes &&... 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, char *P) + : Buffer(B), NextRecord(P) { + DCHECK_NE(Buffer.Data, nullptr); + DCHECK_NE(NextRecord, nullptr); + } + + explicit FDRLogWriter(BufferQueue::Buffer &B) + : FDRLogWriter(B, static_cast(B.Data)) {} + + 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; + } + + template size_t writeMetadataRecords(MetadataRecord (&Recs)[N]) { + constexpr auto Size = sizeof(MetadataRecord) * N; + internal_memcpy(NextRecord, reinterpret_cast(Recs), Size); + NextRecord += Size; + atomic_fetch_add(&Buffer.Extents, Size, memory_order_acq_rel); + return Size; + } + + enum class FunctionRecordKind : uint8_t { + Enter = 0x00, + Exit = 0x01, + TailExit = 0x02, + EnterArg = 0x03, + }; + + 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; + } + + char *getNextRecord() const { return NextRecord; } + +}; // namespace __xray + +} // namespace __xray + +#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_ diff --git a/compiler-rt/lib/xray/xray_fdr_logging.cc b/compiler-rt/lib/xray/xray_fdr_logging.cc index 131c137b677c..bb9492bc94dc 100644 --- a/compiler-rt/lib/xray/xray_fdr_logging.cc +++ b/compiler-rt/lib/xray/xray_fdr_logging.cc @@ -34,6 +34,7 @@ #include "xray_buffer_queue.h" #include "xray_defs.h" #include "xray_fdr_flags.h" +#include "xray_fdr_log_writer.h" #include "xray_flags.h" #include "xray_recursion_guard.h" #include "xray_tsc.h" @@ -138,59 +139,35 @@ static ThreadLocalData &getThreadLocalData() { static void writeNewBufferPreamble(tid_t Tid, timespec TS, pid_t Pid) XRAY_NEVER_INSTRUMENT { - static constexpr int InitRecordsCount = 3; + static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes"); auto &TLD = getThreadLocalData(); - MetadataRecord Metadata[InitRecordsCount]; - { - // Write out a MetadataRecord to signify that this is the start of a new - // buffer, associated with a particular thread, with a new CPU. For the - // data, we have 15 bytes to squeeze as much information as we can. At this - // point we only write down the following bytes: - // - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes) - auto &NewBuffer = Metadata[0]; - NewBuffer.Type = uint8_t(RecordType::Metadata); - NewBuffer.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewBuffer); - int32_t tid = static_cast(Tid); - internal_memcpy(&NewBuffer.Data, &tid, sizeof(tid)); - } + MetadataRecord Metadata[] = { + // Write out a MetadataRecord to signify that this is the start of a new + // buffer, associated with a particular thread, with a new CPU. For the + // data, we have 15 bytes to squeeze as much information as we can. At + // this point we only write down the following bytes: + // - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes) + createMetadataRecord( + static_cast(Tid)), - // Also write the WalltimeMarker record. - { - static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes"); - auto &WalltimeMarker = Metadata[1]; - WalltimeMarker.Type = uint8_t(RecordType::Metadata); - WalltimeMarker.RecordKind = - uint8_t(MetadataRecord::RecordKinds::WalltimeMarker); + // Also write the WalltimeMarker record. We only really need microsecond + // precision here, and enforce across platforms that we need 64-bit + // seconds and 32-bit microseconds encoded in the Metadata record. + createMetadataRecord( + static_cast(TS.tv_sec), + static_cast(TS.tv_nsec / 1000)), - // We only really need microsecond precision here, and enforce across - // platforms that we need 64-bit seconds and 32-bit microseconds encoded in - // the Metadata record. - int32_t Micros = TS.tv_nsec / 1000; - int64_t Seconds = TS.tv_sec; - internal_memcpy(WalltimeMarker.Data, &Seconds, sizeof(Seconds)); - internal_memcpy(WalltimeMarker.Data + sizeof(Seconds), &Micros, - sizeof(Micros)); - } - - // Also write the Pid record. - { - // Write out a MetadataRecord that contains the current pid - auto &PidMetadata = Metadata[2]; - PidMetadata.Type = uint8_t(RecordType::Metadata); - PidMetadata.RecordKind = uint8_t(MetadataRecord::RecordKinds::Pid); - int32_t pid = static_cast(Pid); - internal_memcpy(&PidMetadata.Data, &pid, sizeof(pid)); - } + // Also write the Pid record. + createMetadataRecord( + static_cast(Pid)), + }; TLD.NumConsecutiveFnEnters = 0; TLD.NumTailCalls = 0; if (TLD.BQ == nullptr || TLD.BQ->finalizing()) return; - internal_memcpy(TLD.RecordPtr, Metadata, - sizeof(MetadataRecord) * InitRecordsCount); - TLD.RecordPtr += sizeof(MetadataRecord) * InitRecordsCount; - atomic_store(&TLD.Buffer.Extents, sizeof(MetadataRecord) * InitRecordsCount, - memory_order_release); + FDRLogWriter Writer(TLD.Buffer); + TLD.RecordPtr += Writer.writeMetadataRecords(Metadata); } static void setupNewBuffer(int (*wall_clock_reader)( @@ -198,6 +175,7 @@ static void setupNewBuffer(int (*wall_clock_reader)( auto &TLD = getThreadLocalData(); auto &B = TLD.Buffer; TLD.RecordPtr = static_cast(B.Data); + atomic_store(&B.Extents, 0, memory_order_release); tid_t Tid = GetTid(); timespec TS{0, 0}; pid_t Pid = internal_getpid(); @@ -221,52 +199,38 @@ static void decrementExtents(size_t Subtract) { static void writeNewCPUIdMetadata(uint16_t CPU, uint64_t TSC) XRAY_NEVER_INSTRUMENT { auto &TLD = getThreadLocalData(); - MetadataRecord NewCPUId; - NewCPUId.Type = uint8_t(RecordType::Metadata); - NewCPUId.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewCPUId); + FDRLogWriter W(TLD.Buffer, TLD.RecordPtr); // The data for the New CPU will contain the following bytes: // - CPU ID (uint16_t, 2 bytes) // - Full TSC (uint64_t, 8 bytes) // Total = 10 bytes. - internal_memcpy(&NewCPUId.Data, &CPU, sizeof(CPU)); - internal_memcpy(&NewCPUId.Data[sizeof(CPU)], &TSC, sizeof(TSC)); - internal_memcpy(TLD.RecordPtr, &NewCPUId, sizeof(MetadataRecord)); - TLD.RecordPtr += sizeof(MetadataRecord); + W.writeMetadata(CPU, TSC); + TLD.RecordPtr = W.getNextRecord(); TLD.NumConsecutiveFnEnters = 0; TLD.NumTailCalls = 0; - incrementExtents(sizeof(MetadataRecord)); } static void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT { auto &TLD = getThreadLocalData(); - MetadataRecord TSCWrap; - TSCWrap.Type = uint8_t(RecordType::Metadata); - TSCWrap.RecordKind = uint8_t(MetadataRecord::RecordKinds::TSCWrap); + FDRLogWriter W(TLD.Buffer, TLD.RecordPtr); // The data for the TSCWrap record contains the following bytes: // - Full TSC (uint64_t, 8 bytes) // Total = 8 bytes. - internal_memcpy(&TSCWrap.Data, &TSC, sizeof(TSC)); - internal_memcpy(TLD.RecordPtr, &TSCWrap, sizeof(MetadataRecord)); - TLD.RecordPtr += sizeof(MetadataRecord); + W.writeMetadata(TSC); + TLD.RecordPtr = W.getNextRecord(); TLD.NumConsecutiveFnEnters = 0; TLD.NumTailCalls = 0; - incrementExtents(sizeof(MetadataRecord)); } // Call Argument metadata records store the arguments to a function in the // order of their appearance; holes are not supported by the buffer format. static void writeCallArgumentMetadata(uint64_t A) XRAY_NEVER_INSTRUMENT { auto &TLD = getThreadLocalData(); - MetadataRecord CallArg; - CallArg.Type = uint8_t(RecordType::Metadata); - CallArg.RecordKind = uint8_t(MetadataRecord::RecordKinds::CallArgument); - - internal_memcpy(CallArg.Data, &A, sizeof(A)); - internal_memcpy(TLD.RecordPtr, &CallArg, sizeof(MetadataRecord)); - TLD.RecordPtr += sizeof(MetadataRecord); - incrementExtents(sizeof(MetadataRecord)); + FDRLogWriter W(TLD.Buffer, TLD.RecordPtr); + W.writeMetadata(A); + TLD.RecordPtr = W.getNextRecord(); } static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta, @@ -279,8 +243,8 @@ static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta, return; } - FunctionRecord FuncRecord; - FuncRecord.Type = uint8_t(RecordType::Function); + auto &TLD = getThreadLocalData(); + FDRLogWriter W(TLD.Buffer, TLD.RecordPtr); // Only take 28 bits of the function id. // @@ -289,27 +253,25 @@ static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta, // to the first 28 bits. To do this properly, this means we need to mask the // function id with (2 ^ 28) - 1 == 0x0fffffff. // - FuncRecord.FuncId = FuncId & MaxFuncId; - FuncRecord.TSCDelta = TSCDelta; + auto TruncatedId = FuncId & MaxFuncId; + auto Kind = FDRLogWriter::FunctionRecordKind::Enter; - auto &TLD = getThreadLocalData(); switch (EntryType) { case XRayEntryType::ENTRY: ++TLD.NumConsecutiveFnEnters; - FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter); break; case XRayEntryType::LOG_ARGS_ENTRY: // We should not rewind functions with logged args. TLD.NumConsecutiveFnEnters = 0; TLD.NumTailCalls = 0; - FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter); + Kind = FDRLogWriter::FunctionRecordKind::EnterArg; break; case XRayEntryType::EXIT: // If we've decided to log the function exit, we will never erase the log // before it. TLD.NumConsecutiveFnEnters = 0; TLD.NumTailCalls = 0; - FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionExit); + Kind = FDRLogWriter::FunctionRecordKind::Exit; break; case XRayEntryType::TAIL: // If we just entered the function we're tail exiting from or erased every @@ -324,8 +286,7 @@ static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta, TLD.NumTailCalls = 0; TLD.NumConsecutiveFnEnters = 0; } - FuncRecord.RecordKind = - uint8_t(FunctionRecord::RecordKinds::FunctionTailExit); + Kind = FDRLogWriter::FunctionRecordKind::TailExit; break; case XRayEntryType::CUSTOM_EVENT: { // This is a bug in patching, so we'll report it once and move on. @@ -346,9 +307,8 @@ static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta, } } - internal_memcpy(TLD.RecordPtr, &FuncRecord, sizeof(FunctionRecord)); - TLD.RecordPtr += sizeof(FunctionRecord); - incrementExtents(sizeof(FunctionRecord)); + W.writeFunction(Kind, TruncatedId, TSCDelta); + TLD.RecordPtr = W.getNextRecord(); } static atomic_uint64_t TicksPerSec{0}; @@ -423,6 +383,9 @@ static void rewindRecentCall(uint64_t TSC, uint64_t &LastTSC, static bool releaseThreadLocalBuffer(BufferQueue &BQArg) { auto &TLD = getThreadLocalData(); auto EC = BQArg.releaseBuffer(TLD.Buffer); + if (TLD.Buffer.Data == nullptr) + return true; + if (EC != BufferQueue::ErrorCode::Ok) { Report("Failed to release buffer at %p; error=%s\n", TLD.Buffer.Data, BufferQueue::getErrorString(EC)); @@ -1174,6 +1137,8 @@ XRayLogInitStatus fdrLoggingInit(UNUSED size_t BufferSize, auto &TLD = *reinterpret_cast(TLDPtr); if (TLD.BQ == nullptr) return; + if (TLD.Buffer.Data == nullptr) + return; auto EC = TLD.BQ->releaseBuffer(TLD.Buffer); if (EC != BufferQueue::ErrorCode::Ok) Report("At thread exit, failed to release buffer at %p; error=%s\n", diff --git a/llvm/include/llvm/XRay/Trace.h b/llvm/include/llvm/XRay/Trace.h index 262ff96bc23e..924addd1560d 100644 --- a/llvm/include/llvm/XRay/Trace.h +++ b/llvm/include/llvm/XRay/Trace.h @@ -46,20 +46,25 @@ namespace xray { /// 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