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 arguments. + std::vector CallArgs; }; } // 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; + std::vector CallArgs; }; 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("args", Record.CallArgs); IO.mapRequired("cpu", Record.CPU); IO.mapRequired("thread", Record.TId); IO.mapRequired("kind", Record.Type); @@ -82,6 +85,16 @@ static constexpr bool flow = true; }; +template <> struct SequenceTraits> { + static constexpr bool flow = true; + static size_t size(IO &IO, std::vector &V) { return V.size(); } + static uint64_t &element(IO &IO, std::vector &V, size_t Index) { + if (Index >= V.size()) + V.resize(Index + 1); + return V[Index]; + } +}; + template <> struct MappingTraits { static void mapping(IO &IO, xray::YAMLXRayTrace &Trace) { // A trace file contains two parts, the header and the list of all the Index: lib/XRay/Trace.cpp =================================================================== --- lib/XRay/Trace.cpp +++ lib/XRay/Trace.cpp @@ -125,6 +125,7 @@ FUNCTION_SEQUENCE, SCAN_TO_END_OF_THREAD_BUF, CUSTOM_EVENT_DATA, + CALL_ARGUMENT, }; Token Expects; @@ -148,6 +149,8 @@ return "SCAN_TO_END_OF_THREAD_BUF"; case FDRState::Token::CUSTOM_EVENT_DATA: return "CUSTOM_EVENT_DATA"; + case FDRState::Token::CALL_ARGUMENT: + return "CALL_ARGUMENT"; } return "UNKNOWN"; } @@ -235,6 +238,22 @@ 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. + 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.CallArgs.emplace_back(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 @@ -242,7 +261,8 @@ /// to determine that this is a metadata record as opposed to a function record. Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte, DataExtractor &RecordExtractor, - size_t &RecordSize) { + size_t &RecordSize, + std::vector &Records) { // The remaining 7 bits are the RecordKind enum. uint8_t RecordKind = RecordFirstByte >> 1; switch (RecordKind) { @@ -276,6 +296,11 @@ RecordExtractor, RecordSize)) return E; break; + case 6: // 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( @@ -430,7 +455,7 @@ if (isMetadataRecord) { RecordSize = 16; if (auto E = processFDRMetadataRecord(State, BitField, RecordExtractor, - RecordSize)) + RecordSize, 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.CallArgs}); } 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});