Index: include/llvm/XRay/XRayRecord.h =================================================================== --- include/llvm/XRay/XRayRecord.h +++ include/llvm/XRay/XRayRecord.h @@ -53,7 +53,7 @@ /// This may or may not correspond to actual record types in the raw trace (as /// the loader implementation may synthesize this information in the process of /// of loading). -enum class RecordTypes { ENTER, EXIT }; +enum class RecordTypes { ENTER, EXIT, ENTER_ARG }; struct XRayRecord { /// The type of record. @@ -73,6 +73,9 @@ /// The thread ID for the currently running thread. uint32_t TId; + + /// The function call argument. + uint64_t CallArg; }; } // namespace xray Index: include/llvm/XRay/YAMLXRayRecord.h =================================================================== --- include/llvm/XRay/YAMLXRayRecord.h +++ include/llvm/XRay/YAMLXRayRecord.h @@ -37,6 +37,7 @@ std::string Function; uint64_t TSC; uint32_t TId; + uint64_t CallArg; }; struct YAMLXRayTrace { @@ -54,6 +55,7 @@ static void enumeration(IO &IO, xray::RecordTypes &Type) { IO.enumCase(Type, "function-enter", xray::RecordTypes::ENTER); IO.enumCase(Type, "function-exit", xray::RecordTypes::EXIT); + IO.enumCase(Type, "function-enter-arg", xray::RecordTypes::ENTER_ARG); } }; @@ -73,6 +75,7 @@ IO.mapRequired("type", Record.RecordType); IO.mapRequired("func-id", Record.FuncId); IO.mapOptional("function", Record.Function); + IO.mapOptional("arg1", Record.CallArg); IO.mapRequired("cpu", Record.CPU); IO.mapRequired("thread", Record.TId); IO.mapRequired("kind", Record.Type); Index: lib/XRay/Trace.cpp =================================================================== --- lib/XRay/Trace.cpp +++ lib/XRay/Trace.cpp @@ -123,6 +123,7 @@ NEW_CPU_ID_RECORD, FUNCTION_SEQUENCE, SCAN_TO_END_OF_THREAD_BUF, + CALL_ARGUMENT, }; Token Expects; // Each threads buffer may have trailing garbage to scan over, so we track our @@ -143,6 +144,8 @@ return "FUNCTION_SEQUENCE"; case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF: return "SCAN_TO_END_OF_THREAD_BUF"; + case FDRState::Token::CALL_ARGUMENT: + return "CALL_ARGUMENT"; } return "UNKNOWN"; } @@ -212,13 +215,34 @@ return Error::success(); } +/// State transition when a CallArgumentRecord is encountered. +Error processFDRCallArgumentRecord(FDRState &State, uint8_t RecordFirstByte, + DataExtractor &RecordExtractor, + std::vector &Records) { + uint32_t OffsetPtr = 1; // Read starting after the first byte. + uint16_t ArgNum = RecordExtractor.getU16(&OffsetPtr); + if (ArgNum != 1) + return make_error( + "CallArgument only supports the first argument.", + std::make_error_code(std::errc::executable_format_error)); + auto &Enter = Records.back(); + if (Enter.Type != RecordTypes::ENTER) + return make_error( + "CallArgument needs to be right after a function entry", + std::make_error_code(std::errc::executable_format_error)); + Enter.Type = RecordTypes::ENTER_ARG; + Enter.CallArg = RecordExtractor.getU64(&OffsetPtr); + return Error::success(); +} + /// Advances the state machine for reading the FDR record type by reading one /// Metadata Record and updating the State appropriately based on the kind of /// record encountered. The RecordKind is encoded in the first byte of the /// Record, which the caller should pass in because they have already read it /// to determine that this is a metadata record as opposed to a function record. Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte, - DataExtractor &RecordExtractor) { + DataExtractor &RecordExtractor, + std::vector &Records) { // The remaining 7 bits are the RecordKind enum. uint8_t RecordKind = RecordFirstByte >> 1; switch (RecordKind) { @@ -247,6 +271,12 @@ processFDRWallTimeRecord(State, RecordFirstByte, RecordExtractor)) return E; break; + case 5: // CallArgument + if (auto E = + processFDRCallArgumentRecord(State, RecordFirstByte, + RecordExtractor, Records)) + return E; + break; default: // Widen the record type to uint16_t to prevent conversion to char. return make_error( @@ -375,7 +405,7 @@ uint32_t ExtraDataOffset = 0; BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset); } - FDRState State{0, 0, 0, FDRState::Token::NEW_BUFFER_RECORD_OR_EOF, + FDRState State{0, 0, 0, FDRState::Token::NEW_BUFFER_RECORD_OR_EOF, BufferSize, 0}; // RecordSize will tell the loop how far to seek ahead based on the record // type that we have just read. @@ -400,7 +430,8 @@ bool isMetadataRecord = BitField & 0x01uL; if (isMetadataRecord) { RecordSize = 16; - if (auto E = processFDRMetadataRecord(State, BitField, RecordExtractor)) + if (auto E = processFDRMetadataRecord(State, BitField, RecordExtractor, + Records)) return E; State.CurrentBufferConsumed += RecordSize; } else { // Process Function Record Index: tools/llvm-xray/xray-account.cc =================================================================== --- tools/llvm-xray/xray-account.cc +++ tools/llvm-xray/xray-account.cc @@ -146,7 +146,8 @@ auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { - case RecordTypes::ENTER: { + case RecordTypes::ENTER: + case RecordTypes::ENTER_ARG: { // Function Enter ThreadStack.emplace_back(Record.FuncId, Record.TSC); break; Index: tools/llvm-xray/xray-converter.cc =================================================================== --- tools/llvm-xray/xray-converter.cc +++ tools/llvm-xray/xray-converter.cc @@ -86,7 +86,7 @@ Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId, Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) : llvm::to_string(R.FuncId), - R.TSC, R.TId}); + R.TSC, R.TId, R.CallArg}); } Output Out(OS, nullptr, 0); Out << Trace; @@ -123,6 +123,7 @@ Writer.write(static_cast(R.CPU)); switch (R.Type) { case RecordTypes::ENTER: + case RecordTypes::ENTER_ARG: Writer.write(uint8_t{0}); break; case RecordTypes::EXIT: Index: tools/llvm-xray/xray-graph.cc =================================================================== --- tools/llvm-xray/xray-graph.cc +++ tools/llvm-xray/xray-graph.cc @@ -208,7 +208,8 @@ auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { - case RecordTypes::ENTER: { + case RecordTypes::ENTER: + case RecordTypes::ENTER_ARG: { if (Record.FuncId != 0 && G.count(Record.FuncId) == 0) G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId); ThreadStack.push_back({Record.FuncId, Record.TSC});