1 //===- FDRTraceWriter.cpp - XRay FDR Trace Writer ---------------*- C++ -*-===//
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-exception
7 //===----------------------------------------------------------------------===//
9 // Test a utility that can write out XRay FDR Mode formatted trace files.
11 //===----------------------------------------------------------------------===//
12 #include "llvm/XRay/FDRTraceWriter.h"
20 template <size_t Index
> struct IndexedWriter
{
23 std::enable_if_t
<(Index
<
24 std::tuple_size
<std::remove_reference_t
<Tuple
>>::value
),
26 static size_t write(support::endian::Writer
&OS
, Tuple
&&T
) {
27 OS
.write(std::get
<Index
>(T
));
28 return sizeof(std::get
<Index
>(T
)) + IndexedWriter
<Index
+ 1>::write(OS
, T
);
33 std::enable_if_t
<(Index
>=
34 std::tuple_size
<std::remove_reference_t
<Tuple
>>::value
),
36 static size_t write(support::endian::Writer
&OS
, Tuple
&&) {
41 template <uint8_t Kind
, class... Values
>
42 Error
writeMetadata(support::endian::Writer
&OS
, Values
&&... Ds
) {
43 // The first bit in the first byte of metadata records is always set to 1, so
44 // we ensure this is the case when we write out the first byte of the record.
45 uint8_t FirstByte
= (static_cast<uint8_t>(Kind
) << 1) | uint8_t{0x01u
};
46 auto T
= std::make_tuple(std::forward
<Values
>(std::move(Ds
))...);
47 // Write in field order.
49 auto Bytes
= IndexedWriter
<0>::write(OS
, T
);
50 assert(Bytes
<= 15 && "Must only ever write at most 16 byte metadata!");
51 // Pad out with appropriate numbers of zero's.
52 for (; Bytes
< 15; ++Bytes
)
54 return Error::success();
59 FDRTraceWriter::FDRTraceWriter(raw_ostream
&O
, const XRayFileHeader
&H
)
60 : OS(O
, llvm::endianness::native
) {
61 // We need to re-construct a header, by writing the fields we care about for
62 // traces, in the format that the runtime would have written.
64 (H
.ConstantTSC
? 0x01 : 0x0) | (H
.NonstopTSC
? 0x02 : 0x0);
66 // For endian-correctness, we need to write these fields in the order they
67 // appear and that we expect, instead of blasting bytes of the struct through.
71 OS
.write(H
.CycleFrequency
);
72 ArrayRef
<char> FreeFormBytes(H
.FreeFormData
,
73 sizeof(XRayFileHeader::FreeFormData
));
74 OS
.write(FreeFormBytes
);
77 FDRTraceWriter::~FDRTraceWriter() = default;
79 Error
FDRTraceWriter::visit(BufferExtents
&R
) {
80 return writeMetadata
<7u>(OS
, R
.size());
83 Error
FDRTraceWriter::visit(WallclockRecord
&R
) {
84 return writeMetadata
<4u>(OS
, R
.seconds(), R
.nanos());
87 Error
FDRTraceWriter::visit(NewCPUIDRecord
&R
) {
88 return writeMetadata
<2u>(OS
, R
.cpuid(), R
.tsc());
91 Error
FDRTraceWriter::visit(TSCWrapRecord
&R
) {
92 return writeMetadata
<3u>(OS
, R
.tsc());
95 Error
FDRTraceWriter::visit(CustomEventRecord
&R
) {
96 if (auto E
= writeMetadata
<5u>(OS
, R
.size(), R
.tsc(), R
.cpu()))
99 ArrayRef
<char> Bytes(D
.data(), D
.size());
101 return Error::success();
104 Error
FDRTraceWriter::visit(CustomEventRecordV5
&R
) {
105 if (auto E
= writeMetadata
<5u>(OS
, R
.size(), R
.delta()))
108 ArrayRef
<char> Bytes(D
.data(), D
.size());
110 return Error::success();
113 Error
FDRTraceWriter::visit(TypedEventRecord
&R
) {
114 if (auto E
= writeMetadata
<8u>(OS
, R
.size(), R
.delta(), R
.eventType()))
117 ArrayRef
<char> Bytes(D
.data(), D
.size());
119 return Error::success();
122 Error
FDRTraceWriter::visit(CallArgRecord
&R
) {
123 return writeMetadata
<6u>(OS
, R
.arg());
126 Error
FDRTraceWriter::visit(PIDRecord
&R
) {
127 return writeMetadata
<9u>(OS
, R
.pid());
130 Error
FDRTraceWriter::visit(NewBufferRecord
&R
) {
131 return writeMetadata
<0u>(OS
, R
.tid());
134 Error
FDRTraceWriter::visit(EndBufferRecord
&R
) {
135 return writeMetadata
<1u>(OS
, 0);
138 Error
FDRTraceWriter::visit(FunctionRecord
&R
) {
139 // Write out the data in "field" order, to be endian-aware.
140 uint32_t TypeRecordFuncId
= uint32_t{R
.functionId() & ~uint32_t{0x0Fu
<< 28}};
141 TypeRecordFuncId
<<= 3;
142 TypeRecordFuncId
|= static_cast<uint32_t>(R
.recordType());
143 TypeRecordFuncId
<<= 1;
144 TypeRecordFuncId
&= ~uint32_t{0x01};
145 OS
.write(TypeRecordFuncId
);
147 return Error::success();