1*d6c18657SDean Michael Berris //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// 2*d6c18657SDean Michael Berris // 3*d6c18657SDean Michael Berris // The LLVM Compiler Infrastructure 4*d6c18657SDean Michael Berris // 5*d6c18657SDean Michael Berris // This file is distributed under the University of Illinois Open Source 6*d6c18657SDean Michael Berris // License. See LICENSE.TXT for details. 7*d6c18657SDean Michael Berris // 8*d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 9*d6c18657SDean Michael Berris // 10*d6c18657SDean Michael Berris // XRay log reader implementation. 11*d6c18657SDean Michael Berris // 12*d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 13*d6c18657SDean Michael Berris #include "llvm/XRay/Trace.h" 14*d6c18657SDean Michael Berris #include "llvm/ADT/STLExtras.h" 15*d6c18657SDean Michael Berris #include "llvm/Support/DataExtractor.h" 16*d6c18657SDean Michael Berris #include "llvm/Support/Error.h" 17*d6c18657SDean Michael Berris #include "llvm/Support/FileSystem.h" 18*d6c18657SDean Michael Berris #include "llvm/XRay/YAMLXRayRecord.h" 19*d6c18657SDean Michael Berris 20*d6c18657SDean Michael Berris using namespace llvm; 21*d6c18657SDean Michael Berris using namespace llvm::xray; 22*d6c18657SDean Michael Berris using llvm::yaml::Input; 23*d6c18657SDean Michael Berris 24*d6c18657SDean Michael Berris using XRayRecordStorage = 25*d6c18657SDean Michael Berris std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type; 26*d6c18657SDean Michael Berris 27*d6c18657SDean Michael Berris Error NaiveLogLoader(StringRef Data, XRayFileHeader &FileHeader, 28*d6c18657SDean Michael Berris std::vector<XRayRecord> &Records) { 29*d6c18657SDean Michael Berris // FIXME: Maybe deduce whether the data is little or big-endian using some 30*d6c18657SDean Michael Berris // magic bytes in the beginning of the file? 31*d6c18657SDean Michael Berris 32*d6c18657SDean Michael Berris // First 32 bytes of the file will always be the header. We assume a certain 33*d6c18657SDean Michael Berris // format here: 34*d6c18657SDean Michael Berris // 35*d6c18657SDean Michael Berris // (2) uint16 : version 36*d6c18657SDean Michael Berris // (2) uint16 : type 37*d6c18657SDean Michael Berris // (4) uint32 : bitfield 38*d6c18657SDean Michael Berris // (8) uint64 : cycle frequency 39*d6c18657SDean Michael Berris // (16) - : padding 40*d6c18657SDean Michael Berris // 41*d6c18657SDean Michael Berris if (Data.size() < 32) 42*d6c18657SDean Michael Berris return make_error<StringError>( 43*d6c18657SDean Michael Berris "Not enough bytes for an XRay log.", 44*d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 45*d6c18657SDean Michael Berris 46*d6c18657SDean Michael Berris if (Data.size() - 32 == 0 || Data.size() % 32 != 0) 47*d6c18657SDean Michael Berris return make_error<StringError>( 48*d6c18657SDean Michael Berris "Invalid-sized XRay data.", 49*d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 50*d6c18657SDean Michael Berris 51*d6c18657SDean Michael Berris DataExtractor HeaderExtractor(Data, true, 8); 52*d6c18657SDean Michael Berris uint32_t OffsetPtr = 0; 53*d6c18657SDean Michael Berris FileHeader.Version = HeaderExtractor.getU16(&OffsetPtr); 54*d6c18657SDean Michael Berris FileHeader.Type = HeaderExtractor.getU16(&OffsetPtr); 55*d6c18657SDean Michael Berris uint32_t Bitfield = HeaderExtractor.getU32(&OffsetPtr); 56*d6c18657SDean Michael Berris FileHeader.ConstantTSC = Bitfield & 1uL; 57*d6c18657SDean Michael Berris FileHeader.NonstopTSC = Bitfield & 1uL << 1; 58*d6c18657SDean Michael Berris FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr); 59*d6c18657SDean Michael Berris 60*d6c18657SDean Michael Berris if (FileHeader.Version != 1) 61*d6c18657SDean Michael Berris return make_error<StringError>( 62*d6c18657SDean Michael Berris Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), 63*d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 64*d6c18657SDean Michael Berris 65*d6c18657SDean Michael Berris // Each record after the header will be 32 bytes, in the following format: 66*d6c18657SDean Michael Berris // 67*d6c18657SDean Michael Berris // (2) uint16 : record type 68*d6c18657SDean Michael Berris // (1) uint8 : cpu id 69*d6c18657SDean Michael Berris // (1) uint8 : type 70*d6c18657SDean Michael Berris // (4) sint32 : function id 71*d6c18657SDean Michael Berris // (8) uint64 : tsc 72*d6c18657SDean Michael Berris // (4) uint32 : thread id 73*d6c18657SDean Michael Berris // (12) - : padding 74*d6c18657SDean Michael Berris for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(32)) { 75*d6c18657SDean Michael Berris DataExtractor RecordExtractor(S, true, 8); 76*d6c18657SDean Michael Berris uint32_t OffsetPtr = 0; 77*d6c18657SDean Michael Berris Records.emplace_back(); 78*d6c18657SDean Michael Berris auto &Record = Records.back(); 79*d6c18657SDean Michael Berris Record.RecordType = RecordExtractor.getU16(&OffsetPtr); 80*d6c18657SDean Michael Berris Record.CPU = RecordExtractor.getU8(&OffsetPtr); 81*d6c18657SDean Michael Berris auto Type = RecordExtractor.getU8(&OffsetPtr); 82*d6c18657SDean Michael Berris switch (Type) { 83*d6c18657SDean Michael Berris case 0: 84*d6c18657SDean Michael Berris Record.Type = RecordTypes::ENTER; 85*d6c18657SDean Michael Berris break; 86*d6c18657SDean Michael Berris case 1: 87*d6c18657SDean Michael Berris Record.Type = RecordTypes::EXIT; 88*d6c18657SDean Michael Berris break; 89*d6c18657SDean Michael Berris default: 90*d6c18657SDean Michael Berris return make_error<StringError>( 91*d6c18657SDean Michael Berris Twine("Unknown record type '") + Twine(int{Type}) + "'", 92*d6c18657SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 93*d6c18657SDean Michael Berris } 94*d6c18657SDean Michael Berris Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t)); 95*d6c18657SDean Michael Berris Record.TSC = RecordExtractor.getU64(&OffsetPtr); 96*d6c18657SDean Michael Berris Record.TId = RecordExtractor.getU32(&OffsetPtr); 97*d6c18657SDean Michael Berris } 98*d6c18657SDean Michael Berris return Error::success(); 99*d6c18657SDean Michael Berris } 100*d6c18657SDean Michael Berris 101*d6c18657SDean Michael Berris Error YAMLLogLoader(StringRef Data, XRayFileHeader &FileHeader, 102*d6c18657SDean Michael Berris std::vector<XRayRecord> &Records) { 103*d6c18657SDean Michael Berris 104*d6c18657SDean Michael Berris // Load the documents from the MappedFile. 105*d6c18657SDean Michael Berris YAMLXRayTrace Trace; 106*d6c18657SDean Michael Berris Input In(Data); 107*d6c18657SDean Michael Berris In >> Trace; 108*d6c18657SDean Michael Berris if (In.error()) 109*d6c18657SDean Michael Berris return make_error<StringError>("Failed loading YAML Data.", In.error()); 110*d6c18657SDean Michael Berris 111*d6c18657SDean Michael Berris FileHeader.Version = Trace.Header.Version; 112*d6c18657SDean Michael Berris FileHeader.Type = Trace.Header.Type; 113*d6c18657SDean Michael Berris FileHeader.ConstantTSC = Trace.Header.ConstantTSC; 114*d6c18657SDean Michael Berris FileHeader.NonstopTSC = Trace.Header.NonstopTSC; 115*d6c18657SDean Michael Berris FileHeader.CycleFrequency = Trace.Header.CycleFrequency; 116*d6c18657SDean Michael Berris 117*d6c18657SDean Michael Berris if (FileHeader.Version != 1) 118*d6c18657SDean Michael Berris return make_error<StringError>( 119*d6c18657SDean Michael Berris Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), 120*d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 121*d6c18657SDean Michael Berris 122*d6c18657SDean Michael Berris Records.clear(); 123*d6c18657SDean Michael Berris std::transform(Trace.Records.begin(), Trace.Records.end(), 124*d6c18657SDean Michael Berris std::back_inserter(Records), [&](const YAMLXRayRecord &R) { 125*d6c18657SDean Michael Berris return XRayRecord{R.RecordType, R.CPU, R.Type, 126*d6c18657SDean Michael Berris R.FuncId, R.TSC, R.TId}; 127*d6c18657SDean Michael Berris }); 128*d6c18657SDean Michael Berris return Error::success(); 129*d6c18657SDean Michael Berris } 130*d6c18657SDean Michael Berris 131*d6c18657SDean Michael Berris Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { 132*d6c18657SDean Michael Berris int Fd; 133*d6c18657SDean Michael Berris if (auto EC = sys::fs::openFileForRead(Filename, Fd)) { 134*d6c18657SDean Michael Berris return make_error<StringError>( 135*d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 136*d6c18657SDean Michael Berris } 137*d6c18657SDean Michael Berris 138*d6c18657SDean Michael Berris // Attempt to get the filesize. 139*d6c18657SDean Michael Berris uint64_t FileSize; 140*d6c18657SDean Michael Berris if (auto EC = sys::fs::file_size(Filename, FileSize)) { 141*d6c18657SDean Michael Berris return make_error<StringError>( 142*d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 143*d6c18657SDean Michael Berris } 144*d6c18657SDean Michael Berris if (FileSize < 4) { 145*d6c18657SDean Michael Berris return make_error<StringError>( 146*d6c18657SDean Michael Berris Twine("File '") + Filename + "' too small for XRay.", 147*d6c18657SDean Michael Berris std::make_error_code(std::errc::protocol_error)); 148*d6c18657SDean Michael Berris } 149*d6c18657SDean Michael Berris 150*d6c18657SDean Michael Berris // Attempt to mmap the file. 151*d6c18657SDean Michael Berris std::error_code EC; 152*d6c18657SDean Michael Berris sys::fs::mapped_file_region MappedFile( 153*d6c18657SDean Michael Berris Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); 154*d6c18657SDean Michael Berris if (EC) { 155*d6c18657SDean Michael Berris return make_error<StringError>( 156*d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 157*d6c18657SDean Michael Berris } 158*d6c18657SDean Michael Berris 159*d6c18657SDean Michael Berris // Attempt to detect the file type using file magic. We have a slight bias 160*d6c18657SDean Michael Berris // towards the binary format, and we do this by making sure that the first 4 161*d6c18657SDean Michael Berris // bytes of the binary file is some combination of the following byte 162*d6c18657SDean Michael Berris // patterns: 163*d6c18657SDean Michael Berris // 164*d6c18657SDean Michael Berris // 0x0001 0x0000 - version 1, "naive" format 165*d6c18657SDean Michael Berris // 0x0001 0x0001 - version 1, "flight data recorder" format 166*d6c18657SDean Michael Berris // 167*d6c18657SDean Michael Berris // YAML files dont' typically have those first four bytes as valid text so we 168*d6c18657SDean Michael Berris // try loading assuming YAML if we don't find these bytes. 169*d6c18657SDean Michael Berris // 170*d6c18657SDean Michael Berris // Only if we can't load either the binary or the YAML format will we yield an 171*d6c18657SDean Michael Berris // error. 172*d6c18657SDean Michael Berris StringRef Magic(MappedFile.data(), 4); 173*d6c18657SDean Michael Berris DataExtractor HeaderExtractor(Magic, true, 8); 174*d6c18657SDean Michael Berris uint32_t OffsetPtr = 0; 175*d6c18657SDean Michael Berris uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); 176*d6c18657SDean Michael Berris uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); 177*d6c18657SDean Michael Berris 178*d6c18657SDean Michael Berris Trace T; 179*d6c18657SDean Michael Berris if (Version == 1 && (Type == 0 || Type == 1)) { 180*d6c18657SDean Michael Berris if (auto E = NaiveLogLoader(StringRef(MappedFile.data(), MappedFile.size()), 181*d6c18657SDean Michael Berris T.FileHeader, T.Records)) 182*d6c18657SDean Michael Berris return std::move(E); 183*d6c18657SDean Michael Berris } else { 184*d6c18657SDean Michael Berris if (auto E = YAMLLogLoader(StringRef(MappedFile.data(), MappedFile.size()), 185*d6c18657SDean Michael Berris T.FileHeader, T.Records)) 186*d6c18657SDean Michael Berris return std::move(E); 187*d6c18657SDean Michael Berris } 188*d6c18657SDean Michael Berris 189*d6c18657SDean Michael Berris if (Sort) 190*d6c18657SDean Michael Berris std::sort(T.Records.begin(), T.Records.end(), 191*d6c18657SDean Michael Berris [&](const XRayRecord &L, const XRayRecord &R) { 192*d6c18657SDean Michael Berris return L.TSC < R.TSC; 193*d6c18657SDean Michael Berris }); 194*d6c18657SDean Michael Berris 195*d6c18657SDean Michael Berris return std::move(T); 196*d6c18657SDean Michael Berris } 197