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