Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 1 | //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 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 | //===----------------------------------------------------------------------===// |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 13 | #include "llvm/XRay/Trace.h" |
| 14 | #include "llvm/ADT/STLExtras.h" |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 15 | #include "llvm/Support/DataExtractor.h" |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 16 | #include "llvm/Support/Error.h" |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 17 | #include "llvm/Support/FileSystem.h" |
Dean Michael Berris | d764c1b | 2018-08-22 07:37:55 +0000 | [diff] [blame^] | 18 | #include "llvm/XRay/FileHeaderReader.h" |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 19 | #include "llvm/XRay/YAMLXRayRecord.h" |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 20 | |
| 21 | using namespace llvm; |
| 22 | using namespace llvm::xray; |
| 23 | using llvm::yaml::Input; |
| 24 | |
Benjamin Kramer | 49a49fe | 2017-08-20 13:03:48 +0000 | [diff] [blame] | 25 | namespace { |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 26 | using XRayRecordStorage = |
| 27 | std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type; |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 28 | |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 29 | // This is the number of bytes in the "body" of a MetadataRecord in FDR Mode. |
| 30 | // This already excludes the first byte, which indicates the type of metadata |
| 31 | // record it is. |
| 32 | constexpr auto kFDRMetadataBodySize = 15; |
| 33 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 34 | Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader, |
| 35 | std::vector<XRayRecord> &Records) { |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 36 | if (Data.size() < 32) |
| 37 | return make_error<StringError>( |
| 38 | "Not enough bytes for an XRay log.", |
| 39 | std::make_error_code(std::errc::invalid_argument)); |
| 40 | |
| 41 | if (Data.size() - 32 == 0 || Data.size() % 32 != 0) |
| 42 | return make_error<StringError>( |
| 43 | "Invalid-sized XRay data.", |
| 44 | std::make_error_code(std::errc::invalid_argument)); |
| 45 | |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 46 | DataExtractor Reader(Data, true, 8); |
| 47 | uint32_t OffsetPtr = 0; |
Dean Michael Berris | d764c1b | 2018-08-22 07:37:55 +0000 | [diff] [blame^] | 48 | auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); |
| 49 | if (!FileHeaderOrError) |
| 50 | return FileHeaderOrError.takeError(); |
| 51 | FileHeader = std::move(FileHeaderOrError.get()); |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 52 | |
| 53 | // Each record after the header will be 32 bytes, in the following format: |
| 54 | // |
| 55 | // (2) uint16 : record type |
| 56 | // (1) uint8 : cpu id |
| 57 | // (1) uint8 : type |
| 58 | // (4) sint32 : function id |
| 59 | // (8) uint64 : tsc |
| 60 | // (4) uint32 : thread id |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 61 | // (4) uint32 : process id |
| 62 | // (8) - : padding |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 63 | while (Reader.isValidOffset(OffsetPtr)) { |
| 64 | if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) |
| 65 | return createStringError( |
| 66 | std::make_error_code(std::errc::executable_format_error), |
| 67 | "Not enough bytes to read a full record at offset %d.", OffsetPtr); |
| 68 | auto PreReadOffset = OffsetPtr; |
| 69 | auto RecordType = Reader.getU16(&OffsetPtr); |
| 70 | if (OffsetPtr == PreReadOffset) |
| 71 | return createStringError( |
| 72 | std::make_error_code(std::errc::executable_format_error), |
| 73 | "Failed reading record type at offset %d.", OffsetPtr); |
| 74 | |
| 75 | switch (RecordType) { |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 76 | case 0: { // Normal records. |
| 77 | Records.emplace_back(); |
| 78 | auto &Record = Records.back(); |
| 79 | Record.RecordType = RecordType; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 80 | |
| 81 | PreReadOffset = OffsetPtr; |
| 82 | Record.CPU = Reader.getU8(&OffsetPtr); |
| 83 | if (OffsetPtr == PreReadOffset) |
| 84 | return createStringError( |
| 85 | std::make_error_code(std::errc::executable_format_error), |
| 86 | "Failed reading CPU field at offset %d.", OffsetPtr); |
| 87 | |
| 88 | PreReadOffset = OffsetPtr; |
| 89 | auto Type = Reader.getU8(&OffsetPtr); |
| 90 | if (OffsetPtr == PreReadOffset) |
| 91 | return createStringError( |
| 92 | std::make_error_code(std::errc::executable_format_error), |
| 93 | "Failed reading record type field at offset %d.", OffsetPtr); |
| 94 | |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 95 | switch (Type) { |
| 96 | case 0: |
| 97 | Record.Type = RecordTypes::ENTER; |
| 98 | break; |
| 99 | case 1: |
| 100 | Record.Type = RecordTypes::EXIT; |
| 101 | break; |
| 102 | case 2: |
| 103 | Record.Type = RecordTypes::TAIL_EXIT; |
| 104 | break; |
| 105 | case 3: |
| 106 | Record.Type = RecordTypes::ENTER_ARG; |
| 107 | break; |
| 108 | default: |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 109 | return createStringError( |
| 110 | std::make_error_code(std::errc::executable_format_error), |
| 111 | "Unknown record type '%d' at offset %d.", Type, OffsetPtr); |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 112 | } |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 113 | |
| 114 | PreReadOffset = OffsetPtr; |
| 115 | Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); |
| 116 | if (OffsetPtr == PreReadOffset) |
| 117 | return createStringError( |
| 118 | std::make_error_code(std::errc::executable_format_error), |
| 119 | "Failed reading function id field at offset %d.", OffsetPtr); |
| 120 | |
| 121 | PreReadOffset = OffsetPtr; |
| 122 | Record.TSC = Reader.getU64(&OffsetPtr); |
| 123 | if (OffsetPtr == PreReadOffset) |
| 124 | return createStringError( |
| 125 | std::make_error_code(std::errc::executable_format_error), |
| 126 | "Failed reading TSC field at offset %d.", OffsetPtr); |
| 127 | |
| 128 | PreReadOffset = OffsetPtr; |
| 129 | Record.TId = Reader.getU32(&OffsetPtr); |
| 130 | if (OffsetPtr == PreReadOffset) |
| 131 | return createStringError( |
| 132 | std::make_error_code(std::errc::executable_format_error), |
| 133 | "Failed reading thread id field at offset %d.", OffsetPtr); |
| 134 | |
| 135 | PreReadOffset = OffsetPtr; |
| 136 | Record.PId = Reader.getU32(&OffsetPtr); |
| 137 | if (OffsetPtr == PreReadOffset) |
| 138 | return createStringError( |
| 139 | std::make_error_code(std::errc::executable_format_error), |
| 140 | "Failed reading process id at offset %d.", OffsetPtr); |
| 141 | |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 142 | break; |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 143 | } |
| 144 | case 1: { // Arg payload record. |
| 145 | auto &Record = Records.back(); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 146 | |
| 147 | // We skip the next two bytes of the record, because we don't need the |
| 148 | // type and the CPU record for arg payloads. |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 149 | OffsetPtr += 2; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 150 | PreReadOffset = OffsetPtr; |
| 151 | int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); |
| 152 | if (OffsetPtr == PreReadOffset) |
| 153 | return createStringError( |
| 154 | std::make_error_code(std::errc::executable_format_error), |
| 155 | "Failed reading function id field at offset %d.", OffsetPtr); |
| 156 | |
| 157 | PreReadOffset = OffsetPtr; |
| 158 | auto TId = Reader.getU32(&OffsetPtr); |
| 159 | if (OffsetPtr == PreReadOffset) |
| 160 | return createStringError( |
| 161 | std::make_error_code(std::errc::executable_format_error), |
| 162 | "Failed reading thread id field at offset %d.", OffsetPtr); |
| 163 | |
| 164 | PreReadOffset = OffsetPtr; |
| 165 | auto PId = Reader.getU32(&OffsetPtr); |
| 166 | if (OffsetPtr == PreReadOffset) |
| 167 | return createStringError( |
| 168 | std::make_error_code(std::errc::executable_format_error), |
| 169 | "Failed reading process id field at offset %d.", OffsetPtr); |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 170 | |
| 171 | // Make a check for versions above 3 for the Pid field |
| 172 | if (Record.FuncId != FuncId || Record.TId != TId || |
| 173 | (FileHeader.Version >= 3 ? Record.PId != PId : false)) |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 174 | return createStringError( |
| 175 | std::make_error_code(std::errc::executable_format_error), |
| 176 | "Corrupted log, found arg payload following non-matching " |
| 177 | "function+thread record. Record for function %d != %d at offset " |
| 178 | "%d", |
| 179 | Record.FuncId, FuncId, OffsetPtr); |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 180 | |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 181 | PreReadOffset = OffsetPtr; |
| 182 | auto Arg = Reader.getU64(&OffsetPtr); |
| 183 | if (OffsetPtr == PreReadOffset) |
| 184 | return createStringError( |
| 185 | std::make_error_code(std::errc::executable_format_error), |
| 186 | "Failed reading argument payload at offset %d.", OffsetPtr); |
| 187 | |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 188 | Record.CallArgs.push_back(Arg); |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 189 | break; |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 190 | } |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 191 | default: |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 192 | return createStringError( |
| 193 | std::make_error_code(std::errc::executable_format_error), |
| 194 | "Unknown record type '%d' at offset %d.", RecordType, OffsetPtr); |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 195 | } |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 196 | // Advance the offset pointer enough bytes to align to 32-byte records for |
| 197 | // basic mode logs. |
| 198 | OffsetPtr += 8; |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 199 | } |
| 200 | return Error::success(); |
| 201 | } |
| 202 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 203 | /// When reading from a Flight Data Recorder mode log, metadata records are |
| 204 | /// sparse compared to packed function records, so we must maintain state as we |
| 205 | /// read through the sequence of entries. This allows the reader to denormalize |
| 206 | /// the CPUId and Thread Id onto each Function Record and transform delta |
| 207 | /// encoded TSC values into absolute encodings on each record. |
| 208 | struct FDRState { |
| 209 | uint16_t CPUId; |
| 210 | uint16_t ThreadId; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 211 | int32_t ProcessId; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 212 | uint64_t BaseTSC; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 213 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 214 | /// Encode some of the state transitions for the FDR log reader as explicit |
| 215 | /// checks. These are expectations for the next Record in the stream. |
| 216 | enum class Token { |
| 217 | NEW_BUFFER_RECORD_OR_EOF, |
| 218 | WALLCLOCK_RECORD, |
| 219 | NEW_CPU_ID_RECORD, |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 220 | FUNCTION_SEQUENCE, |
| 221 | SCAN_TO_END_OF_THREAD_BUF, |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 222 | CUSTOM_EVENT_DATA, |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 223 | CALL_ARGUMENT, |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 224 | BUFFER_EXTENTS, |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 225 | PID_RECORD, |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 226 | }; |
| 227 | Token Expects; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 228 | |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 229 | // Each threads buffer may have trailing garbage to scan over, so we track our |
| 230 | // progress. |
| 231 | uint64_t CurrentBufferSize; |
| 232 | uint64_t CurrentBufferConsumed; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 233 | }; |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 234 | |
Benjamin Kramer | 49a49fe | 2017-08-20 13:03:48 +0000 | [diff] [blame] | 235 | const char *fdrStateToTwine(const FDRState::Token &state) { |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 236 | switch (state) { |
| 237 | case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: |
| 238 | return "NEW_BUFFER_RECORD_OR_EOF"; |
| 239 | case FDRState::Token::WALLCLOCK_RECORD: |
| 240 | return "WALLCLOCK_RECORD"; |
| 241 | case FDRState::Token::NEW_CPU_ID_RECORD: |
| 242 | return "NEW_CPU_ID_RECORD"; |
| 243 | case FDRState::Token::FUNCTION_SEQUENCE: |
| 244 | return "FUNCTION_SEQUENCE"; |
| 245 | case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF: |
| 246 | return "SCAN_TO_END_OF_THREAD_BUF"; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 247 | case FDRState::Token::CUSTOM_EVENT_DATA: |
| 248 | return "CUSTOM_EVENT_DATA"; |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 249 | case FDRState::Token::CALL_ARGUMENT: |
| 250 | return "CALL_ARGUMENT"; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 251 | case FDRState::Token::BUFFER_EXTENTS: |
| 252 | return "BUFFER_EXTENTS"; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 253 | case FDRState::Token::PID_RECORD: |
| 254 | return "PID_RECORD"; |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 255 | } |
| 256 | return "UNKNOWN"; |
| 257 | } |
| 258 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 259 | /// State transition when a NewBufferRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 260 | Error processFDRNewBufferRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 261 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 262 | if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 263 | return createStringError( |
| 264 | std::make_error_code(std::errc::executable_format_error), |
| 265 | "Malformed log: Read New Buffer record kind out of sequence; expected: " |
| 266 | "%s at offset %d.", |
| 267 | fdrStateToTwine(State.Expects), OffsetPtr); |
| 268 | |
| 269 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 270 | State.ThreadId = RecordExtractor.getU16(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 271 | if (OffsetPtr == PreReadOffset) |
| 272 | return createStringError( |
| 273 | std::make_error_code(std::errc::executable_format_error), |
| 274 | "Failed reading the thread id at offset %d.", OffsetPtr); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 275 | State.Expects = FDRState::Token::WALLCLOCK_RECORD; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 276 | |
| 277 | // Advance the offset pointer by enough bytes representing the remaining |
| 278 | // padding in a metadata record. |
| 279 | OffsetPtr += kFDRMetadataBodySize - 2; |
| 280 | assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 281 | return Error::success(); |
| 282 | } |
| 283 | |
| 284 | /// State transition when an EndOfBufferRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 285 | Error processFDREndOfBufferRecord(FDRState &State, uint32_t &OffsetPtr) { |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 286 | if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 287 | return createStringError( |
| 288 | std::make_error_code(std::errc::executable_format_error), |
| 289 | "Malformed log: Received EOB message without current buffer; expected: " |
| 290 | "%s at offset %d.", |
| 291 | fdrStateToTwine(State.Expects), OffsetPtr); |
| 292 | |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 293 | State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 294 | |
| 295 | // Advance the offset pointer by enough bytes representing the remaining |
| 296 | // padding in a metadata record. |
| 297 | OffsetPtr += kFDRMetadataBodySize; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 298 | return Error::success(); |
| 299 | } |
| 300 | |
| 301 | /// State transition when a NewCPUIdRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 302 | Error processFDRNewCPUIdRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 303 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 304 | if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE && |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 305 | State.Expects != FDRState::Token::NEW_CPU_ID_RECORD) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 306 | return make_error<StringError>( |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 307 | Twine("Malformed log. Read NewCPUId record kind out of sequence; " |
| 308 | "expected: ") + |
| 309 | fdrStateToTwine(State.Expects), |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 310 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 311 | auto BeginOffset = OffsetPtr; |
| 312 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 313 | State.CPUId = RecordExtractor.getU16(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 314 | if (OffsetPtr == PreReadOffset) |
| 315 | return createStringError( |
| 316 | std::make_error_code(std::errc::executable_format_error), |
| 317 | "Failed reading the CPU field at offset %d.", OffsetPtr); |
| 318 | |
| 319 | PreReadOffset = OffsetPtr; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 320 | State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 321 | if (OffsetPtr == PreReadOffset) |
| 322 | return createStringError( |
| 323 | std::make_error_code(std::errc::executable_format_error), |
| 324 | "Failed reading the base TSC field at offset %d.", OffsetPtr); |
| 325 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 326 | State.Expects = FDRState::Token::FUNCTION_SEQUENCE; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 327 | |
| 328 | // Advance the offset pointer by a few bytes, to account for the padding in |
| 329 | // CPU ID metadata records that we've already advanced through. |
| 330 | OffsetPtr += kFDRMetadataBodySize - (OffsetPtr - BeginOffset); |
| 331 | assert(OffsetPtr - BeginOffset == kFDRMetadataBodySize); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 332 | return Error::success(); |
| 333 | } |
| 334 | |
| 335 | /// State transition when a TSCWrapRecord (overflow detection) is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 336 | Error processFDRTSCWrapRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 337 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 338 | if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 339 | return make_error<StringError>( |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 340 | Twine("Malformed log. Read TSCWrap record kind out of sequence; " |
| 341 | "expecting: ") + |
| 342 | fdrStateToTwine(State.Expects), |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 343 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 344 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 345 | State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 346 | if (OffsetPtr == PreReadOffset) |
| 347 | return createStringError( |
| 348 | std::make_error_code(std::errc::executable_format_error), |
| 349 | "Failed reading the base TSC field at offset %d.", OffsetPtr); |
| 350 | |
| 351 | // Advance the offset pointer by a few more bytes, accounting for the padding |
| 352 | // in the metadata record after reading the base TSC. |
| 353 | OffsetPtr += kFDRMetadataBodySize - 8; |
| 354 | assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 355 | return Error::success(); |
| 356 | } |
| 357 | |
| 358 | /// State transition when a WallTimeMarkerRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 359 | Error processFDRWallTimeRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 360 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 361 | if (State.Expects != FDRState::Token::WALLCLOCK_RECORD) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 362 | return make_error<StringError>( |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 363 | Twine("Malformed log. Read Wallclock record kind out of sequence; " |
| 364 | "expecting: ") + |
| 365 | fdrStateToTwine(State.Expects), |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 366 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 367 | |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 368 | // Read in the data from the walltime record. |
| 369 | auto PreReadOffset = OffsetPtr; |
| 370 | auto WallTime = RecordExtractor.getU64(&OffsetPtr); |
| 371 | if (OffsetPtr == PreReadOffset) |
| 372 | return createStringError( |
| 373 | std::make_error_code(std::errc::executable_format_error), |
| 374 | "Failed reading the walltime record at offset %d.", OffsetPtr); |
| 375 | |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 376 | // TODO: Someday, reconcile the TSC ticks to wall clock time for presentation |
| 377 | // purposes. For now, we're ignoring these records. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 378 | (void)WallTime; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 379 | State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 380 | |
| 381 | // Advance the offset pointer by a few more bytes, accounting for the padding |
| 382 | // in the metadata record after reading in the walltime data. |
| 383 | OffsetPtr += kFDRMetadataBodySize - 8; |
| 384 | assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 385 | return Error::success(); |
| 386 | } |
| 387 | |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 388 | /// State transition when a PidRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 389 | Error processFDRPidRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 390 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 391 | if (State.Expects != FDRState::Token::PID_RECORD) |
| 392 | return make_error<StringError>( |
| 393 | Twine("Malformed log. Read Pid record kind out of sequence; " |
| 394 | "expected: ") + |
| 395 | fdrStateToTwine(State.Expects), |
| 396 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 397 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 398 | State.ProcessId = RecordExtractor.getU32(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 399 | if (OffsetPtr == PreReadOffset) |
| 400 | return createStringError( |
| 401 | std::make_error_code(std::errc::executable_format_error), |
| 402 | "Failed reading the process ID at offset %d.", OffsetPtr); |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 403 | State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 404 | |
| 405 | // Advance the offset pointer by a few more bytes, accounting for the padding |
| 406 | // in the metadata record after reading in the PID. |
| 407 | OffsetPtr += kFDRMetadataBodySize - 4; |
| 408 | assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 409 | return Error::success(); |
| 410 | } |
| 411 | |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 412 | /// State transition when a CustomEventMarker is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 413 | Error processCustomEventMarker(FDRState &State, DataExtractor &RecordExtractor, |
| 414 | uint32_t &OffsetPtr) { |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 415 | // We can encounter a CustomEventMarker anywhere in the log, so we can handle |
Keith Wyss | 3d0bc9e | 2017-08-02 21:47:27 +0000 | [diff] [blame] | 416 | // it regardless of the expectation. However, we do set the expectation to |
| 417 | // read a set number of fixed bytes, as described in the metadata. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 418 | auto BeginOffset = OffsetPtr; |
| 419 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 420 | uint32_t DataSize = RecordExtractor.getU32(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 421 | if (OffsetPtr == PreReadOffset) |
| 422 | return createStringError( |
| 423 | std::make_error_code(std::errc::executable_format_error), |
| 424 | "Failed reading a custom event marker at offset %d.", OffsetPtr); |
| 425 | |
| 426 | PreReadOffset = OffsetPtr; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 427 | uint64_t TSC = RecordExtractor.getU64(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 428 | if (OffsetPtr == PreReadOffset) |
| 429 | return createStringError( |
| 430 | std::make_error_code(std::errc::executable_format_error), |
| 431 | "Failed reading the TSC at offset %d.", OffsetPtr); |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 432 | |
Dean Michael Berris | 0a465d7 | 2017-10-05 05:18:17 +0000 | [diff] [blame] | 433 | // FIXME: Actually represent the record through the API. For now we only |
| 434 | // skip through the data. |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 435 | (void)TSC; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 436 | // Advance the offset ptr by the size of the data associated with the custom |
| 437 | // event, as well as the padding associated with the remainder of the metadata |
| 438 | // record. |
| 439 | OffsetPtr += (kFDRMetadataBodySize - (OffsetPtr - BeginOffset)) + DataSize; |
| 440 | if (!RecordExtractor.isValidOffset(OffsetPtr)) |
| 441 | return createStringError( |
| 442 | std::make_error_code(std::errc::executable_format_error), |
| 443 | "Reading custom event data moves past addressable trace data (starting " |
| 444 | "at offset %d, advancing to offset %d).", |
| 445 | BeginOffset, OffsetPtr); |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 446 | return Error::success(); |
| 447 | } |
| 448 | |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 449 | /// State transition when an BufferExtents record is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 450 | Error processBufferExtents(FDRState &State, DataExtractor &RecordExtractor, |
| 451 | uint32_t &OffsetPtr) { |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 452 | if (State.Expects != FDRState::Token::BUFFER_EXTENTS) |
| 453 | return make_error<StringError>( |
| 454 | Twine("Malformed log. Buffer Extents unexpected; expected: ") + |
| 455 | fdrStateToTwine(State.Expects), |
| 456 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 457 | |
| 458 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 459 | State.CurrentBufferSize = RecordExtractor.getU64(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 460 | if (OffsetPtr == PreReadOffset) |
| 461 | return createStringError( |
| 462 | std::make_error_code(std::errc::executable_format_error), |
| 463 | "Failed to read current buffer size at offset %d.", OffsetPtr); |
| 464 | |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 465 | State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 466 | |
| 467 | // Advance the offset pointer by enough bytes accounting for the padding in a |
| 468 | // metadata record, after we read in the buffer extents. |
| 469 | OffsetPtr += kFDRMetadataBodySize - 8; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 470 | return Error::success(); |
| 471 | } |
| 472 | |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 473 | /// State transition when a CallArgumentRecord is encountered. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 474 | Error processFDRCallArgumentRecord(FDRState &State, |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 475 | DataExtractor &RecordExtractor, |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 476 | std::vector<XRayRecord> &Records, |
| 477 | uint32_t &OffsetPtr) { |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 478 | auto &Enter = Records.back(); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 479 | if (Enter.Type != RecordTypes::ENTER && Enter.Type != RecordTypes::ENTER_ARG) |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 480 | return make_error<StringError>( |
| 481 | "CallArgument needs to be right after a function entry", |
| 482 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 483 | |
| 484 | auto PreReadOffset = OffsetPtr; |
| 485 | auto Arg = RecordExtractor.getU64(&OffsetPtr); |
| 486 | if (OffsetPtr == PreReadOffset) |
| 487 | return createStringError( |
| 488 | std::make_error_code(std::errc::executable_format_error), |
| 489 | "Failed to read argument record at offset %d.", OffsetPtr); |
| 490 | |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 491 | Enter.Type = RecordTypes::ENTER_ARG; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 492 | Enter.CallArgs.emplace_back(Arg); |
| 493 | |
| 494 | // Advance the offset pointer by enough bytes accounting for the padding in a |
| 495 | // metadata record, after reading the payload. |
| 496 | OffsetPtr += kFDRMetadataBodySize - 8; |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 497 | return Error::success(); |
| 498 | } |
| 499 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 500 | /// Advances the state machine for reading the FDR record type by reading one |
Keith Wyss | e96152a | 2017-04-06 03:32:01 +0000 | [diff] [blame] | 501 | /// Metadata Record and updating the State appropriately based on the kind of |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 502 | /// record encountered. The RecordKind is encoded in the first byte of the |
| 503 | /// Record, which the caller should pass in because they have already read it |
| 504 | /// to determine that this is a metadata record as opposed to a function record. |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 505 | /// |
| 506 | /// Beginning with Version 2 of the FDR log, we do not depend on the size of the |
| 507 | /// buffer, but rather use the extents to determine how far to read in the log |
| 508 | /// for this particular buffer. |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 509 | /// |
| 510 | /// In Version 3, FDR log now includes a pid metadata record after |
| 511 | /// WallTimeMarker |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 512 | Error processFDRMetadataRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 513 | uint32_t &OffsetPtr, |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 514 | std::vector<XRayRecord> &Records, |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 515 | uint16_t Version, uint8_t FirstByte) { |
| 516 | // The remaining 7 bits of the first byte are the RecordKind enum for each |
| 517 | // Metadata Record. |
| 518 | switch (FirstByte >> 1) { |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 519 | case 0: // NewBuffer |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 520 | if (auto E = processFDRNewBufferRecord(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 521 | return E; |
| 522 | break; |
| 523 | case 1: // EndOfBuffer |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 524 | if (Version >= 2) |
| 525 | return make_error<StringError>( |
| 526 | "Since Version 2 of FDR logging, we no longer support EOB records.", |
| 527 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 528 | if (auto E = processFDREndOfBufferRecord(State, OffsetPtr)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 529 | return E; |
| 530 | break; |
| 531 | case 2: // NewCPUId |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 532 | if (auto E = processFDRNewCPUIdRecord(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 533 | return E; |
| 534 | break; |
| 535 | case 3: // TSCWrap |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 536 | if (auto E = processFDRTSCWrapRecord(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 537 | return E; |
| 538 | break; |
| 539 | case 4: // WallTimeMarker |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 540 | if (auto E = processFDRWallTimeRecord(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 541 | return E; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 542 | // In Version 3 and and above, a PidRecord is expected after WallTimeRecord |
| 543 | if (Version >= 3) |
| 544 | State.Expects = FDRState::Token::PID_RECORD; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 545 | break; |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 546 | case 5: // CustomEventMarker |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 547 | if (auto E = processCustomEventMarker(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | a7bbe44 | 2017-05-12 01:06:41 +0000 | [diff] [blame] | 548 | return E; |
| 549 | break; |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 550 | case 6: // CallArgument |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 551 | if (auto E = processFDRCallArgumentRecord(State, RecordExtractor, Records, |
| 552 | OffsetPtr)) |
Martin Pelikan | 10c873f | 2017-09-27 04:48:03 +0000 | [diff] [blame] | 553 | return E; |
| 554 | break; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 555 | case 7: // BufferExtents |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 556 | if (auto E = processBufferExtents(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 557 | return E; |
| 558 | break; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 559 | case 9: // Pid |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 560 | if (auto E = processFDRPidRecord(State, RecordExtractor, OffsetPtr)) |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 561 | return E; |
| 562 | break; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 563 | default: |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 564 | return createStringError( |
| 565 | std::make_error_code(std::errc::executable_format_error), |
| 566 | "Illegal metadata record type: '%d' at offset %d.", FirstByte >> 1, |
| 567 | OffsetPtr); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 568 | } |
| 569 | return Error::success(); |
| 570 | } |
| 571 | |
| 572 | /// Reads a function record from an FDR format log, appending a new XRayRecord |
| 573 | /// to the vector being populated and updating the State with a new value |
| 574 | /// reference value to interpret TSC deltas. |
| 575 | /// |
| 576 | /// The XRayRecord constructed includes information from the function record |
| 577 | /// processed here as well as Thread ID and CPU ID formerly extracted into |
| 578 | /// State. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 579 | Error processFDRFunctionRecord(FDRState &State, DataExtractor &RecordExtractor, |
| 580 | uint32_t &OffsetPtr, uint8_t FirstByte, |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 581 | std::vector<XRayRecord> &Records) { |
| 582 | switch (State.Expects) { |
| 583 | case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: |
| 584 | return make_error<StringError>( |
| 585 | "Malformed log. Received Function Record before new buffer setup.", |
| 586 | std::make_error_code(std::errc::executable_format_error)); |
| 587 | case FDRState::Token::WALLCLOCK_RECORD: |
| 588 | return make_error<StringError>( |
| 589 | "Malformed log. Received Function Record when expecting wallclock.", |
| 590 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 591 | case FDRState::Token::PID_RECORD: |
| 592 | return make_error<StringError>( |
| 593 | "Malformed log. Received Function Record when expecting pid.", |
| 594 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 595 | case FDRState::Token::NEW_CPU_ID_RECORD: |
| 596 | return make_error<StringError>( |
| 597 | "Malformed log. Received Function Record before first CPU record.", |
| 598 | std::make_error_code(std::errc::executable_format_error)); |
| 599 | default: |
| 600 | Records.emplace_back(); |
| 601 | auto &Record = Records.back(); |
| 602 | Record.RecordType = 0; // Record is type NORMAL. |
| 603 | // Strip off record type bit and use the next three bits. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 604 | auto T = (FirstByte >> 1) & 0x07; |
| 605 | switch (T) { |
| 606 | case static_cast<decltype(T)>(RecordTypes::ENTER): |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 607 | Record.Type = RecordTypes::ENTER; |
| 608 | break; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 609 | case static_cast<decltype(T)>(RecordTypes::EXIT): |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 610 | Record.Type = RecordTypes::EXIT; |
| 611 | break; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 612 | case static_cast<decltype(T)>(RecordTypes::TAIL_EXIT): |
Dean Michael Berris | 0f84a7d | 2017-09-18 06:08:46 +0000 | [diff] [blame] | 613 | Record.Type = RecordTypes::TAIL_EXIT; |
| 614 | break; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 615 | case static_cast<decltype(T)>(RecordTypes::ENTER_ARG): |
| 616 | Record.Type = RecordTypes::ENTER_ARG; |
| 617 | State.Expects = FDRState::Token::CALL_ARGUMENT; |
| 618 | break; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 619 | default: |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 620 | return createStringError( |
| 621 | std::make_error_code(std::errc::executable_format_error), |
| 622 | "Illegal function record type '%d' at offset %d.", T, OffsetPtr); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 623 | } |
| 624 | Record.CPU = State.CPUId; |
| 625 | Record.TId = State.ThreadId; |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 626 | Record.PId = State.ProcessId; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 627 | |
| 628 | // Back up one byte to re-read the first byte, which is important for |
| 629 | // computing the function id for a record. |
| 630 | --OffsetPtr; |
| 631 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 632 | // Despite function Id being a signed int on XRayRecord, |
| 633 | // when it is written to an FDR format, the top bits are truncated, |
| 634 | // so it is effectively an unsigned value. When we shift off the |
| 635 | // top four bits, we want the shift to be logical, so we read as |
| 636 | // uint32_t. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 637 | auto PreReadOffset = OffsetPtr; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 638 | uint32_t FuncIdBitField = RecordExtractor.getU32(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 639 | if (OffsetPtr == PreReadOffset) |
| 640 | return createStringError( |
| 641 | std::make_error_code(std::errc::executable_format_error), |
| 642 | "Failed reading truncated function id field at offset %d.", |
| 643 | OffsetPtr); |
| 644 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 645 | Record.FuncId = FuncIdBitField >> 4; |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 646 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 647 | // FunctionRecords have a 32 bit delta from the previous absolute TSC |
| 648 | // or TSC delta. If this would overflow, we should read a TSCWrap record |
| 649 | // with an absolute TSC reading. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 650 | PreReadOffset = OffsetPtr; |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 651 | uint64_t NewTSC = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 652 | if (OffsetPtr == PreReadOffset) |
| 653 | return createStringError( |
| 654 | std::make_error_code(std::errc::executable_format_error), |
| 655 | "Failed reading TSC delta at offset %d.", OffsetPtr); |
| 656 | |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 657 | State.BaseTSC = NewTSC; |
| 658 | Record.TSC = NewTSC; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 659 | } |
| 660 | return Error::success(); |
| 661 | } |
| 662 | |
| 663 | /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is |
| 664 | /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such |
| 665 | /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of |
| 666 | /// of interspersed 16 byte Metadata Records and 8 byte Function Records. |
| 667 | /// |
| 668 | /// The following is an attempt to document the grammar of the format, which is |
| 669 | /// parsed by this function for little-endian machines. Since the format makes |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 670 | /// use of BitFields, when we support big-endian architectures, we will need to |
Simon Pilgrim | 68168d1 | 2017-03-30 12:59:53 +0000 | [diff] [blame] | 671 | /// adjust not only the endianness parameter to llvm's RecordExtractor, but also |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 672 | /// the bit twiddling logic, which is consistent with the little-endian |
| 673 | /// convention that BitFields within a struct will first be packed into the |
| 674 | /// least significant bits the address they belong to. |
| 675 | /// |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 676 | /// We expect a format complying with the grammar in the following pseudo-EBNF |
| 677 | /// in Version 1 of the FDR log. |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 678 | /// |
| 679 | /// FDRLog: XRayFileHeader ThreadBuffer* |
Keith Wyss | 3d0bc9e | 2017-08-02 21:47:27 +0000 | [diff] [blame] | 680 | /// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. |
| 681 | /// Includes BufferSize |
| 682 | /// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 683 | /// BufSize: 8 byte unsigned integer indicating how large the buffer is. |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 684 | /// NewBuffer: 16 byte metadata record with Thread Id. |
| 685 | /// WallClockTime: 16 byte metadata record with human readable time. |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 686 | /// Pid: 16 byte metadata record with Pid |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 687 | /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 688 | /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 689 | /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord |
| 690 | /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. |
| 691 | /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 692 | /// |
| 693 | /// In Version 2, we make the following changes: |
| 694 | /// |
| 695 | /// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId |
| 696 | /// FunctionSequence |
| 697 | /// BufferExtents: 16 byte metdata record describing how many usable bytes are |
| 698 | /// in the buffer. This is measured from the start of the buffer |
| 699 | /// and must always be at least 48 (bytes). |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 700 | /// |
| 701 | /// In Version 3, we make the following changes: |
| 702 | /// |
| 703 | /// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId |
| 704 | /// FunctionSequence |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 705 | /// EOB: *deprecated* |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 706 | Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader, |
| 707 | std::vector<XRayRecord> &Records) { |
Dean Michael Berris | d764c1b | 2018-08-22 07:37:55 +0000 | [diff] [blame^] | 708 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 709 | if (Data.size() < 32) |
| 710 | return make_error<StringError>( |
| 711 | "Not enough bytes for an XRay log.", |
| 712 | std::make_error_code(std::errc::invalid_argument)); |
| 713 | |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 714 | DataExtractor Reader(Data, true, 8); |
| 715 | uint32_t OffsetPtr = 0; |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 716 | |
Dean Michael Berris | d764c1b | 2018-08-22 07:37:55 +0000 | [diff] [blame^] | 717 | auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); |
| 718 | if (!FileHeaderOrError) |
| 719 | return FileHeaderOrError.takeError(); |
| 720 | FileHeader = std::move(FileHeaderOrError.get()); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 721 | |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 722 | uint64_t BufferSize = 0; |
| 723 | { |
| 724 | StringRef ExtraDataRef(FileHeader.FreeFormData, 16); |
| 725 | DataExtractor ExtraDataExtractor(ExtraDataRef, true, 8); |
| 726 | uint32_t ExtraDataOffset = 0; |
| 727 | BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset); |
| 728 | } |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 729 | |
| 730 | FDRState::Token InitialExpectation; |
| 731 | switch (FileHeader.Version) { |
| 732 | case 1: |
| 733 | InitialExpectation = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; |
| 734 | break; |
| 735 | case 2: |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 736 | case 3: |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 737 | InitialExpectation = FDRState::Token::BUFFER_EXTENTS; |
| 738 | break; |
| 739 | default: |
| 740 | return make_error<StringError>( |
| 741 | Twine("Unsupported version '") + Twine(FileHeader.Version) + "'", |
| 742 | std::make_error_code(std::errc::executable_format_error)); |
| 743 | } |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 744 | FDRState State{0, 0, 0, 0, InitialExpectation, BufferSize, 0}; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 745 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 746 | // RecordSize will tell the loop how far to seek ahead based on the record |
| 747 | // type that we have just read. |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 748 | while (Reader.isValidOffset(OffsetPtr)) { |
| 749 | auto BeginOffset = OffsetPtr; |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 750 | if (State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF) { |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 751 | OffsetPtr += State.CurrentBufferSize - State.CurrentBufferConsumed; |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 752 | State.CurrentBufferConsumed = 0; |
| 753 | State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; |
| 754 | continue; |
| 755 | } |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 756 | auto PreReadOffset = OffsetPtr; |
| 757 | uint8_t BitField = Reader.getU8(&OffsetPtr); |
| 758 | if (OffsetPtr == PreReadOffset) |
| 759 | return createStringError( |
| 760 | std::make_error_code(std::errc::executable_format_error), |
| 761 | "Failed reading first byte of record at offset %d.", OffsetPtr); |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 762 | bool isMetadataRecord = BitField & 0x01uL; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 763 | bool isBufferExtents = |
| 764 | (BitField >> 1) == 7; // BufferExtents record kind == 7 |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 765 | if (isMetadataRecord) { |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 766 | if (auto E = processFDRMetadataRecord(State, Reader, OffsetPtr, Records, |
| 767 | FileHeader.Version, BitField)) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 768 | return E; |
| 769 | } else { // Process Function Record |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 770 | if (auto E = processFDRFunctionRecord(State, Reader, OffsetPtr, BitField, |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 771 | Records)) |
| 772 | return E; |
| 773 | } |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 774 | |
| 775 | // The BufferExtents record is technically not part of the buffer, so we |
| 776 | // don't count the size of that record against the buffer's actual size. |
| 777 | if (!isBufferExtents) |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 778 | State.CurrentBufferConsumed += OffsetPtr - BeginOffset; |
| 779 | |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 780 | assert(State.CurrentBufferConsumed <= State.CurrentBufferSize); |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 781 | |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 782 | if ((FileHeader.Version == 2 || FileHeader.Version == 3) && |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 783 | State.CurrentBufferSize == State.CurrentBufferConsumed) { |
| 784 | // In Version 2 of the log, we don't need to scan to the end of the thread |
| 785 | // buffer if we've already consumed all the bytes we need to. |
| 786 | State.Expects = FDRState::Token::BUFFER_EXTENTS; |
| 787 | State.CurrentBufferSize = BufferSize; |
| 788 | State.CurrentBufferConsumed = 0; |
| 789 | } |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 790 | } |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 791 | |
| 792 | // Having iterated over everything we've been given, we've either consumed |
| 793 | // everything and ended up in the end state, or were told to skip the rest. |
| 794 | bool Finished = State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF && |
Dean Michael Berris | 0f84a7d | 2017-09-18 06:08:46 +0000 | [diff] [blame] | 795 | State.CurrentBufferSize == State.CurrentBufferConsumed; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 796 | if ((State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF && |
| 797 | State.Expects != FDRState::Token::BUFFER_EXTENTS) && |
| 798 | !Finished) |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 799 | return make_error<StringError>( |
Dean Michael Berris | 60c2487 | 2017-03-29 06:10:12 +0000 | [diff] [blame] | 800 | Twine("Encountered EOF with unexpected state expectation ") + |
| 801 | fdrStateToTwine(State.Expects) + |
| 802 | ". Remaining expected bytes in thread buffer total " + |
| 803 | Twine(State.CurrentBufferSize - State.CurrentBufferConsumed), |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 804 | std::make_error_code(std::errc::executable_format_error)); |
| 805 | |
| 806 | return Error::success(); |
| 807 | } |
| 808 | |
| 809 | Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader, |
| 810 | std::vector<XRayRecord> &Records) { |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 811 | YAMLXRayTrace Trace; |
| 812 | Input In(Data); |
| 813 | In >> Trace; |
| 814 | if (In.error()) |
| 815 | return make_error<StringError>("Failed loading YAML Data.", In.error()); |
| 816 | |
| 817 | FileHeader.Version = Trace.Header.Version; |
| 818 | FileHeader.Type = Trace.Header.Type; |
| 819 | FileHeader.ConstantTSC = Trace.Header.ConstantTSC; |
| 820 | FileHeader.NonstopTSC = Trace.Header.NonstopTSC; |
| 821 | FileHeader.CycleFrequency = Trace.Header.CycleFrequency; |
| 822 | |
| 823 | if (FileHeader.Version != 1) |
| 824 | return make_error<StringError>( |
| 825 | Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), |
| 826 | std::make_error_code(std::errc::invalid_argument)); |
| 827 | |
| 828 | Records.clear(); |
| 829 | std::transform(Trace.Records.begin(), Trace.Records.end(), |
| 830 | std::back_inserter(Records), [&](const YAMLXRayRecord &R) { |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 831 | return XRayRecord{R.RecordType, R.CPU, R.Type, R.FuncId, |
| 832 | R.TSC, R.TId, R.PId, R.CallArgs}; |
Dean Michael Berris | f8f909f | 2017-01-10 02:38:11 +0000 | [diff] [blame] | 833 | }); |
| 834 | return Error::success(); |
| 835 | } |
Benjamin Kramer | 49a49fe | 2017-08-20 13:03:48 +0000 | [diff] [blame] | 836 | } // namespace |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 837 | |
| 838 | Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { |
| 839 | int Fd; |
| 840 | if (auto EC = sys::fs::openFileForRead(Filename, Fd)) { |
| 841 | return make_error<StringError>( |
| 842 | Twine("Cannot read log from '") + Filename + "'", EC); |
| 843 | } |
| 844 | |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 845 | uint64_t FileSize; |
| 846 | if (auto EC = sys::fs::file_size(Filename, FileSize)) { |
| 847 | return make_error<StringError>( |
| 848 | Twine("Cannot read log from '") + Filename + "'", EC); |
| 849 | } |
| 850 | if (FileSize < 4) { |
| 851 | return make_error<StringError>( |
| 852 | Twine("File '") + Filename + "' too small for XRay.", |
Hans Wennborg | 84da661 | 2017-01-12 18:33:14 +0000 | [diff] [blame] | 853 | std::make_error_code(std::errc::executable_format_error)); |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 854 | } |
| 855 | |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 856 | // Map the opened file into memory and use a StringRef to access it later. |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 857 | std::error_code EC; |
| 858 | sys::fs::mapped_file_region MappedFile( |
| 859 | Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); |
| 860 | if (EC) { |
| 861 | return make_error<StringError>( |
| 862 | Twine("Cannot read log from '") + Filename + "'", EC); |
| 863 | } |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 864 | auto Data = StringRef(MappedFile.data(), MappedFile.size()); |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 865 | |
| 866 | // Attempt to detect the file type using file magic. We have a slight bias |
| 867 | // towards the binary format, and we do this by making sure that the first 4 |
| 868 | // bytes of the binary file is some combination of the following byte |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 869 | // patterns: (observe the code loading them assumes they're little endian) |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 870 | // |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 871 | // 0x01 0x00 0x00 0x00 - version 1, "naive" format |
| 872 | // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 873 | // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 874 | // |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 875 | // YAML files don't typically have those first four bytes as valid text so we |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 876 | // try loading assuming YAML if we don't find these bytes. |
| 877 | // |
| 878 | // Only if we can't load either the binary or the YAML format will we yield an |
| 879 | // error. |
| 880 | StringRef Magic(MappedFile.data(), 4); |
| 881 | DataExtractor HeaderExtractor(Magic, true, 8); |
| 882 | uint32_t OffsetPtr = 0; |
| 883 | uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); |
| 884 | uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); |
| 885 | |
Dean Michael Berris | 4f83c4d | 2017-02-17 01:47:16 +0000 | [diff] [blame] | 886 | enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; |
| 887 | |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 888 | Trace T; |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 889 | switch (Type) { |
| 890 | case NAIVE_FORMAT: |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 891 | if (Version == 1 || Version == 2 || Version == 3) { |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 892 | if (auto E = loadNaiveFormatLog(Data, T.FileHeader, T.Records)) |
| 893 | return std::move(E); |
| 894 | } else { |
| 895 | return make_error<StringError>( |
| 896 | Twine("Unsupported version for Basic/Naive Mode logging: ") + |
| 897 | Twine(Version), |
| 898 | std::make_error_code(std::errc::executable_format_error)); |
| 899 | } |
| 900 | break; |
| 901 | case FLIGHT_DATA_RECORDER_FORMAT: |
Dean Michael Berris | 1014126 | 2018-07-13 05:38:22 +0000 | [diff] [blame] | 902 | if (Version == 1 || Version == 2 || Version == 3) { |
Dean Michael Berris | 6ec7262 | 2017-11-21 07:16:57 +0000 | [diff] [blame] | 903 | if (auto E = loadFDRLog(Data, T.FileHeader, T.Records)) |
| 904 | return std::move(E); |
| 905 | } else { |
| 906 | return make_error<StringError>( |
| 907 | Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), |
| 908 | std::make_error_code(std::errc::executable_format_error)); |
| 909 | } |
| 910 | break; |
| 911 | default: |
Martin Pelikan | d78db15 | 2017-09-15 04:22:16 +0000 | [diff] [blame] | 912 | if (auto E = loadYAMLLog(Data, T.FileHeader, T.Records)) |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 913 | return std::move(E); |
| 914 | } |
| 915 | |
| 916 | if (Sort) |
Mandeep Singh Grang | 28f3d5c | 2017-11-14 18:11:08 +0000 | [diff] [blame] | 917 | std::stable_sort(T.Records.begin(), T.Records.end(), |
Dean Michael Berris | a9d477a | 2018-08-07 04:42:39 +0000 | [diff] [blame] | 918 | [&](const XRayRecord &L, const XRayRecord &R) { |
| 919 | return L.TSC < R.TSC; |
| 920 | }); |
Dean Michael Berris | d6c1865 | 2017-01-11 06:39:09 +0000 | [diff] [blame] | 921 | |
| 922 | return std::move(T); |
| 923 | } |