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