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