xref: /llvm-project-15.0.7/llvm/lib/XRay/Trace.cpp (revision e96152a9)
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 
274f83c4d1SDean Michael Berris // Populates the FileHeader reference by reading the first 32 bytes of the file.
284f83c4d1SDean Michael Berris Error readBinaryFormatHeader(StringRef Data, XRayFileHeader &FileHeader) {
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   DataExtractor HeaderExtractor(Data, true, 8);
42d6c18657SDean Michael Berris   uint32_t OffsetPtr = 0;
43d6c18657SDean Michael Berris   FileHeader.Version = HeaderExtractor.getU16(&OffsetPtr);
44d6c18657SDean Michael Berris   FileHeader.Type = HeaderExtractor.getU16(&OffsetPtr);
45d6c18657SDean Michael Berris   uint32_t Bitfield = HeaderExtractor.getU32(&OffsetPtr);
46d6c18657SDean Michael Berris   FileHeader.ConstantTSC = Bitfield & 1uL;
47d6c18657SDean Michael Berris   FileHeader.NonstopTSC = Bitfield & 1uL << 1;
48d6c18657SDean Michael Berris   FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr);
4960c24878SDean Michael Berris   std::memcpy(&FileHeader.FreeFormData, Data.bytes_begin() + OffsetPtr, 16);
50d6c18657SDean Michael Berris   if (FileHeader.Version != 1)
51d6c18657SDean Michael Berris     return make_error<StringError>(
52d6c18657SDean Michael Berris         Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
53d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
544f83c4d1SDean Michael Berris   return Error::success();
554f83c4d1SDean Michael Berris }
564f83c4d1SDean Michael Berris 
574f83c4d1SDean Michael Berris Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader,
584f83c4d1SDean Michael Berris                          std::vector<XRayRecord> &Records) {
594f83c4d1SDean Michael Berris   // Check that there is at least a header
604f83c4d1SDean Michael Berris   if (Data.size() < 32)
614f83c4d1SDean Michael Berris     return make_error<StringError>(
624f83c4d1SDean Michael Berris         "Not enough bytes for an XRay log.",
634f83c4d1SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
644f83c4d1SDean Michael Berris 
654f83c4d1SDean Michael Berris   if (Data.size() - 32 == 0 || Data.size() % 32 != 0)
664f83c4d1SDean Michael Berris     return make_error<StringError>(
674f83c4d1SDean Michael Berris         "Invalid-sized XRay data.",
684f83c4d1SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
694f83c4d1SDean Michael Berris 
704f83c4d1SDean Michael Berris   if (auto E = readBinaryFormatHeader(Data, FileHeader))
714f83c4d1SDean Michael Berris     return E;
72d6c18657SDean Michael Berris 
73d6c18657SDean Michael Berris   // Each record after the header will be 32 bytes, in the following format:
74d6c18657SDean Michael Berris   //
75d6c18657SDean Michael Berris   //   (2)   uint16 : record type
76d6c18657SDean Michael Berris   //   (1)   uint8  : cpu id
77d6c18657SDean Michael Berris   //   (1)   uint8  : type
78d6c18657SDean Michael Berris   //   (4)   sint32 : function id
79d6c18657SDean Michael Berris   //   (8)   uint64 : tsc
80d6c18657SDean Michael Berris   //   (4)   uint32 : thread id
81d6c18657SDean Michael Berris   //   (12)  -      : padding
82d6c18657SDean Michael Berris   for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(32)) {
83d6c18657SDean Michael Berris     DataExtractor RecordExtractor(S, true, 8);
84d6c18657SDean Michael Berris     uint32_t OffsetPtr = 0;
85d6c18657SDean Michael Berris     Records.emplace_back();
86d6c18657SDean Michael Berris     auto &Record = Records.back();
87d6c18657SDean Michael Berris     Record.RecordType = RecordExtractor.getU16(&OffsetPtr);
88d6c18657SDean Michael Berris     Record.CPU = RecordExtractor.getU8(&OffsetPtr);
89d6c18657SDean Michael Berris     auto Type = RecordExtractor.getU8(&OffsetPtr);
90d6c18657SDean Michael Berris     switch (Type) {
91d6c18657SDean Michael Berris     case 0:
92d6c18657SDean Michael Berris       Record.Type = RecordTypes::ENTER;
93d6c18657SDean Michael Berris       break;
94d6c18657SDean Michael Berris     case 1:
95d6c18657SDean Michael Berris       Record.Type = RecordTypes::EXIT;
96d6c18657SDean Michael Berris       break;
97d6c18657SDean Michael Berris     default:
98d6c18657SDean Michael Berris       return make_error<StringError>(
99d6c18657SDean Michael Berris           Twine("Unknown record type '") + Twine(int{Type}) + "'",
100d6c18657SDean Michael Berris           std::make_error_code(std::errc::executable_format_error));
101d6c18657SDean Michael Berris     }
102d6c18657SDean Michael Berris     Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t));
103d6c18657SDean Michael Berris     Record.TSC = RecordExtractor.getU64(&OffsetPtr);
104d6c18657SDean Michael Berris     Record.TId = RecordExtractor.getU32(&OffsetPtr);
105d6c18657SDean Michael Berris   }
106d6c18657SDean Michael Berris   return Error::success();
107d6c18657SDean Michael Berris }
108d6c18657SDean Michael Berris 
1094f83c4d1SDean Michael Berris /// When reading from a Flight Data Recorder mode log, metadata records are
1104f83c4d1SDean Michael Berris /// sparse compared to packed function records, so we must maintain state as we
1114f83c4d1SDean Michael Berris /// read through the sequence of entries. This allows the reader to denormalize
1124f83c4d1SDean Michael Berris /// the CPUId and Thread Id onto each Function Record and transform delta
1134f83c4d1SDean Michael Berris /// encoded TSC values into absolute encodings on each record.
1144f83c4d1SDean Michael Berris struct FDRState {
1154f83c4d1SDean Michael Berris   uint16_t CPUId;
1164f83c4d1SDean Michael Berris   uint16_t ThreadId;
1174f83c4d1SDean Michael Berris   uint64_t BaseTSC;
1184f83c4d1SDean Michael Berris   /// Encode some of the state transitions for the FDR log reader as explicit
1194f83c4d1SDean Michael Berris   /// checks. These are expectations for the next Record in the stream.
1204f83c4d1SDean Michael Berris   enum class Token {
1214f83c4d1SDean Michael Berris     NEW_BUFFER_RECORD_OR_EOF,
1224f83c4d1SDean Michael Berris     WALLCLOCK_RECORD,
1234f83c4d1SDean Michael Berris     NEW_CPU_ID_RECORD,
12460c24878SDean Michael Berris     FUNCTION_SEQUENCE,
12560c24878SDean Michael Berris     SCAN_TO_END_OF_THREAD_BUF,
1264f83c4d1SDean Michael Berris   };
1274f83c4d1SDean Michael Berris   Token Expects;
12860c24878SDean Michael Berris   // Each threads buffer may have trailing garbage to scan over, so we track our
12960c24878SDean Michael Berris   // progress.
13060c24878SDean Michael Berris   uint64_t CurrentBufferSize;
13160c24878SDean Michael Berris   uint64_t CurrentBufferConsumed;
1324f83c4d1SDean Michael Berris };
133d6c18657SDean Michael Berris 
13460c24878SDean Michael Berris Twine fdrStateToTwine(const FDRState::Token &state) {
13560c24878SDean Michael Berris   switch (state) {
13660c24878SDean Michael Berris   case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF:
13760c24878SDean Michael Berris     return "NEW_BUFFER_RECORD_OR_EOF";
13860c24878SDean Michael Berris   case FDRState::Token::WALLCLOCK_RECORD:
13960c24878SDean Michael Berris     return "WALLCLOCK_RECORD";
14060c24878SDean Michael Berris   case FDRState::Token::NEW_CPU_ID_RECORD:
14160c24878SDean Michael Berris     return "NEW_CPU_ID_RECORD";
14260c24878SDean Michael Berris   case FDRState::Token::FUNCTION_SEQUENCE:
14360c24878SDean Michael Berris     return "FUNCTION_SEQUENCE";
14460c24878SDean Michael Berris   case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF:
14560c24878SDean Michael Berris     return "SCAN_TO_END_OF_THREAD_BUF";
14660c24878SDean Michael Berris   }
14760c24878SDean Michael Berris   return "UNKNOWN";
14860c24878SDean Michael Berris }
14960c24878SDean Michael Berris 
1504f83c4d1SDean Michael Berris /// State transition when a NewBufferRecord is encountered.
1514f83c4d1SDean Michael Berris Error processFDRNewBufferRecord(FDRState &State, uint8_t RecordFirstByte,
1524f83c4d1SDean Michael Berris                                 DataExtractor &RecordExtractor) {
1534f83c4d1SDean Michael Berris 
15460c24878SDean Michael Berris   if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF)
1554f83c4d1SDean Michael Berris     return make_error<StringError>(
1564f83c4d1SDean Michael Berris         "Malformed log. Read New Buffer record kind out of sequence",
1574f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
1584f83c4d1SDean Michael Berris   uint32_t OffsetPtr = 1; // 1 byte into record.
1594f83c4d1SDean Michael Berris   State.ThreadId = RecordExtractor.getU16(&OffsetPtr);
1604f83c4d1SDean Michael Berris   State.Expects = FDRState::Token::WALLCLOCK_RECORD;
1614f83c4d1SDean Michael Berris   return Error::success();
1624f83c4d1SDean Michael Berris }
1634f83c4d1SDean Michael Berris 
1644f83c4d1SDean Michael Berris /// State transition when an EndOfBufferRecord is encountered.
1654f83c4d1SDean Michael Berris Error processFDREndOfBufferRecord(FDRState &State, uint8_t RecordFirstByte,
1664f83c4d1SDean Michael Berris                                   DataExtractor &RecordExtractor) {
16760c24878SDean Michael Berris   if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF)
1684f83c4d1SDean Michael Berris     return make_error<StringError>(
1694f83c4d1SDean Michael Berris         "Malformed log. Received EOB message without current buffer.",
1704f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
17160c24878SDean Michael Berris   State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF;
1724f83c4d1SDean Michael Berris   return Error::success();
1734f83c4d1SDean Michael Berris }
1744f83c4d1SDean Michael Berris 
1754f83c4d1SDean Michael Berris /// State transition when a NewCPUIdRecord is encountered.
1764f83c4d1SDean Michael Berris Error processFDRNewCPUIdRecord(FDRState &State, uint8_t RecordFirstByte,
1774f83c4d1SDean Michael Berris                                DataExtractor &RecordExtractor) {
1784f83c4d1SDean Michael Berris   if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE &&
17960c24878SDean Michael Berris       State.Expects != FDRState::Token::NEW_CPU_ID_RECORD)
1804f83c4d1SDean Michael Berris     return make_error<StringError>(
1814f83c4d1SDean Michael Berris         "Malformed log. Read NewCPUId record kind out of sequence",
1824f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
1834f83c4d1SDean Michael Berris   uint32_t OffsetPtr = 1; // Read starting after the first byte.
1844f83c4d1SDean Michael Berris   State.CPUId = RecordExtractor.getU16(&OffsetPtr);
1854f83c4d1SDean Michael Berris   State.BaseTSC = RecordExtractor.getU64(&OffsetPtr);
1864f83c4d1SDean Michael Berris   State.Expects = FDRState::Token::FUNCTION_SEQUENCE;
1874f83c4d1SDean Michael Berris   return Error::success();
1884f83c4d1SDean Michael Berris }
1894f83c4d1SDean Michael Berris 
1904f83c4d1SDean Michael Berris /// State transition when a TSCWrapRecord (overflow detection) is encountered.
1914f83c4d1SDean Michael Berris Error processFDRTSCWrapRecord(FDRState &State, uint8_t RecordFirstByte,
1924f83c4d1SDean Michael Berris                               DataExtractor &RecordExtractor) {
19360c24878SDean Michael Berris   if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE)
1944f83c4d1SDean Michael Berris     return make_error<StringError>(
1954f83c4d1SDean Michael Berris         "Malformed log. Read TSCWrap record kind out of sequence",
1964f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
1974f83c4d1SDean Michael Berris   uint32_t OffsetPtr = 1; // Read starting after the first byte.
1984f83c4d1SDean Michael Berris   State.BaseTSC = RecordExtractor.getU64(&OffsetPtr);
1994f83c4d1SDean Michael Berris   return Error::success();
2004f83c4d1SDean Michael Berris }
2014f83c4d1SDean Michael Berris 
2024f83c4d1SDean Michael Berris /// State transition when a WallTimeMarkerRecord is encountered.
2034f83c4d1SDean Michael Berris Error processFDRWallTimeRecord(FDRState &State, uint8_t RecordFirstByte,
2044f83c4d1SDean Michael Berris                                DataExtractor &RecordExtractor) {
20560c24878SDean Michael Berris   if (State.Expects != FDRState::Token::WALLCLOCK_RECORD)
2064f83c4d1SDean Michael Berris     return make_error<StringError>(
2074f83c4d1SDean Michael Berris         "Malformed log. Read Wallclock record kind out of sequence",
2084f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
2094f83c4d1SDean Michael Berris   // We don't encode the wall time into any of the records.
2104f83c4d1SDean Michael Berris   // XRayRecords are concerned with the TSC instead.
2114f83c4d1SDean Michael Berris   State.Expects = FDRState::Token::NEW_CPU_ID_RECORD;
2124f83c4d1SDean Michael Berris   return Error::success();
2134f83c4d1SDean Michael Berris }
2144f83c4d1SDean Michael Berris 
2154f83c4d1SDean Michael Berris /// Advances the state machine for reading the FDR record type by reading one
216*e96152a9SKeith Wyss /// Metadata Record and updating the State appropriately based on the kind of
2174f83c4d1SDean Michael Berris /// record encountered. The RecordKind is encoded in the first byte of the
2184f83c4d1SDean Michael Berris /// Record, which the caller should pass in because they have already read it
2194f83c4d1SDean Michael Berris /// to determine that this is a metadata record as opposed to a function record.
2204f83c4d1SDean Michael Berris Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte,
2214f83c4d1SDean Michael Berris                                DataExtractor &RecordExtractor) {
2224f83c4d1SDean Michael Berris   // The remaining 7 bits are the RecordKind enum.
2234f83c4d1SDean Michael Berris   uint8_t RecordKind = RecordFirstByte >> 1;
2244f83c4d1SDean Michael Berris   switch (RecordKind) {
2254f83c4d1SDean Michael Berris   case 0: // NewBuffer
2264f83c4d1SDean Michael Berris     if (auto E =
2274f83c4d1SDean Michael Berris             processFDRNewBufferRecord(State, RecordFirstByte, RecordExtractor))
2284f83c4d1SDean Michael Berris       return E;
2294f83c4d1SDean Michael Berris     break;
2304f83c4d1SDean Michael Berris   case 1: // EndOfBuffer
2314f83c4d1SDean Michael Berris     if (auto E = processFDREndOfBufferRecord(State, RecordFirstByte,
2324f83c4d1SDean Michael Berris                                              RecordExtractor))
2334f83c4d1SDean Michael Berris       return E;
2344f83c4d1SDean Michael Berris     break;
2354f83c4d1SDean Michael Berris   case 2: // NewCPUId
2364f83c4d1SDean Michael Berris     if (auto E =
2374f83c4d1SDean Michael Berris             processFDRNewCPUIdRecord(State, RecordFirstByte, RecordExtractor))
2384f83c4d1SDean Michael Berris       return E;
2394f83c4d1SDean Michael Berris     break;
2404f83c4d1SDean Michael Berris   case 3: // TSCWrap
2414f83c4d1SDean Michael Berris     if (auto E =
2424f83c4d1SDean Michael Berris             processFDRTSCWrapRecord(State, RecordFirstByte, RecordExtractor))
2434f83c4d1SDean Michael Berris       return E;
2444f83c4d1SDean Michael Berris     break;
2454f83c4d1SDean Michael Berris   case 4: // WallTimeMarker
2464f83c4d1SDean Michael Berris     if (auto E =
2474f83c4d1SDean Michael Berris             processFDRWallTimeRecord(State, RecordFirstByte, RecordExtractor))
2484f83c4d1SDean Michael Berris       return E;
2494f83c4d1SDean Michael Berris     break;
2504f83c4d1SDean Michael Berris   default:
2514f83c4d1SDean Michael Berris     // Widen the record type to uint16_t to prevent conversion to char.
2524f83c4d1SDean Michael Berris     return make_error<StringError>(
2534f83c4d1SDean Michael Berris         Twine("Illegal metadata record type: ")
2544f83c4d1SDean Michael Berris             .concat(Twine(static_cast<unsigned>(RecordKind))),
2554f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
2564f83c4d1SDean Michael Berris   }
2574f83c4d1SDean Michael Berris   return Error::success();
2584f83c4d1SDean Michael Berris }
2594f83c4d1SDean Michael Berris 
2604f83c4d1SDean Michael Berris /// Reads a function record from an FDR format log, appending a new XRayRecord
2614f83c4d1SDean Michael Berris /// to the vector being populated and updating the State with a new value
2624f83c4d1SDean Michael Berris /// reference value to interpret TSC deltas.
2634f83c4d1SDean Michael Berris ///
2644f83c4d1SDean Michael Berris /// The XRayRecord constructed includes information from the function record
2654f83c4d1SDean Michael Berris /// processed here as well as Thread ID and CPU ID formerly extracted into
2664f83c4d1SDean Michael Berris /// State.
2674f83c4d1SDean Michael Berris Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
2684f83c4d1SDean Michael Berris                                DataExtractor &RecordExtractor,
2694f83c4d1SDean Michael Berris                                std::vector<XRayRecord> &Records) {
2704f83c4d1SDean Michael Berris   switch (State.Expects) {
2714f83c4d1SDean Michael Berris   case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF:
2724f83c4d1SDean Michael Berris     return make_error<StringError>(
2734f83c4d1SDean Michael Berris         "Malformed log. Received Function Record before new buffer setup.",
2744f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
2754f83c4d1SDean Michael Berris   case FDRState::Token::WALLCLOCK_RECORD:
2764f83c4d1SDean Michael Berris     return make_error<StringError>(
2774f83c4d1SDean Michael Berris         "Malformed log. Received Function Record when expecting wallclock.",
2784f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
2794f83c4d1SDean Michael Berris   case FDRState::Token::NEW_CPU_ID_RECORD:
2804f83c4d1SDean Michael Berris     return make_error<StringError>(
2814f83c4d1SDean Michael Berris         "Malformed log. Received Function Record before first CPU record.",
2824f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
2834f83c4d1SDean Michael Berris   default:
2844f83c4d1SDean Michael Berris     Records.emplace_back();
2854f83c4d1SDean Michael Berris     auto &Record = Records.back();
2864f83c4d1SDean Michael Berris     Record.RecordType = 0; // Record is type NORMAL.
2874f83c4d1SDean Michael Berris     // Strip off record type bit and use the next three bits.
2884f83c4d1SDean Michael Berris     uint8_t RecordType = (RecordFirstByte >> 1) & 0x07;
2894f83c4d1SDean Michael Berris     switch (RecordType) {
2904f83c4d1SDean Michael Berris     case static_cast<uint8_t>(RecordTypes::ENTER):
2914f83c4d1SDean Michael Berris       Record.Type = RecordTypes::ENTER;
2924f83c4d1SDean Michael Berris       break;
2934f83c4d1SDean Michael Berris     case static_cast<uint8_t>(RecordTypes::EXIT):
2944f83c4d1SDean Michael Berris     case 2: // TAIL_EXIT is not yet defined in RecordTypes.
2954f83c4d1SDean Michael Berris       Record.Type = RecordTypes::EXIT;
2964f83c4d1SDean Michael Berris       break;
2974f83c4d1SDean Michael Berris     default:
2984f83c4d1SDean Michael Berris       // When initializing the error, convert to uint16_t so that the record
2994f83c4d1SDean Michael Berris       // type isn't interpreted as a char.
3004f83c4d1SDean Michael Berris       return make_error<StringError>(
3014f83c4d1SDean Michael Berris           Twine("Illegal function record type: ")
3024f83c4d1SDean Michael Berris               .concat(Twine(static_cast<unsigned>(RecordType))),
3034f83c4d1SDean Michael Berris           std::make_error_code(std::errc::executable_format_error));
3044f83c4d1SDean Michael Berris     }
3054f83c4d1SDean Michael Berris     Record.CPU = State.CPUId;
3064f83c4d1SDean Michael Berris     Record.TId = State.ThreadId;
3074f83c4d1SDean Michael Berris     // Back up to read first 32 bits, including the 8 we pulled RecordType
3084f83c4d1SDean Michael Berris     // and RecordKind out of. The remaining 28 are FunctionId.
3094f83c4d1SDean Michael Berris     uint32_t OffsetPtr = 0;
3104f83c4d1SDean Michael Berris     // Despite function Id being a signed int on XRayRecord,
3114f83c4d1SDean Michael Berris     // when it is written to an FDR format, the top bits are truncated,
3124f83c4d1SDean Michael Berris     // so it is effectively an unsigned value. When we shift off the
3134f83c4d1SDean Michael Berris     // top four bits, we want the shift to be logical, so we read as
3144f83c4d1SDean Michael Berris     // uint32_t.
3154f83c4d1SDean Michael Berris     uint32_t FuncIdBitField = RecordExtractor.getU32(&OffsetPtr);
3164f83c4d1SDean Michael Berris     Record.FuncId = FuncIdBitField >> 4;
3174f83c4d1SDean Michael Berris     // FunctionRecords have a 32 bit delta from the previous absolute TSC
3184f83c4d1SDean Michael Berris     // or TSC delta. If this would overflow, we should read a TSCWrap record
3194f83c4d1SDean Michael Berris     // with an absolute TSC reading.
3204f83c4d1SDean Michael Berris     uint64_t new_tsc = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr);
3214f83c4d1SDean Michael Berris     State.BaseTSC = new_tsc;
3224f83c4d1SDean Michael Berris     Record.TSC = new_tsc;
3234f83c4d1SDean Michael Berris   }
3244f83c4d1SDean Michael Berris   return Error::success();
3254f83c4d1SDean Michael Berris }
3264f83c4d1SDean Michael Berris 
3274f83c4d1SDean Michael Berris /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is
3284f83c4d1SDean Michael Berris /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such
3294f83c4d1SDean Michael Berris /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of
3304f83c4d1SDean Michael Berris /// of interspersed 16 byte Metadata Records and 8 byte Function Records.
3314f83c4d1SDean Michael Berris ///
3324f83c4d1SDean Michael Berris /// The following is an attempt to document the grammar of the format, which is
3334f83c4d1SDean Michael Berris /// parsed by this function for little-endian machines. Since the format makes
3344f83c4d1SDean Michael Berris /// use of BitFields, when we support big-Endian architectures, we will need to
33568168d17SSimon Pilgrim /// adjust not only the endianness parameter to llvm's RecordExtractor, but also
3364f83c4d1SDean Michael Berris /// the bit twiddling logic, which is consistent with the little-endian
3374f83c4d1SDean Michael Berris /// convention that BitFields within a struct will first be packed into the
3384f83c4d1SDean Michael Berris /// least significant bits the address they belong to.
3394f83c4d1SDean Michael Berris ///
3404f83c4d1SDean Michael Berris /// We expect a format complying with the grammar in the following pseudo-EBNF.
3414f83c4d1SDean Michael Berris ///
3424f83c4d1SDean Michael Berris /// FDRLog: XRayFileHeader ThreadBuffer*
3434f83c4d1SDean Michael Berris /// XRayFileHeader: 32 bits to identify the log as FDR with machine metadata.
34460c24878SDean Michael Berris /// ThreadBuffer: BufSize NewBuffer WallClockTime NewCPUId FunctionSequence EOB
34560c24878SDean Michael Berris /// BufSize: 8 byte unsigned integer indicating how large the buffer is.
3464f83c4d1SDean Michael Berris /// NewBuffer: 16 byte metadata record with Thread Id.
3474f83c4d1SDean Michael Berris /// WallClockTime: 16 byte metadata record with human readable time.
3484f83c4d1SDean Michael Berris /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading.
34960c24878SDean Michael Berris /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize.
3504f83c4d1SDean Michael Berris /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord
3514f83c4d1SDean Michael Berris /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading.
3524f83c4d1SDean Michael Berris /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta.
3534f83c4d1SDean Michael Berris Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
3544f83c4d1SDean Michael Berris                  std::vector<XRayRecord> &Records) {
3554f83c4d1SDean Michael Berris   if (Data.size() < 32)
3564f83c4d1SDean Michael Berris     return make_error<StringError>(
3574f83c4d1SDean Michael Berris         "Not enough bytes for an XRay log.",
3584f83c4d1SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
3594f83c4d1SDean Michael Berris 
3604f83c4d1SDean Michael Berris   // For an FDR log, there are records sized 16 and 8 bytes.
36160c24878SDean Michael Berris   // There actually may be no records if no non-trivial functions are
36260c24878SDean Michael Berris   // instrumented.
36360c24878SDean Michael Berris   if (Data.size() % 8 != 0)
3644f83c4d1SDean Michael Berris     return make_error<StringError>(
3654f83c4d1SDean Michael Berris         "Invalid-sized XRay data.",
3664f83c4d1SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
3674f83c4d1SDean Michael Berris 
3684f83c4d1SDean Michael Berris   if (auto E = readBinaryFormatHeader(Data, FileHeader))
3694f83c4d1SDean Michael Berris     return E;
3704f83c4d1SDean Michael Berris 
37160c24878SDean Michael Berris   uint64_t BufferSize = 0;
37260c24878SDean Michael Berris   {
37360c24878SDean Michael Berris     StringRef ExtraDataRef(FileHeader.FreeFormData, 16);
37460c24878SDean Michael Berris     DataExtractor ExtraDataExtractor(ExtraDataRef, true, 8);
37560c24878SDean Michael Berris     uint32_t ExtraDataOffset = 0;
37660c24878SDean Michael Berris     BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset);
37760c24878SDean Michael Berris   }
37860c24878SDean Michael Berris   FDRState State{0,          0, 0, FDRState::Token::NEW_BUFFER_RECORD_OR_EOF,
37960c24878SDean Michael Berris                  BufferSize, 0};
3804f83c4d1SDean Michael Berris   // RecordSize will tell the loop how far to seek ahead based on the record
3814f83c4d1SDean Michael Berris   // type that we have just read.
3824f83c4d1SDean Michael Berris   size_t RecordSize = 0;
3834f83c4d1SDean Michael Berris   for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(RecordSize)) {
3844f83c4d1SDean Michael Berris     DataExtractor RecordExtractor(S, true, 8);
3854f83c4d1SDean Michael Berris     uint32_t OffsetPtr = 0;
38660c24878SDean Michael Berris     if (State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF) {
38760c24878SDean Michael Berris       RecordSize = State.CurrentBufferSize - State.CurrentBufferConsumed;
38860c24878SDean Michael Berris       if (S.size() < State.CurrentBufferSize - State.CurrentBufferConsumed) {
38960c24878SDean Michael Berris         return make_error<StringError>(
39060c24878SDean Michael Berris             Twine("Incomplete thread buffer. Expected ") +
39160c24878SDean Michael Berris                 Twine(State.CurrentBufferSize - State.CurrentBufferConsumed) +
39260c24878SDean Michael Berris                 " remaining bytes but found " + Twine(S.size()),
39360c24878SDean Michael Berris             make_error_code(std::errc::invalid_argument));
39460c24878SDean Michael Berris       }
39560c24878SDean Michael Berris       State.CurrentBufferConsumed = 0;
39660c24878SDean Michael Berris       State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF;
39760c24878SDean Michael Berris       continue;
39860c24878SDean Michael Berris     }
3994f83c4d1SDean Michael Berris     uint8_t BitField = RecordExtractor.getU8(&OffsetPtr);
4004f83c4d1SDean Michael Berris     bool isMetadataRecord = BitField & 0x01uL;
4014f83c4d1SDean Michael Berris     if (isMetadataRecord) {
4024f83c4d1SDean Michael Berris       RecordSize = 16;
4034f83c4d1SDean Michael Berris       if (auto E = processFDRMetadataRecord(State, BitField, RecordExtractor))
4044f83c4d1SDean Michael Berris         return E;
40560c24878SDean Michael Berris       State.CurrentBufferConsumed += RecordSize;
4064f83c4d1SDean Michael Berris     } else { // Process Function Record
4074f83c4d1SDean Michael Berris       RecordSize = 8;
4084f83c4d1SDean Michael Berris       if (auto E = processFDRFunctionRecord(State, BitField, RecordExtractor,
4094f83c4d1SDean Michael Berris                                             Records))
4104f83c4d1SDean Michael Berris         return E;
41160c24878SDean Michael Berris       State.CurrentBufferConsumed += RecordSize;
4124f83c4d1SDean Michael Berris     }
4134f83c4d1SDean Michael Berris   }
41460c24878SDean Michael Berris   // There are two conditions
41560c24878SDean Michael Berris   if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF &&
41660c24878SDean Michael Berris       !(State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF &&
41760c24878SDean Michael Berris         State.CurrentBufferSize == State.CurrentBufferConsumed))
4184f83c4d1SDean Michael Berris     return make_error<StringError>(
41960c24878SDean Michael Berris         Twine("Encountered EOF with unexpected state expectation ") +
42060c24878SDean Michael Berris             fdrStateToTwine(State.Expects) +
42160c24878SDean Michael Berris             ". Remaining expected bytes in thread buffer total " +
42260c24878SDean Michael Berris             Twine(State.CurrentBufferSize - State.CurrentBufferConsumed),
4234f83c4d1SDean Michael Berris         std::make_error_code(std::errc::executable_format_error));
4244f83c4d1SDean Michael Berris 
4254f83c4d1SDean Michael Berris   return Error::success();
4264f83c4d1SDean Michael Berris }
4274f83c4d1SDean Michael Berris 
4284f83c4d1SDean Michael Berris Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader,
4294f83c4d1SDean Michael Berris                   std::vector<XRayRecord> &Records) {
430d6c18657SDean Michael Berris   // Load the documents from the MappedFile.
431d6c18657SDean Michael Berris   YAMLXRayTrace Trace;
432d6c18657SDean Michael Berris   Input In(Data);
433d6c18657SDean Michael Berris   In >> Trace;
434d6c18657SDean Michael Berris   if (In.error())
435d6c18657SDean Michael Berris     return make_error<StringError>("Failed loading YAML Data.", In.error());
436d6c18657SDean Michael Berris 
437d6c18657SDean Michael Berris   FileHeader.Version = Trace.Header.Version;
438d6c18657SDean Michael Berris   FileHeader.Type = Trace.Header.Type;
439d6c18657SDean Michael Berris   FileHeader.ConstantTSC = Trace.Header.ConstantTSC;
440d6c18657SDean Michael Berris   FileHeader.NonstopTSC = Trace.Header.NonstopTSC;
441d6c18657SDean Michael Berris   FileHeader.CycleFrequency = Trace.Header.CycleFrequency;
442d6c18657SDean Michael Berris 
443d6c18657SDean Michael Berris   if (FileHeader.Version != 1)
444d6c18657SDean Michael Berris     return make_error<StringError>(
445d6c18657SDean Michael Berris         Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
446d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
447d6c18657SDean Michael Berris 
448d6c18657SDean Michael Berris   Records.clear();
449d6c18657SDean Michael Berris   std::transform(Trace.Records.begin(), Trace.Records.end(),
450d6c18657SDean Michael Berris                  std::back_inserter(Records), [&](const YAMLXRayRecord &R) {
451d6c18657SDean Michael Berris                    return XRayRecord{R.RecordType, R.CPU, R.Type,
452d6c18657SDean Michael Berris                                      R.FuncId,     R.TSC, R.TId};
453d6c18657SDean Michael Berris                  });
454d6c18657SDean Michael Berris   return Error::success();
455d6c18657SDean Michael Berris }
456d6c18657SDean Michael Berris 
457d6c18657SDean Michael Berris Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
458d6c18657SDean Michael Berris   int Fd;
459d6c18657SDean Michael Berris   if (auto EC = sys::fs::openFileForRead(Filename, Fd)) {
460d6c18657SDean Michael Berris     return make_error<StringError>(
461d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
462d6c18657SDean Michael Berris   }
463d6c18657SDean Michael Berris 
464d6c18657SDean Michael Berris   // Attempt to get the filesize.
465d6c18657SDean Michael Berris   uint64_t FileSize;
466d6c18657SDean Michael Berris   if (auto EC = sys::fs::file_size(Filename, FileSize)) {
467d6c18657SDean Michael Berris     return make_error<StringError>(
468d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
469d6c18657SDean Michael Berris   }
470d6c18657SDean Michael Berris   if (FileSize < 4) {
471d6c18657SDean Michael Berris     return make_error<StringError>(
472d6c18657SDean Michael Berris         Twine("File '") + Filename + "' too small for XRay.",
47384da6615SHans Wennborg         std::make_error_code(std::errc::executable_format_error));
474d6c18657SDean Michael Berris   }
475d6c18657SDean Michael Berris 
476d6c18657SDean Michael Berris   // Attempt to mmap the file.
477d6c18657SDean Michael Berris   std::error_code EC;
478d6c18657SDean Michael Berris   sys::fs::mapped_file_region MappedFile(
479d6c18657SDean Michael Berris       Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
480d6c18657SDean Michael Berris   if (EC) {
481d6c18657SDean Michael Berris     return make_error<StringError>(
482d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
483d6c18657SDean Michael Berris   }
484d6c18657SDean Michael Berris 
485d6c18657SDean Michael Berris   // Attempt to detect the file type using file magic. We have a slight bias
486d6c18657SDean Michael Berris   // towards the binary format, and we do this by making sure that the first 4
487d6c18657SDean Michael Berris   // bytes of the binary file is some combination of the following byte
488d6c18657SDean Michael Berris   // patterns:
489d6c18657SDean Michael Berris   //
490d6c18657SDean Michael Berris   //   0x0001 0x0000 - version 1, "naive" format
491d6c18657SDean Michael Berris   //   0x0001 0x0001 - version 1, "flight data recorder" format
492d6c18657SDean Michael Berris   //
493d6c18657SDean Michael Berris   // YAML files dont' typically have those first four bytes as valid text so we
494d6c18657SDean Michael Berris   // try loading assuming YAML if we don't find these bytes.
495d6c18657SDean Michael Berris   //
496d6c18657SDean Michael Berris   // Only if we can't load either the binary or the YAML format will we yield an
497d6c18657SDean Michael Berris   // error.
498d6c18657SDean Michael Berris   StringRef Magic(MappedFile.data(), 4);
499d6c18657SDean Michael Berris   DataExtractor HeaderExtractor(Magic, true, 8);
500d6c18657SDean Michael Berris   uint32_t OffsetPtr = 0;
501d6c18657SDean Michael Berris   uint16_t Version = HeaderExtractor.getU16(&OffsetPtr);
502d6c18657SDean Michael Berris   uint16_t Type = HeaderExtractor.getU16(&OffsetPtr);
503d6c18657SDean Michael Berris 
5044f83c4d1SDean Michael Berris   enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 };
5054f83c4d1SDean Michael Berris 
506d6c18657SDean Michael Berris   Trace T;
5074f83c4d1SDean Michael Berris   if (Version == 1 && Type == NAIVE_FORMAT) {
5084f83c4d1SDean Michael Berris     if (auto E =
5094f83c4d1SDean Michael Berris             loadNaiveFormatLog(StringRef(MappedFile.data(), MappedFile.size()),
5104f83c4d1SDean Michael Berris                                T.FileHeader, T.Records))
5114f83c4d1SDean Michael Berris       return std::move(E);
5124f83c4d1SDean Michael Berris   } else if (Version == 1 && Type == FLIGHT_DATA_RECORDER_FORMAT) {
5134f83c4d1SDean Michael Berris     if (auto E = loadFDRLog(StringRef(MappedFile.data(), MappedFile.size()),
514d6c18657SDean Michael Berris                             T.FileHeader, T.Records))
515d6c18657SDean Michael Berris       return std::move(E);
516d6c18657SDean Michael Berris   } else {
5174f83c4d1SDean Michael Berris     if (auto E = loadYAMLLog(StringRef(MappedFile.data(), MappedFile.size()),
518d6c18657SDean Michael Berris                              T.FileHeader, T.Records))
519d6c18657SDean Michael Berris       return std::move(E);
520d6c18657SDean Michael Berris   }
521d6c18657SDean Michael Berris 
522d6c18657SDean Michael Berris   if (Sort)
523d6c18657SDean Michael Berris     std::sort(T.Records.begin(), T.Records.end(),
524d6c18657SDean Michael Berris               [&](const XRayRecord &L, const XRayRecord &R) {
525d6c18657SDean Michael Berris                 return L.TSC < R.TSC;
526d6c18657SDean Michael Berris               });
527d6c18657SDean Michael Berris 
528d6c18657SDean Michael Berris   return std::move(T);
529d6c18657SDean Michael Berris }
530