brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.7 KiB · 0378663 Raw
232 lines · c
1//===-- xray_fdr_log_writer.h ---------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of XRay, a function call tracing system.10//11//===----------------------------------------------------------------------===//12#ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_13#define COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_14 15#include "xray_buffer_queue.h"16#include "xray_fdr_log_records.h"17#include <functional>18#include <tuple>19#include <type_traits>20#include <utility>21 22namespace __xray {23 24template <size_t Index> struct SerializerImpl {25  template <class Tuple,26            typename std::enable_if<27                Index<std::tuple_size<28                          typename std::remove_reference<Tuple>::type>::value,29                      int>::type = 0> static void serializeTo(char *Buffer,30                                                              Tuple &&T) {31    auto P = reinterpret_cast<const char *>(&std::get<Index>(T));32    constexpr auto Size = sizeof(std::get<Index>(T));33    internal_memcpy(Buffer, P, Size);34    SerializerImpl<Index + 1>::serializeTo(Buffer + Size,35                                           std::forward<Tuple>(T));36  }37 38  template <class Tuple,39            typename std::enable_if<40                Index >= std::tuple_size<typename std::remove_reference<41                             Tuple>::type>::value,42                int>::type = 0>43  static void serializeTo(char *, Tuple &&) {}44};45 46using Serializer = SerializerImpl<0>;47 48template <class Tuple, size_t Index> struct AggregateSizesImpl {49  static constexpr size_t value =50      sizeof(typename std::tuple_element<Index, Tuple>::type) +51      AggregateSizesImpl<Tuple, Index - 1>::value;52};53 54template <class Tuple> struct AggregateSizesImpl<Tuple, 0> {55  static constexpr size_t value =56      sizeof(typename std::tuple_element<0, Tuple>::type);57};58 59template <class Tuple> struct AggregateSizes {60  static constexpr size_t value =61      AggregateSizesImpl<Tuple, std::tuple_size<Tuple>::value - 1>::value;62};63 64template <MetadataRecord::RecordKinds Kind, class... DataTypes>65MetadataRecord createMetadataRecord(DataTypes &&... Ds) {66  static_assert(AggregateSizes<std::tuple<DataTypes...>>::value <=67                    sizeof(MetadataRecord) - 1,68                "Metadata payload longer than metadata buffer!");69  MetadataRecord R;70  R.Type = 1;71  R.RecordKind = static_cast<uint8_t>(Kind);72  Serializer::serializeTo(R.Data,73                          std::make_tuple(std::forward<DataTypes>(Ds)...));74  return R;75}76 77class FDRLogWriter {78  BufferQueue::Buffer &Buffer;79  char *NextRecord = nullptr;80 81  template <class T> void writeRecord(const T &R) {82    internal_memcpy(NextRecord, reinterpret_cast<const char *>(&R), sizeof(T));83    NextRecord += sizeof(T);84    // We need this atomic fence here to ensure that other threads attempting to85    // read the bytes in the buffer will see the writes committed before the86    // extents are updated.87    atomic_thread_fence(memory_order_release);88    atomic_fetch_add(Buffer.Extents, sizeof(T), memory_order_acq_rel);89  }90 91public:92  explicit FDRLogWriter(BufferQueue::Buffer &B, char *P)93      : Buffer(B), NextRecord(P) {94    DCHECK_NE(Buffer.Data, nullptr);95    DCHECK_NE(NextRecord, nullptr);96  }97 98  explicit FDRLogWriter(BufferQueue::Buffer &B)99      : FDRLogWriter(B, static_cast<char *>(B.Data)) {}100 101  template <MetadataRecord::RecordKinds Kind, class... Data>102  bool writeMetadata(Data &&... Ds) {103    // TODO: Check boundary conditions:104    // 1) Buffer is full, and cannot handle one metadata record.105    // 2) Buffer queue is finalising.106    writeRecord(createMetadataRecord<Kind>(std::forward<Data>(Ds)...));107    return true;108  }109 110  template <size_t N> size_t writeMetadataRecords(MetadataRecord (&Recs)[N]) {111    constexpr auto Size = sizeof(MetadataRecord) * N;112    internal_memcpy(NextRecord, reinterpret_cast<const char *>(Recs), Size);113    NextRecord += Size;114    // We need this atomic fence here to ensure that other threads attempting to115    // read the bytes in the buffer will see the writes committed before the116    // extents are updated.117    atomic_thread_fence(memory_order_release);118    atomic_fetch_add(Buffer.Extents, Size, memory_order_acq_rel);119    return Size;120  }121 122  enum class FunctionRecordKind : uint8_t {123    Enter = 0x00,124    Exit = 0x01,125    TailExit = 0x02,126    EnterArg = 0x03,127  };128 129  bool writeFunction(FunctionRecordKind Kind, int32_t FuncId, int32_t Delta) {130    FunctionRecord R;131    R.Type = 0;132    R.RecordKind = uint8_t(Kind);133    R.FuncId = FuncId;134    R.TSCDelta = Delta;135    writeRecord(R);136    return true;137  }138 139  bool writeFunctionWithArg(FunctionRecordKind Kind, int32_t FuncId,140                            int32_t Delta, uint64_t Arg) {141    // We need to write the function with arg into the buffer, and then142    // atomically update the buffer extents. This ensures that any reads143    // synchronised on the buffer extents record will always see the writes144    // that happen before the atomic update.145    FunctionRecord R;146    R.Type = 0;147    R.RecordKind = uint8_t(Kind);148    R.FuncId = FuncId;149    R.TSCDelta = Delta;150    MetadataRecord A =151        createMetadataRecord<MetadataRecord::RecordKinds::CallArgument>(Arg);152    NextRecord = reinterpret_cast<char *>(internal_memcpy(153                     NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +154                 sizeof(R);155    NextRecord = reinterpret_cast<char *>(internal_memcpy(156                     NextRecord, reinterpret_cast<char *>(&A), sizeof(A))) +157                 sizeof(A);158    // We need this atomic fence here to ensure that other threads attempting to159    // read the bytes in the buffer will see the writes committed before the160    // extents are updated.161    atomic_thread_fence(memory_order_release);162    atomic_fetch_add(Buffer.Extents, sizeof(R) + sizeof(A),163                     memory_order_acq_rel);164    return true;165  }166 167  bool writeCustomEvent(int32_t Delta, const void *Event, int32_t EventSize) {168    // We write the metadata record and the custom event data into the buffer169    // first, before we atomically update the extents for the buffer. This170    // allows us to ensure that any threads reading the extents of the buffer171    // will only ever see the full metadata and custom event payload accounted172    // (no partial writes accounted).173    MetadataRecord R =174        createMetadataRecord<MetadataRecord::RecordKinds::CustomEventMarker>(175            EventSize, Delta);176    NextRecord = reinterpret_cast<char *>(internal_memcpy(177                     NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +178                 sizeof(R);179    NextRecord = reinterpret_cast<char *>(180                     internal_memcpy(NextRecord, Event, EventSize)) +181                 EventSize;182 183    // We need this atomic fence here to ensure that other threads attempting to184    // read the bytes in the buffer will see the writes committed before the185    // extents are updated.186    atomic_thread_fence(memory_order_release);187    atomic_fetch_add(Buffer.Extents, sizeof(R) + EventSize,188                     memory_order_acq_rel);189    return true;190  }191 192  bool writeTypedEvent(int32_t Delta, uint16_t EventType, const void *Event,193                       int32_t EventSize) {194    // We do something similar when writing out typed events, see195    // writeCustomEvent(...) above for details.196    MetadataRecord R =197        createMetadataRecord<MetadataRecord::RecordKinds::TypedEventMarker>(198            EventSize, Delta, EventType);199    NextRecord = reinterpret_cast<char *>(internal_memcpy(200                     NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +201                 sizeof(R);202    NextRecord = reinterpret_cast<char *>(203                     internal_memcpy(NextRecord, Event, EventSize)) +204                 EventSize;205 206    // We need this atomic fence here to ensure that other threads attempting to207    // read the bytes in the buffer will see the writes committed before the208    // extents are updated.209    atomic_thread_fence(memory_order_release);210    atomic_fetch_add(Buffer.Extents, EventSize, memory_order_acq_rel);211    return true;212  }213 214  char *getNextRecord() const { return NextRecord; }215 216  void resetRecord() {217    NextRecord = reinterpret_cast<char *>(Buffer.Data);218    atomic_store(Buffer.Extents, 0, memory_order_release);219  }220 221  void undoWrites(size_t B) {222    DCHECK_GE(NextRecord - B, reinterpret_cast<char *>(Buffer.Data));223    NextRecord -= B;224    atomic_fetch_sub(Buffer.Extents, B, memory_order_acq_rel);225  }226 227}; // namespace __xray228 229} // namespace __xray230 231#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_232