1343171Sdim//===- FDRTraceWriter.cpp - XRay FDR Trace Writer ---------------*- C++ -*-===// 2343171Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343171Sdim// 7343171Sdim//===----------------------------------------------------------------------===// 8343171Sdim// 9343171Sdim// Test a utility that can write out XRay FDR Mode formatted trace files. 10343171Sdim// 11343171Sdim//===----------------------------------------------------------------------===// 12343171Sdim#include "llvm/XRay/FDRTraceWriter.h" 13343171Sdim#include <tuple> 14343171Sdim 15343171Sdimnamespace llvm { 16343171Sdimnamespace xray { 17343171Sdim 18343171Sdimnamespace { 19343171Sdim 20343171Sdimtemplate <size_t Index> struct IndexedWriter { 21343171Sdim template < 22343171Sdim class Tuple, 23343171Sdim typename std::enable_if< 24343171Sdim (Index < 25343171Sdim std::tuple_size<typename std::remove_reference<Tuple>::type>::value), 26343171Sdim int>::type = 0> 27343171Sdim static size_t write(support::endian::Writer &OS, Tuple &&T) { 28343171Sdim OS.write(std::get<Index>(T)); 29343171Sdim return sizeof(std::get<Index>(T)) + IndexedWriter<Index + 1>::write(OS, T); 30343171Sdim } 31343171Sdim 32343171Sdim template < 33343171Sdim class Tuple, 34343171Sdim typename std::enable_if< 35343171Sdim (Index >= 36343171Sdim std::tuple_size<typename std::remove_reference<Tuple>::type>::value), 37343171Sdim int>::type = 0> 38343171Sdim static size_t write(support::endian::Writer &OS, Tuple &&) { 39343171Sdim return 0; 40343171Sdim } 41343171Sdim}; 42343171Sdim 43343171Sdimtemplate <uint8_t Kind, class... Values> 44343171SdimError writeMetadata(support::endian::Writer &OS, Values &&... Ds) { 45343171Sdim // The first bit in the first byte of metadata records is always set to 1, so 46343171Sdim // we ensure this is the case when we write out the first byte of the record. 47343171Sdim uint8_t FirstByte = (static_cast<uint8_t>(Kind) << 1) | uint8_t{0x01u}; 48343171Sdim auto T = std::make_tuple(std::forward<Values>(std::move(Ds))...); 49343171Sdim // Write in field order. 50343171Sdim OS.write(FirstByte); 51343171Sdim auto Bytes = IndexedWriter<0>::write(OS, T); 52343171Sdim assert(Bytes <= 15 && "Must only ever write at most 16 byte metadata!"); 53343171Sdim // Pad out with appropriate numbers of zero's. 54343171Sdim for (; Bytes < 15; ++Bytes) 55343171Sdim OS.write('\0'); 56343171Sdim return Error::success(); 57343171Sdim} 58343171Sdim 59343171Sdim} // namespace 60343171Sdim 61343171SdimFDRTraceWriter::FDRTraceWriter(raw_ostream &O, const XRayFileHeader &H) 62343171Sdim : OS(O, support::endianness::native) { 63343171Sdim // We need to re-construct a header, by writing the fields we care about for 64343171Sdim // traces, in the format that the runtime would have written. 65343171Sdim uint32_t BitField = 66343171Sdim (H.ConstantTSC ? 0x01 : 0x0) | (H.NonstopTSC ? 0x02 : 0x0); 67343171Sdim 68343171Sdim // For endian-correctness, we need to write these fields in the order they 69343171Sdim // appear and that we expect, instead of blasting bytes of the struct through. 70343171Sdim OS.write(H.Version); 71343171Sdim OS.write(H.Type); 72343171Sdim OS.write(BitField); 73343171Sdim OS.write(H.CycleFrequency); 74343171Sdim ArrayRef<char> FreeFormBytes(H.FreeFormData, 75343171Sdim sizeof(XRayFileHeader::FreeFormData)); 76343171Sdim OS.write(FreeFormBytes); 77343171Sdim} 78343171Sdim 79343171SdimFDRTraceWriter::~FDRTraceWriter() {} 80343171Sdim 81343171SdimError FDRTraceWriter::visit(BufferExtents &R) { 82343171Sdim return writeMetadata<7u>(OS, R.size()); 83343171Sdim} 84343171Sdim 85343171SdimError FDRTraceWriter::visit(WallclockRecord &R) { 86343171Sdim return writeMetadata<4u>(OS, R.seconds(), R.nanos()); 87343171Sdim} 88343171Sdim 89343171SdimError FDRTraceWriter::visit(NewCPUIDRecord &R) { 90343171Sdim return writeMetadata<2u>(OS, R.cpuid(), R.tsc()); 91343171Sdim} 92343171Sdim 93343171SdimError FDRTraceWriter::visit(TSCWrapRecord &R) { 94343171Sdim return writeMetadata<3u>(OS, R.tsc()); 95343171Sdim} 96343171Sdim 97343171SdimError FDRTraceWriter::visit(CustomEventRecord &R) { 98343171Sdim if (auto E = writeMetadata<5u>(OS, R.size(), R.tsc(), R.cpu())) 99343171Sdim return E; 100343171Sdim auto D = R.data(); 101343171Sdim ArrayRef<char> Bytes(D.data(), D.size()); 102343171Sdim OS.write(Bytes); 103343171Sdim return Error::success(); 104343171Sdim} 105343171Sdim 106343171SdimError FDRTraceWriter::visit(CustomEventRecordV5 &R) { 107343171Sdim if (auto E = writeMetadata<5u>(OS, R.size(), R.delta())) 108343171Sdim return E; 109343171Sdim auto D = R.data(); 110343171Sdim ArrayRef<char> Bytes(D.data(), D.size()); 111343171Sdim OS.write(Bytes); 112343171Sdim return Error::success(); 113343171Sdim} 114343171Sdim 115343171SdimError FDRTraceWriter::visit(TypedEventRecord &R) { 116343171Sdim if (auto E = writeMetadata<8u>(OS, R.size(), R.delta(), R.eventType())) 117343171Sdim return E; 118343171Sdim auto D = R.data(); 119343171Sdim ArrayRef<char> Bytes(D.data(), D.size()); 120343171Sdim OS.write(Bytes); 121343171Sdim return Error::success(); 122343171Sdim} 123343171Sdim 124343171SdimError FDRTraceWriter::visit(CallArgRecord &R) { 125343171Sdim return writeMetadata<6u>(OS, R.arg()); 126343171Sdim} 127343171Sdim 128343171SdimError FDRTraceWriter::visit(PIDRecord &R) { 129343171Sdim return writeMetadata<9u>(OS, R.pid()); 130343171Sdim} 131343171Sdim 132343171SdimError FDRTraceWriter::visit(NewBufferRecord &R) { 133343171Sdim return writeMetadata<0u>(OS, R.tid()); 134343171Sdim} 135343171Sdim 136343171SdimError FDRTraceWriter::visit(EndBufferRecord &R) { 137343171Sdim return writeMetadata<1u>(OS, 0); 138343171Sdim} 139343171Sdim 140343171SdimError FDRTraceWriter::visit(FunctionRecord &R) { 141343171Sdim // Write out the data in "field" order, to be endian-aware. 142343171Sdim uint32_t TypeRecordFuncId = uint32_t{R.functionId() & ~uint32_t{0x0Fu << 28}}; 143343171Sdim TypeRecordFuncId <<= 3; 144343171Sdim TypeRecordFuncId |= static_cast<uint32_t>(R.recordType()); 145343171Sdim TypeRecordFuncId <<= 1; 146343171Sdim TypeRecordFuncId &= ~uint32_t{0x01}; 147343171Sdim OS.write(TypeRecordFuncId); 148343171Sdim OS.write(R.delta()); 149343171Sdim return Error::success(); 150343171Sdim} 151343171Sdim 152343171Sdim} // namespace xray 153343171Sdim} // namespace llvm 154