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