diff options
| author | vvvv <[email protected]> | 2024-02-06 20:01:22 +0300 |
|---|---|---|
| committer | vvvv <[email protected]> | 2024-02-06 20:22:16 +0300 |
| commit | 0203b7a9a40828bb2bd4c32029b79ff0ea3d1f8f (patch) | |
| tree | e630d0d5bd0bd29fc8c2d2842ed2cfde781b993a /contrib/libs/llvm16/lib/XRay | |
| parent | ba27db76d99d12a4f1c06960b5449423218614c4 (diff) | |
llvm16 targets
Diffstat (limited to 'contrib/libs/llvm16/lib/XRay')
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/BlockIndexer.cpp | 97 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/BlockPrinter.cpp | 113 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/BlockVerifier.cpp | 204 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/FDRRecordProducer.cpp | 198 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/FDRRecords.cpp | 66 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/FDRTraceExpander.cpp | 131 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/FDRTraceWriter.cpp | 151 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/FileHeaderReader.cpp | 73 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/InstrumentationMap.cpp | 291 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/LogBuilderConsumer.cpp | 37 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/Profile.cpp | 403 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/RecordInitializer.cpp | 431 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/RecordPrinter.cpp | 108 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/Trace.cpp | 475 | ||||
| -rw-r--r-- | contrib/libs/llvm16/lib/XRay/ya.make | 41 |
15 files changed, 2819 insertions, 0 deletions
diff --git a/contrib/libs/llvm16/lib/XRay/BlockIndexer.cpp b/contrib/libs/llvm16/lib/XRay/BlockIndexer.cpp new file mode 100644 index 00000000000..a99a6815f0d --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/BlockIndexer.cpp @@ -0,0 +1,97 @@ +//===- BlockIndexer.cpp - FDR Block Indexing VIsitor ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// An implementation of the RecordVisitor which generates a mapping between a +// thread and a range of records representing a block. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockIndexer.h" + +namespace llvm { +namespace xray { + +Error BlockIndexer::visit(BufferExtents &) { return Error::success(); } + +Error BlockIndexer::visit(WallclockRecord &R) { + CurrentBlock.Records.push_back(&R); + CurrentBlock.WallclockTime = &R; + return Error::success(); +} + +Error BlockIndexer::visit(NewCPUIDRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(TSCWrapRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(CustomEventRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(CustomEventRecordV5 &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(TypedEventRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(CallArgRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(PIDRecord &R) { + CurrentBlock.ProcessID = R.pid(); + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(NewBufferRecord &R) { + if (!CurrentBlock.Records.empty()) + if (auto E = flush()) + return E; + + CurrentBlock.ThreadID = R.tid(); + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(EndBufferRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::visit(FunctionRecord &R) { + CurrentBlock.Records.push_back(&R); + return Error::success(); +} + +Error BlockIndexer::flush() { + Index::iterator It; + std::tie(It, std::ignore) = + Indices.insert({{CurrentBlock.ProcessID, CurrentBlock.ThreadID}, {}}); + It->second.push_back({CurrentBlock.ProcessID, CurrentBlock.ThreadID, + CurrentBlock.WallclockTime, + std::move(CurrentBlock.Records)}); + CurrentBlock.ProcessID = 0; + CurrentBlock.ThreadID = 0; + CurrentBlock.Records = {}; + CurrentBlock.WallclockTime = nullptr; + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/BlockPrinter.cpp b/contrib/libs/llvm16/lib/XRay/BlockPrinter.cpp new file mode 100644 index 00000000000..63a60c3c56a --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/BlockPrinter.cpp @@ -0,0 +1,113 @@ +//===- BlockPrinter.cpp - FDR Block Pretty Printer Implementation --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockPrinter.h" + +namespace llvm { +namespace xray { + +Error BlockPrinter::visit(BufferExtents &R) { + OS << "\n[New Block]\n"; + CurrentState = State::Preamble; + return RP.visit(R); +} + +// Preamble printing. +Error BlockPrinter::visit(NewBufferRecord &R) { + if (CurrentState == State::Start) + OS << "\n[New Block]\n"; + + OS << "Preamble: \n"; + CurrentState = State::Preamble; + return RP.visit(R); +} + +Error BlockPrinter::visit(WallclockRecord &R) { + CurrentState = State::Preamble; + return RP.visit(R); +} + +Error BlockPrinter::visit(PIDRecord &R) { + CurrentState = State::Preamble; + return RP.visit(R); +} + +// Metadata printing. +Error BlockPrinter::visit(NewCPUIDRecord &R) { + if (CurrentState == State::Preamble) + OS << "\nBody:\n"; + if (CurrentState == State::Function) + OS << "\nMetadata: "; + CurrentState = State::Metadata; + OS << " "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(TSCWrapRecord &R) { + if (CurrentState == State::Function) + OS << "\nMetadata:"; + CurrentState = State::Metadata; + OS << " "; + auto E = RP.visit(R); + return E; +} + +// Custom events will be rendered like "function" events. +Error BlockPrinter::visit(CustomEventRecord &R) { + if (CurrentState == State::Metadata) + OS << "\n"; + CurrentState = State::CustomEvent; + OS << "* "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(CustomEventRecordV5 &R) { + if (CurrentState == State::Metadata) + OS << "\n"; + CurrentState = State::CustomEvent; + OS << "* "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(TypedEventRecord &R) { + if (CurrentState == State::Metadata) + OS << "\n"; + CurrentState = State::CustomEvent; + OS << "* "; + auto E = RP.visit(R); + return E; +} + +// Function call printing. +Error BlockPrinter::visit(FunctionRecord &R) { + if (CurrentState == State::Metadata) + OS << "\n"; + CurrentState = State::Function; + OS << "- "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(CallArgRecord &R) { + CurrentState = State::Arg; + OS << " : "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(EndBufferRecord &R) { + CurrentState = State::End; + OS << " *** "; + auto E = RP.visit(R); + return E; +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/BlockVerifier.cpp b/contrib/libs/llvm16/lib/XRay/BlockVerifier.cpp new file mode 100644 index 00000000000..9fb49fa9a86 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/BlockVerifier.cpp @@ -0,0 +1,204 @@ +//===- BlockVerifier.cpp - FDR Block Verifier -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockVerifier.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace xray { +namespace { + +constexpr unsigned long long mask(BlockVerifier::State S) { + return 1uLL << static_cast<std::size_t>(S); +} + +constexpr std::size_t number(BlockVerifier::State S) { + return static_cast<std::size_t>(S); +} + +StringRef recordToString(BlockVerifier::State R) { + switch (R) { + case BlockVerifier::State::BufferExtents: + return "BufferExtents"; + case BlockVerifier::State::NewBuffer: + return "NewBuffer"; + case BlockVerifier::State::WallClockTime: + return "WallClockTime"; + case BlockVerifier::State::PIDEntry: + return "PIDEntry"; + case BlockVerifier::State::NewCPUId: + return "NewCPUId"; + case BlockVerifier::State::TSCWrap: + return "TSCWrap"; + case BlockVerifier::State::CustomEvent: + return "CustomEvent"; + case BlockVerifier::State::Function: + return "Function"; + case BlockVerifier::State::CallArg: + return "CallArg"; + case BlockVerifier::State::EndOfBuffer: + return "EndOfBuffer"; + case BlockVerifier::State::TypedEvent: + return "TypedEvent"; + case BlockVerifier::State::StateMax: + case BlockVerifier::State::Unknown: + return "Unknown"; + } + llvm_unreachable("Unkown state!"); +} + +struct Transition { + BlockVerifier::State From; + std::bitset<number(BlockVerifier::State::StateMax)> ToStates; +}; + +} // namespace + +Error BlockVerifier::transition(State To) { + using ToSet = std::bitset<number(State::StateMax)>; + static constexpr std::array<const Transition, number(State::StateMax)> + TransitionTable{{{State::Unknown, + {mask(State::BufferExtents) | mask(State::NewBuffer)}}, + + {State::BufferExtents, {mask(State::NewBuffer)}}, + + {State::NewBuffer, {mask(State::WallClockTime)}}, + + {State::WallClockTime, + {mask(State::PIDEntry) | mask(State::NewCPUId)}}, + + {State::PIDEntry, {mask(State::NewCPUId)}}, + + {State::NewCPUId, + {mask(State::NewCPUId) | mask(State::TSCWrap) | + mask(State::CustomEvent) | mask(State::Function) | + mask(State::EndOfBuffer) | mask(State::TypedEvent)}}, + + {State::TSCWrap, + {mask(State::TSCWrap) | mask(State::NewCPUId) | + mask(State::CustomEvent) | mask(State::Function) | + mask(State::EndOfBuffer) | mask(State::TypedEvent)}}, + + {State::CustomEvent, + {mask(State::CustomEvent) | mask(State::TSCWrap) | + mask(State::NewCPUId) | mask(State::Function) | + mask(State::EndOfBuffer) | mask(State::TypedEvent)}}, + + {State::TypedEvent, + {mask(State::TypedEvent) | mask(State::TSCWrap) | + mask(State::NewCPUId) | mask(State::Function) | + mask(State::EndOfBuffer) | mask(State::CustomEvent)}}, + + {State::Function, + {mask(State::Function) | mask(State::TSCWrap) | + mask(State::NewCPUId) | mask(State::CustomEvent) | + mask(State::CallArg) | mask(State::EndOfBuffer) | + mask(State::TypedEvent)}}, + + {State::CallArg, + {mask(State::CallArg) | mask(State::Function) | + mask(State::TSCWrap) | mask(State::NewCPUId) | + mask(State::CustomEvent) | mask(State::EndOfBuffer) | + mask(State::TypedEvent)}}, + + {State::EndOfBuffer, {}}}}; + + if (CurrentRecord >= State::StateMax) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "BUG (BlockVerifier): Cannot find transition table entry for %s, " + "transitioning to %s.", + recordToString(CurrentRecord).data(), recordToString(To).data()); + + // If we're at an EndOfBuffer record, we ignore anything that follows that + // isn't a NewBuffer record. + if (CurrentRecord == State::EndOfBuffer && To != State::NewBuffer) + return Error::success(); + + auto &Mapping = TransitionTable[number(CurrentRecord)]; + auto &Destinations = Mapping.ToStates; + assert(Mapping.From == CurrentRecord && + "BUG: Wrong index for record mapping."); + if ((Destinations & ToSet(mask(To))) == 0) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "BlockVerifier: Invalid transition from %s to %s.", + recordToString(CurrentRecord).data(), recordToString(To).data()); + + CurrentRecord = To; + return Error::success(); +} // namespace xray + +Error BlockVerifier::visit(BufferExtents &) { + return transition(State::BufferExtents); +} + +Error BlockVerifier::visit(WallclockRecord &) { + return transition(State::WallClockTime); +} + +Error BlockVerifier::visit(NewCPUIDRecord &) { + return transition(State::NewCPUId); +} + +Error BlockVerifier::visit(TSCWrapRecord &) { + return transition(State::TSCWrap); +} + +Error BlockVerifier::visit(CustomEventRecord &) { + return transition(State::CustomEvent); +} + +Error BlockVerifier::visit(CustomEventRecordV5 &) { + return transition(State::CustomEvent); +} + +Error BlockVerifier::visit(TypedEventRecord &) { + return transition(State::TypedEvent); +} + +Error BlockVerifier::visit(CallArgRecord &) { + return transition(State::CallArg); +} + +Error BlockVerifier::visit(PIDRecord &) { return transition(State::PIDEntry); } + +Error BlockVerifier::visit(NewBufferRecord &) { + return transition(State::NewBuffer); +} + +Error BlockVerifier::visit(EndBufferRecord &) { + return transition(State::EndOfBuffer); +} + +Error BlockVerifier::visit(FunctionRecord &) { + return transition(State::Function); +} + +Error BlockVerifier::verify() { + // The known terminal conditions are the following: + switch (CurrentRecord) { + case State::EndOfBuffer: + case State::NewCPUId: + case State::CustomEvent: + case State::TypedEvent: + case State::Function: + case State::CallArg: + case State::TSCWrap: + return Error::success(); + default: + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "BlockVerifier: Invalid terminal condition %s, malformed block.", + recordToString(CurrentRecord).data()); + } +} + +void BlockVerifier::reset() { CurrentRecord = State::Unknown; } + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/FDRRecordProducer.cpp b/contrib/libs/llvm16/lib/XRay/FDRRecordProducer.cpp new file mode 100644 index 00000000000..479b710444b --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/FDRRecordProducer.cpp @@ -0,0 +1,198 @@ +//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRRecordProducer.h" +#include "llvm/Support/DataExtractor.h" + +#include <cstdint> + +namespace llvm { +namespace xray { + +namespace { + +// Keep this in sync with the values written in the XRay FDR mode runtime in +// compiler-rt. +enum MetadataRecordKinds : uint8_t { + NewBufferKind, + EndOfBufferKind, + NewCPUIdKind, + TSCWrapKind, + WalltimeMarkerKind, + CustomEventMarkerKind, + CallArgumentKind, + BufferExtentsKind, + TypedEventMarkerKind, + PidKind, + // This is an end marker, used to identify the upper bound for this enum. + EnumEndMarker, +}; + +Expected<std::unique_ptr<Record>> +metadataRecordType(const XRayFileHeader &Header, uint8_t T) { + + if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker)) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Invalid metadata record type: %d", T); + switch (T) { + case MetadataRecordKinds::NewBufferKind: + return std::make_unique<NewBufferRecord>(); + case MetadataRecordKinds::EndOfBufferKind: + if (Header.Version >= 2) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "End of buffer records are no longer supported starting version " + "2 of the log."); + return std::make_unique<EndBufferRecord>(); + case MetadataRecordKinds::NewCPUIdKind: + return std::make_unique<NewCPUIDRecord>(); + case MetadataRecordKinds::TSCWrapKind: + return std::make_unique<TSCWrapRecord>(); + case MetadataRecordKinds::WalltimeMarkerKind: + return std::make_unique<WallclockRecord>(); + case MetadataRecordKinds::CustomEventMarkerKind: + if (Header.Version >= 5) + return std::make_unique<CustomEventRecordV5>(); + return std::make_unique<CustomEventRecord>(); + case MetadataRecordKinds::CallArgumentKind: + return std::make_unique<CallArgRecord>(); + case MetadataRecordKinds::BufferExtentsKind: + return std::make_unique<BufferExtents>(); + case MetadataRecordKinds::TypedEventMarkerKind: + return std::make_unique<TypedEventRecord>(); + case MetadataRecordKinds::PidKind: + return std::make_unique<PIDRecord>(); + case MetadataRecordKinds::EnumEndMarker: + llvm_unreachable("Invalid MetadataRecordKind"); + } + llvm_unreachable("Unhandled MetadataRecordKinds enum value"); +} + +constexpr bool isMetadataIntroducer(uint8_t FirstByte) { + return FirstByte & 0x01u; +} + +} // namespace + +Expected<std::unique_ptr<Record>> +FileBasedRecordProducer::findNextBufferExtent() { + // We seek one byte at a time until we find a suitable buffer extents metadata + // record introducer. + std::unique_ptr<Record> R; + while (!R) { + auto PreReadOffset = OffsetPtr; + uint8_t FirstByte = E.getU8(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading one byte from offset %" PRId64 ".", OffsetPtr); + + if (isMetadataIntroducer(FirstByte)) { + auto LoadedType = FirstByte >> 1; + if (LoadedType == MetadataRecordKinds::BufferExtentsKind) { + auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType); + if (!MetadataRecordOrErr) + return MetadataRecordOrErr.takeError(); + + R = std::move(MetadataRecordOrErr.get()); + RecordInitializer RI(E, OffsetPtr); + if (auto Err = R->apply(RI)) + return std::move(Err); + return std::move(R); + } + } + } + llvm_unreachable("Must always terminate with either an error or a record."); +} + +Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() { + // First, we set up our result record. + std::unique_ptr<Record> R; + + // Before we do any further reading, we should check whether we're at the end + // of the current buffer we're been consuming. In FDR logs version >= 3, we + // rely on the buffer extents record to determine how many bytes we should be + // considering as valid records. + if (Header.Version >= 3 && CurrentBufferBytes == 0) { + // Find the next buffer extents record. + auto BufferExtentsOrError = findNextBufferExtent(); + if (!BufferExtentsOrError) + return joinErrors( + BufferExtentsOrError.takeError(), + createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed to find the next BufferExtents record.")); + + R = std::move(BufferExtentsOrError.get()); + assert(R != nullptr); + assert(isa<BufferExtents>(R.get())); + auto BE = cast<BufferExtents>(R.get()); + CurrentBufferBytes = BE->size(); + return std::move(R); + } + + // + // At the top level, we read one byte to determine the type of the record to + // create. This byte will comprise of the following bits: + // + // - offset 0: A '1' indicates a metadata record, a '0' indicates a function + // record. + // - offsets 1-7: For metadata records, this will indicate the kind of + // metadata record should be loaded. + // + // We read first byte, then create the appropriate type of record to consume + // the rest of the bytes. + auto PreReadOffset = OffsetPtr; + uint8_t FirstByte = E.getU8(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading one byte from offset %" PRId64 ".", OffsetPtr); + + // For metadata records, handle especially here. + if (isMetadataIntroducer(FirstByte)) { + auto LoadedType = FirstByte >> 1; + auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType); + if (!MetadataRecordOrErr) + return joinErrors( + MetadataRecordOrErr.takeError(), + createStringError( + std::make_error_code(std::errc::executable_format_error), + "Encountered an unsupported metadata record (%d) " + "at offset %" PRId64 ".", + LoadedType, PreReadOffset)); + R = std::move(MetadataRecordOrErr.get()); + } else { + R = std::make_unique<FunctionRecord>(); + } + RecordInitializer RI(E, OffsetPtr); + + if (auto Err = R->apply(RI)) + return std::move(Err); + + // If we encountered a BufferExtents record, we should record the remaining + // bytes for the current buffer, to determine when we should start ignoring + // potentially malformed data and looking for buffer extents records. + if (auto BE = dyn_cast<BufferExtents>(R.get())) { + CurrentBufferBytes = BE->size(); + } else if (Header.Version >= 3) { + if (OffsetPtr - PreReadOffset > CurrentBufferBytes) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Buffer over-read at offset %" PRId64 " (over-read by %" PRId64 + " bytes); Record Type = %s.", + OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes, + Record::kindToString(R->getRecordType()).data()); + + CurrentBufferBytes -= OffsetPtr - PreReadOffset; + } + assert(R != nullptr); + return std::move(R); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/FDRRecords.cpp b/contrib/libs/llvm16/lib/XRay/FDRRecords.cpp new file mode 100644 index 00000000000..ff315d35417 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/FDRRecords.cpp @@ -0,0 +1,66 @@ +//===- FDRRecords.cpp - XRay Flight Data Recorder Mode Records -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Define types and operations on these types that represent the different kinds +// of records we encounter in XRay flight data recorder mode traces. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRRecords.h" + +namespace llvm { +namespace xray { + +Error BufferExtents::apply(RecordVisitor &V) { return V.visit(*this); } +Error WallclockRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error NewCPUIDRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error TSCWrapRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error CustomEventRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error CallArgRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error PIDRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error NewBufferRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error EndBufferRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error FunctionRecord::apply(RecordVisitor &V) { return V.visit(*this); } +Error CustomEventRecordV5::apply(RecordVisitor &V) { return V.visit(*this); } +Error TypedEventRecord::apply(RecordVisitor &V) { return V.visit(*this); } + +StringRef Record::kindToString(RecordKind K) { + switch (K) { + case RecordKind::RK_Metadata: + return "Metadata"; + case RecordKind::RK_Metadata_BufferExtents: + return "Metadata:BufferExtents"; + case RecordKind::RK_Metadata_WallClockTime: + return "Metadata:WallClockTime"; + case RecordKind::RK_Metadata_NewCPUId: + return "Metadata:NewCPUId"; + case RecordKind::RK_Metadata_TSCWrap: + return "Metadata:TSCWrap"; + case RecordKind::RK_Metadata_CustomEvent: + return "Metadata:CustomEvent"; + case RecordKind::RK_Metadata_CustomEventV5: + return "Metadata:CustomEventV5"; + case RecordKind::RK_Metadata_CallArg: + return "Metadata:CallArg"; + case RecordKind::RK_Metadata_PIDEntry: + return "Metadata:PIDEntry"; + case RecordKind::RK_Metadata_NewBuffer: + return "Metadata:NewBuffer"; + case RecordKind::RK_Metadata_EndOfBuffer: + return "Metadata:EndOfBuffer"; + case RecordKind::RK_Metadata_TypedEvent: + return "Metadata:TypedEvent"; + case RecordKind::RK_Metadata_LastMetadata: + return "Metadata:LastMetadata"; + case RecordKind::RK_Function: + return "Function"; + } + return "Unknown"; +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/FDRTraceExpander.cpp b/contrib/libs/llvm16/lib/XRay/FDRTraceExpander.cpp new file mode 100644 index 00000000000..b68e997fe70 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/FDRTraceExpander.cpp @@ -0,0 +1,131 @@ +//===- FDRTraceExpander.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRTraceExpander.h" + +namespace llvm { +namespace xray { + +void TraceExpander::resetCurrentRecord() { + if (BuildingRecord) + C(CurrentRecord); + BuildingRecord = false; + CurrentRecord.CallArgs.clear(); + CurrentRecord.Data.clear(); +} + +Error TraceExpander::visit(BufferExtents &) { + resetCurrentRecord(); + return Error::success(); +} + +Error TraceExpander::visit(WallclockRecord &) { return Error::success(); } + +Error TraceExpander::visit(NewCPUIDRecord &R) { + CPUId = R.cpuid(); + BaseTSC = R.tsc(); + return Error::success(); +} + +Error TraceExpander::visit(TSCWrapRecord &R) { + BaseTSC = R.tsc(); + return Error::success(); +} + +Error TraceExpander::visit(CustomEventRecord &R) { + resetCurrentRecord(); + if (!IgnoringRecords) { + CurrentRecord.TSC = R.tsc(); + CurrentRecord.CPU = R.cpu(); + CurrentRecord.PId = PID; + CurrentRecord.TId = TID; + CurrentRecord.Type = RecordTypes::CUSTOM_EVENT; + CurrentRecord.Data = std::string(R.data()); + BuildingRecord = true; + } + return Error::success(); +} + +Error TraceExpander::visit(CustomEventRecordV5 &R) { + resetCurrentRecord(); + if (!IgnoringRecords) { + BaseTSC += R.delta(); + CurrentRecord.TSC = BaseTSC; + CurrentRecord.CPU = CPUId; + CurrentRecord.PId = PID; + CurrentRecord.TId = TID; + CurrentRecord.Type = RecordTypes::CUSTOM_EVENT; + CurrentRecord.Data = std::string(R.data()); + BuildingRecord = true; + } + return Error::success(); +} + +Error TraceExpander::visit(TypedEventRecord &R) { + resetCurrentRecord(); + if (!IgnoringRecords) { + BaseTSC += R.delta(); + CurrentRecord.TSC = BaseTSC; + CurrentRecord.CPU = CPUId; + CurrentRecord.PId = PID; + CurrentRecord.TId = TID; + CurrentRecord.RecordType = R.eventType(); + CurrentRecord.Type = RecordTypes::TYPED_EVENT; + CurrentRecord.Data = std::string(R.data()); + BuildingRecord = true; + } + return Error::success(); +} + +Error TraceExpander::visit(CallArgRecord &R) { + CurrentRecord.CallArgs.push_back(R.arg()); + CurrentRecord.Type = RecordTypes::ENTER_ARG; + return Error::success(); +} + +Error TraceExpander::visit(PIDRecord &R) { + PID = R.pid(); + return Error::success(); +} + +Error TraceExpander::visit(NewBufferRecord &R) { + if (IgnoringRecords) + IgnoringRecords = false; + TID = R.tid(); + if (LogVersion == 2) + PID = R.tid(); + return Error::success(); +} + +Error TraceExpander::visit(EndBufferRecord &) { + IgnoringRecords = true; + resetCurrentRecord(); + return Error::success(); +} + +Error TraceExpander::visit(FunctionRecord &R) { + resetCurrentRecord(); + if (!IgnoringRecords) { + BaseTSC += R.delta(); + CurrentRecord.Type = R.recordType(); + CurrentRecord.FuncId = R.functionId(); + CurrentRecord.TSC = BaseTSC; + CurrentRecord.PId = PID; + CurrentRecord.TId = TID; + CurrentRecord.CPU = CPUId; + BuildingRecord = true; + } + return Error::success(); +} + +Error TraceExpander::flush() { + resetCurrentRecord(); + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/FDRTraceWriter.cpp b/contrib/libs/llvm16/lib/XRay/FDRTraceWriter.cpp new file mode 100644 index 00000000000..2b80740ed43 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/FDRTraceWriter.cpp @@ -0,0 +1,151 @@ +//===- FDRTraceWriter.cpp - XRay FDR Trace Writer ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Test a utility that can write out XRay FDR Mode formatted trace files. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRTraceWriter.h" +#include <tuple> + +namespace llvm { +namespace xray { + +namespace { + +template <size_t Index> struct IndexedWriter { + template < + class Tuple, + std::enable_if_t<(Index < + std::tuple_size<std::remove_reference_t<Tuple>>::value), + int> = 0> + static size_t write(support::endian::Writer &OS, Tuple &&T) { + OS.write(std::get<Index>(T)); + return sizeof(std::get<Index>(T)) + IndexedWriter<Index + 1>::write(OS, T); + } + + template < + class Tuple, + std::enable_if_t<(Index >= + std::tuple_size<std::remove_reference_t<Tuple>>::value), + int> = 0> + static size_t write(support::endian::Writer &OS, Tuple &&) { + return 0; + } +}; + +template <uint8_t Kind, class... Values> +Error writeMetadata(support::endian::Writer &OS, Values &&... Ds) { + // The first bit in the first byte of metadata records is always set to 1, so + // we ensure this is the case when we write out the first byte of the record. + uint8_t FirstByte = (static_cast<uint8_t>(Kind) << 1) | uint8_t{0x01u}; + auto T = std::make_tuple(std::forward<Values>(std::move(Ds))...); + // Write in field order. + OS.write(FirstByte); + auto Bytes = IndexedWriter<0>::write(OS, T); + assert(Bytes <= 15 && "Must only ever write at most 16 byte metadata!"); + // Pad out with appropriate numbers of zero's. + for (; Bytes < 15; ++Bytes) + OS.write('\0'); + return Error::success(); +} + +} // namespace + +FDRTraceWriter::FDRTraceWriter(raw_ostream &O, const XRayFileHeader &H) + : OS(O, support::endianness::native) { + // We need to re-construct a header, by writing the fields we care about for + // traces, in the format that the runtime would have written. + uint32_t BitField = + (H.ConstantTSC ? 0x01 : 0x0) | (H.NonstopTSC ? 0x02 : 0x0); + + // For endian-correctness, we need to write these fields in the order they + // appear and that we expect, instead of blasting bytes of the struct through. + OS.write(H.Version); + OS.write(H.Type); + OS.write(BitField); + OS.write(H.CycleFrequency); + ArrayRef<char> FreeFormBytes(H.FreeFormData, + sizeof(XRayFileHeader::FreeFormData)); + OS.write(FreeFormBytes); +} + +FDRTraceWriter::~FDRTraceWriter() = default; + +Error FDRTraceWriter::visit(BufferExtents &R) { + return writeMetadata<7u>(OS, R.size()); +} + +Error FDRTraceWriter::visit(WallclockRecord &R) { + return writeMetadata<4u>(OS, R.seconds(), R.nanos()); +} + +Error FDRTraceWriter::visit(NewCPUIDRecord &R) { + return writeMetadata<2u>(OS, R.cpuid(), R.tsc()); +} + +Error FDRTraceWriter::visit(TSCWrapRecord &R) { + return writeMetadata<3u>(OS, R.tsc()); +} + +Error FDRTraceWriter::visit(CustomEventRecord &R) { + if (auto E = writeMetadata<5u>(OS, R.size(), R.tsc(), R.cpu())) + return E; + auto D = R.data(); + ArrayRef<char> Bytes(D.data(), D.size()); + OS.write(Bytes); + return Error::success(); +} + +Error FDRTraceWriter::visit(CustomEventRecordV5 &R) { + if (auto E = writeMetadata<5u>(OS, R.size(), R.delta())) + return E; + auto D = R.data(); + ArrayRef<char> Bytes(D.data(), D.size()); + OS.write(Bytes); + return Error::success(); +} + +Error FDRTraceWriter::visit(TypedEventRecord &R) { + if (auto E = writeMetadata<8u>(OS, R.size(), R.delta(), R.eventType())) + return E; + auto D = R.data(); + ArrayRef<char> Bytes(D.data(), D.size()); + OS.write(Bytes); + return Error::success(); +} + +Error FDRTraceWriter::visit(CallArgRecord &R) { + return writeMetadata<6u>(OS, R.arg()); +} + +Error FDRTraceWriter::visit(PIDRecord &R) { + return writeMetadata<9u>(OS, R.pid()); +} + +Error FDRTraceWriter::visit(NewBufferRecord &R) { + return writeMetadata<0u>(OS, R.tid()); +} + +Error FDRTraceWriter::visit(EndBufferRecord &R) { + return writeMetadata<1u>(OS, 0); +} + +Error FDRTraceWriter::visit(FunctionRecord &R) { + // Write out the data in "field" order, to be endian-aware. + uint32_t TypeRecordFuncId = uint32_t{R.functionId() & ~uint32_t{0x0Fu << 28}}; + TypeRecordFuncId <<= 3; + TypeRecordFuncId |= static_cast<uint32_t>(R.recordType()); + TypeRecordFuncId <<= 1; + TypeRecordFuncId &= ~uint32_t{0x01}; + OS.write(TypeRecordFuncId); + OS.write(R.delta()); + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/FileHeaderReader.cpp b/contrib/libs/llvm16/lib/XRay/FileHeaderReader.cpp new file mode 100644 index 00000000000..6b6daf9deba --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/FileHeaderReader.cpp @@ -0,0 +1,73 @@ +//===- FileHeaderReader.cpp - XRay File Header Reader --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FileHeaderReader.h" + +namespace llvm { +namespace xray { + +// Populates the FileHeader reference by reading the first 32 bytes of the file. +Expected<XRayFileHeader> readBinaryFormatHeader(DataExtractor &HeaderExtractor, + uint64_t &OffsetPtr) { + // FIXME: Maybe deduce whether the data is little or big-endian using some + // magic bytes in the beginning of the file? + + // First 32 bytes of the file will always be the header. We assume a certain + // format here: + // + // (2) uint16 : version + // (2) uint16 : type + // (4) uint32 : bitfield + // (8) uint64 : cycle frequency + // (16) - : padding + XRayFileHeader FileHeader; + auto PreReadOffset = OffsetPtr; + FileHeader.Version = HeaderExtractor.getU16(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading version from file header at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + FileHeader.Type = HeaderExtractor.getU16(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading file type from file header at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + uint32_t Bitfield = HeaderExtractor.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading flag bits from file header at offset %" PRId64 ".", + OffsetPtr); + + FileHeader.ConstantTSC = Bitfield & 1uL; + FileHeader.NonstopTSC = Bitfield & 1uL << 1; + PreReadOffset = OffsetPtr; + FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading cycle frequency from file header at offset %" PRId64 + ".", + OffsetPtr); + + std::memcpy(&FileHeader.FreeFormData, + HeaderExtractor.getData().bytes_begin() + OffsetPtr, 16); + + // Manually advance the offset pointer 16 bytes, after getting a raw memcpy + // from the underlying data. + OffsetPtr += 16; + return std::move(FileHeader); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/InstrumentationMap.cpp b/contrib/libs/llvm16/lib/XRay/InstrumentationMap.cpp new file mode 100644 index 00000000000..ee190d9e58c --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/InstrumentationMap.cpp @@ -0,0 +1,291 @@ +//===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of the InstrumentationMap type for XRay sleds. +// +//===----------------------------------------------------------------------===// + +#include "llvm/XRay/InstrumentationMap.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocationResolver.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/YAMLTraits.h" +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <system_error> +#include <vector> + +using namespace llvm; +using namespace xray; + +std::optional<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr) const { + auto I = FunctionIds.find(Addr); + if (I != FunctionIds.end()) + return I->second; + return std::nullopt; +} + +std::optional<uint64_t> +InstrumentationMap::getFunctionAddr(int32_t FuncId) const { + auto I = FunctionAddresses.find(FuncId); + if (I != FunctionAddresses.end()) + return I->second; + return std::nullopt; +} + +using RelocMap = DenseMap<uint64_t, uint64_t>; + +static Error +loadObj(StringRef Filename, object::OwningBinary<object::ObjectFile> &ObjFile, + InstrumentationMap::SledContainer &Sleds, + InstrumentationMap::FunctionAddressMap &FunctionAddresses, + InstrumentationMap::FunctionAddressReverseMap &FunctionIds) { + InstrumentationMap Map; + + // Find the section named "xray_instr_map". + if ((!ObjFile.getBinary()->isELF() && !ObjFile.getBinary()->isMachO()) || + !(ObjFile.getBinary()->getArch() == Triple::x86_64 || + ObjFile.getBinary()->getArch() == Triple::ppc64le || + ObjFile.getBinary()->getArch() == Triple::arm || + ObjFile.getBinary()->getArch() == Triple::aarch64)) + return make_error<StringError>( + "File format not supported (only does ELF and Mach-O little endian " + "64-bit).", + std::make_error_code(std::errc::not_supported)); + + StringRef Contents = ""; + const auto &Sections = ObjFile.getBinary()->sections(); + uint64_t Address = 0; + auto I = llvm::find_if(Sections, [&](object::SectionRef Section) { + Expected<StringRef> NameOrErr = Section.getName(); + if (NameOrErr) { + Address = Section.getAddress(); + return *NameOrErr == "xray_instr_map"; + } + consumeError(NameOrErr.takeError()); + return false; + }); + + if (I == Sections.end()) + return make_error<StringError>( + "Failed to find XRay instrumentation map.", + std::make_error_code(std::errc::executable_format_error)); + + if (Error E = I->getContents().moveInto(Contents)) + return E; + + RelocMap Relocs; + if (ObjFile.getBinary()->isELF()) { + uint32_t RelativeRelocation = [](object::ObjectFile *ObjFile) { + if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(ObjFile)) + return ELFObj->getELFFile().getRelativeRelocationType(); + else if (const auto *ELFObj = + dyn_cast<object::ELF32BEObjectFile>(ObjFile)) + return ELFObj->getELFFile().getRelativeRelocationType(); + else if (const auto *ELFObj = + dyn_cast<object::ELF64LEObjectFile>(ObjFile)) + return ELFObj->getELFFile().getRelativeRelocationType(); + else if (const auto *ELFObj = + dyn_cast<object::ELF64BEObjectFile>(ObjFile)) + return ELFObj->getELFFile().getRelativeRelocationType(); + else + return static_cast<uint32_t>(0); + }(ObjFile.getBinary()); + + object::SupportsRelocation Supports; + object::RelocationResolver Resolver; + std::tie(Supports, Resolver) = + object::getRelocationResolver(*ObjFile.getBinary()); + + for (const object::SectionRef &Section : Sections) { + for (const object::RelocationRef &Reloc : Section.relocations()) { + if (ObjFile.getBinary()->getArch() == Triple::arm) { + if (Supports && Supports(Reloc.getType())) { + Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue(); + if (!ValueOrErr) + return ValueOrErr.takeError(); + Relocs.insert( + {Reloc.getOffset(), + object::resolveRelocation(Resolver, Reloc, *ValueOrErr, 0)}); + } + } else if (Supports && Supports(Reloc.getType())) { + auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend(); + auto A = AddendOrErr ? *AddendOrErr : 0; + Expected<uint64_t> ValueOrErr = Reloc.getSymbol()->getValue(); + if (!ValueOrErr) + // TODO: Test this error. + return ValueOrErr.takeError(); + Relocs.insert( + {Reloc.getOffset(), + object::resolveRelocation(Resolver, Reloc, *ValueOrErr, A)}); + } else if (Reloc.getType() == RelativeRelocation) { + if (auto AddendOrErr = object::ELFRelocationRef(Reloc).getAddend()) + Relocs.insert({Reloc.getOffset(), *AddendOrErr}); + } + } + } + } + + // Copy the instrumentation map data into the Sleds data structure. + auto C = Contents.bytes_begin(); + bool Is32Bit = ObjFile.getBinary()->makeTriple().isArch32Bit(); + size_t ELFSledEntrySize = Is32Bit ? 16 : 32; + + if ((C - Contents.bytes_end()) % ELFSledEntrySize != 0) + return make_error<StringError>( + Twine("Instrumentation map entries not evenly divisible by size of " + "an XRay sled entry."), + std::make_error_code(std::errc::executable_format_error)); + + auto RelocateOrElse = [&](uint64_t Offset, uint64_t Address) { + if (!Address) { + uint64_t A = I->getAddress() + C - Contents.bytes_begin() + Offset; + RelocMap::const_iterator R = Relocs.find(A); + if (R != Relocs.end()) + return R->second; + } + return Address; + }; + + const int WordSize = Is32Bit ? 4 : 8; + int32_t FuncId = 1; + uint64_t CurFn = 0; + for (; C != Contents.bytes_end(); C += ELFSledEntrySize) { + DataExtractor Extractor( + StringRef(reinterpret_cast<const char *>(C), ELFSledEntrySize), true, + 8); + Sleds.push_back({}); + auto &Entry = Sleds.back(); + uint64_t OffsetPtr = 0; + uint64_t AddrOff = OffsetPtr; + if (Is32Bit) + Entry.Address = RelocateOrElse(AddrOff, Extractor.getU32(&OffsetPtr)); + else + Entry.Address = RelocateOrElse(AddrOff, Extractor.getU64(&OffsetPtr)); + uint64_t FuncOff = OffsetPtr; + if (Is32Bit) + Entry.Function = RelocateOrElse(FuncOff, Extractor.getU32(&OffsetPtr)); + else + Entry.Function = RelocateOrElse(FuncOff, Extractor.getU64(&OffsetPtr)); + auto Kind = Extractor.getU8(&OffsetPtr); + static constexpr SledEntry::FunctionKinds Kinds[] = { + SledEntry::FunctionKinds::ENTRY, SledEntry::FunctionKinds::EXIT, + SledEntry::FunctionKinds::TAIL, + SledEntry::FunctionKinds::LOG_ARGS_ENTER, + SledEntry::FunctionKinds::CUSTOM_EVENT}; + if (Kind >= std::size(Kinds)) + return errorCodeToError( + std::make_error_code(std::errc::executable_format_error)); + Entry.Kind = Kinds[Kind]; + Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0; + Entry.Version = Extractor.getU8(&OffsetPtr); + if (Entry.Version >= 2) { + Entry.Address += C - Contents.bytes_begin() + Address; + Entry.Function += C - Contents.bytes_begin() + WordSize + Address; + } + + // We do replicate the function id generation scheme implemented in the + // XRay runtime. + // FIXME: Figure out how to keep this consistent with the XRay runtime. + if (CurFn == 0) { + CurFn = Entry.Function; + FunctionAddresses[FuncId] = Entry.Function; + FunctionIds[Entry.Function] = FuncId; + } + if (Entry.Function != CurFn) { + ++FuncId; + CurFn = Entry.Function; + FunctionAddresses[FuncId] = Entry.Function; + FunctionIds[Entry.Function] = FuncId; + } + } + return Error::success(); +} + +static Error +loadYAML(sys::fs::file_t Fd, size_t FileSize, StringRef Filename, + InstrumentationMap::SledContainer &Sleds, + InstrumentationMap::FunctionAddressMap &FunctionAddresses, + InstrumentationMap::FunctionAddressReverseMap &FunctionIds) { + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + sys::fs::closeFile(Fd); + if (EC) + return make_error<StringError>( + Twine("Failed memory-mapping file '") + Filename + "'.", EC); + + std::vector<YAMLXRaySledEntry> YAMLSleds; + yaml::Input In(StringRef(MappedFile.data(), MappedFile.size())); + In >> YAMLSleds; + if (In.error()) + return make_error<StringError>( + Twine("Failed loading YAML document from '") + Filename + "'.", + In.error()); + + Sleds.reserve(YAMLSleds.size()); + for (const auto &Y : YAMLSleds) { + FunctionAddresses[Y.FuncId] = Y.Function; + FunctionIds[Y.Function] = Y.FuncId; + Sleds.push_back(SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument, + Y.Version}); + } + return Error::success(); +} + +// FIXME: Create error types that encapsulate a bit more information than what +// StringError instances contain. +Expected<InstrumentationMap> +llvm::xray::loadInstrumentationMap(StringRef Filename) { + // At this point we assume the file is an object file -- and if that doesn't + // work, we treat it as YAML. + // FIXME: Extend to support non-ELF and non-x86_64 binaries. + + InstrumentationMap Map; + auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename); + if (!ObjectFileOrError) { + auto E = ObjectFileOrError.takeError(); + // We try to load it as YAML if the ELF load didn't work. + Expected<sys::fs::file_t> FdOrErr = + sys::fs::openNativeFileForRead(Filename); + if (!FdOrErr) { + // Report the ELF load error if YAML failed. + consumeError(FdOrErr.takeError()); + return std::move(E); + } + + uint64_t FileSize; + if (sys::fs::file_size(Filename, FileSize)) + return std::move(E); + + // If the file is empty, we return the original error. + if (FileSize == 0) + return std::move(E); + + // From this point on the errors will be only for the YAML parts, so we + // consume the errors at this point. + consumeError(std::move(E)); + if (auto E = loadYAML(*FdOrErr, FileSize, Filename, Map.Sleds, + Map.FunctionAddresses, Map.FunctionIds)) + return std::move(E); + } else if (auto E = loadObj(Filename, *ObjectFileOrError, Map.Sleds, + Map.FunctionAddresses, Map.FunctionIds)) { + return std::move(E); + } + return Map; +} diff --git a/contrib/libs/llvm16/lib/XRay/LogBuilderConsumer.cpp b/contrib/libs/llvm16/lib/XRay/LogBuilderConsumer.cpp new file mode 100644 index 00000000000..ffb49f9eb4e --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/LogBuilderConsumer.cpp @@ -0,0 +1,37 @@ +//===- FDRRecordConsumer.h - XRay Flight Data Recorder Mode Records -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRRecordConsumer.h" + +namespace llvm { +namespace xray { + +Error LogBuilderConsumer::consume(std::unique_ptr<Record> R) { + if (!R) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Must not call RecordConsumer::consume() with a null pointer."); + Records.push_back(std::move(R)); + return Error::success(); +} + +Error PipelineConsumer::consume(std::unique_ptr<Record> R) { + if (!R) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Must not call RecordConsumer::consume() with a null pointer."); + + // We apply all of the visitors in order, and concatenate errors + // appropriately. + Error Result = Error::success(); + for (auto *V : Visitors) + Result = joinErrors(std::move(Result), R->apply(*V)); + return Result; +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/Profile.cpp b/contrib/libs/llvm16/lib/XRay/Profile.cpp new file mode 100644 index 00000000000..c1a43632b60 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/Profile.cpp @@ -0,0 +1,403 @@ +//===- Profile.cpp - XRay Profile Abstraction -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the XRay Profile class representing the latency profile generated by +// XRay's profiling mode. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/Profile.h" + +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/XRay/Trace.h" +#include <deque> +#include <memory> + +namespace llvm { +namespace xray { + +Profile::Profile(const Profile &O) { + // We need to re-create all the tries from the original (O), into the current + // Profile being initialized, through the Block instances we see. + for (const auto &Block : O) { + Blocks.push_back({Block.Thread, {}}); + auto &B = Blocks.back(); + for (const auto &PathData : Block.PathData) + B.PathData.push_back({internPath(cantFail(O.expandPath(PathData.first))), + PathData.second}); + } +} + +Profile &Profile::operator=(const Profile &O) { + Profile P = O; + *this = std::move(P); + return *this; +} + +namespace { + +struct BlockHeader { + uint32_t Size; + uint32_t Number; + uint64_t Thread; +}; + +static Expected<BlockHeader> readBlockHeader(DataExtractor &Extractor, + uint64_t &Offset) { + BlockHeader H; + uint64_t CurrentOffset = Offset; + H.Size = Extractor.getU32(&Offset); + if (Offset == CurrentOffset) + return make_error<StringError>( + Twine("Error parsing block header size at offset '") + + Twine(CurrentOffset) + "'", + std::make_error_code(std::errc::invalid_argument)); + CurrentOffset = Offset; + H.Number = Extractor.getU32(&Offset); + if (Offset == CurrentOffset) + return make_error<StringError>( + Twine("Error parsing block header number at offset '") + + Twine(CurrentOffset) + "'", + std::make_error_code(std::errc::invalid_argument)); + CurrentOffset = Offset; + H.Thread = Extractor.getU64(&Offset); + if (Offset == CurrentOffset) + return make_error<StringError>( + Twine("Error parsing block header thread id at offset '") + + Twine(CurrentOffset) + "'", + std::make_error_code(std::errc::invalid_argument)); + return H; +} + +static Expected<std::vector<Profile::FuncID>> readPath(DataExtractor &Extractor, + uint64_t &Offset) { + // We're reading a sequence of int32_t's until we find a 0. + std::vector<Profile::FuncID> Path; + auto CurrentOffset = Offset; + int32_t FuncId; + do { + FuncId = Extractor.getSigned(&Offset, 4); + if (CurrentOffset == Offset) + return make_error<StringError>( + Twine("Error parsing path at offset '") + Twine(CurrentOffset) + "'", + std::make_error_code(std::errc::invalid_argument)); + CurrentOffset = Offset; + Path.push_back(FuncId); + } while (FuncId != 0); + return std::move(Path); +} + +static Expected<Profile::Data> readData(DataExtractor &Extractor, + uint64_t &Offset) { + // We expect a certain number of elements for Data: + // - A 64-bit CallCount + // - A 64-bit CumulativeLocalTime counter + Profile::Data D; + auto CurrentOffset = Offset; + D.CallCount = Extractor.getU64(&Offset); + if (CurrentOffset == Offset) + return make_error<StringError>( + Twine("Error parsing call counts at offset '") + Twine(CurrentOffset) + + "'", + std::make_error_code(std::errc::invalid_argument)); + CurrentOffset = Offset; + D.CumulativeLocalTime = Extractor.getU64(&Offset); + if (CurrentOffset == Offset) + return make_error<StringError>( + Twine("Error parsing cumulative local time at offset '") + + Twine(CurrentOffset) + "'", + std::make_error_code(std::errc::invalid_argument)); + return D; +} + +} // namespace + +Error Profile::addBlock(Block &&B) { + if (B.PathData.empty()) + return make_error<StringError>( + "Block may not have empty path data.", + std::make_error_code(std::errc::invalid_argument)); + + Blocks.emplace_back(std::move(B)); + return Error::success(); +} + +Expected<std::vector<Profile::FuncID>> Profile::expandPath(PathID P) const { + auto It = PathIDMap.find(P); + if (It == PathIDMap.end()) + return make_error<StringError>( + Twine("PathID not found: ") + Twine(P), + std::make_error_code(std::errc::invalid_argument)); + std::vector<Profile::FuncID> Path; + for (auto Node = It->second; Node; Node = Node->Caller) + Path.push_back(Node->Func); + return std::move(Path); +} + +Profile::PathID Profile::internPath(ArrayRef<FuncID> P) { + if (P.empty()) + return 0; + + auto RootToLeafPath = reverse(P); + + // Find the root. + auto It = RootToLeafPath.begin(); + auto PathRoot = *It++; + auto RootIt = + find_if(Roots, [PathRoot](TrieNode *N) { return N->Func == PathRoot; }); + + // If we've not seen this root before, remember it. + TrieNode *Node = nullptr; + if (RootIt == Roots.end()) { + NodeStorage.emplace_back(); + Node = &NodeStorage.back(); + Node->Func = PathRoot; + Roots.push_back(Node); + } else { + Node = *RootIt; + } + + // Now traverse the path, re-creating if necessary. + while (It != RootToLeafPath.end()) { + auto NodeFuncID = *It++; + auto CalleeIt = find_if(Node->Callees, [NodeFuncID](TrieNode *N) { + return N->Func == NodeFuncID; + }); + if (CalleeIt == Node->Callees.end()) { + NodeStorage.emplace_back(); + auto NewNode = &NodeStorage.back(); + NewNode->Func = NodeFuncID; + NewNode->Caller = Node; + Node->Callees.push_back(NewNode); + Node = NewNode; + } else { + Node = *CalleeIt; + } + } + + // At this point, Node *must* be pointing at the leaf. + assert(Node->Func == P.front()); + if (Node->ID == 0) { + Node->ID = NextID++; + PathIDMap.insert({Node->ID, Node}); + } + return Node->ID; +} + +Profile mergeProfilesByThread(const Profile &L, const Profile &R) { + Profile Merged; + using PathDataMap = DenseMap<Profile::PathID, Profile::Data>; + using PathDataMapPtr = std::unique_ptr<PathDataMap>; + using PathDataVector = decltype(Profile::Block::PathData); + using ThreadProfileIndexMap = DenseMap<Profile::ThreadID, PathDataMapPtr>; + ThreadProfileIndexMap ThreadProfileIndex; + + for (const auto &P : {std::ref(L), std::ref(R)}) + for (const auto &Block : P.get()) { + ThreadProfileIndexMap::iterator It; + std::tie(It, std::ignore) = ThreadProfileIndex.insert( + {Block.Thread, PathDataMapPtr{new PathDataMap()}}); + for (const auto &PathAndData : Block.PathData) { + auto &PathID = PathAndData.first; + auto &Data = PathAndData.second; + auto NewPathID = + Merged.internPath(cantFail(P.get().expandPath(PathID))); + PathDataMap::iterator PathDataIt; + bool Inserted; + std::tie(PathDataIt, Inserted) = It->second->insert({NewPathID, Data}); + if (!Inserted) { + auto &ExistingData = PathDataIt->second; + ExistingData.CallCount += Data.CallCount; + ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime; + } + } + } + + for (const auto &IndexedThreadBlock : ThreadProfileIndex) { + PathDataVector PathAndData; + PathAndData.reserve(IndexedThreadBlock.second->size()); + copy(*IndexedThreadBlock.second, std::back_inserter(PathAndData)); + cantFail( + Merged.addBlock({IndexedThreadBlock.first, std::move(PathAndData)})); + } + return Merged; +} + +Profile mergeProfilesByStack(const Profile &L, const Profile &R) { + Profile Merged; + using PathDataMap = DenseMap<Profile::PathID, Profile::Data>; + PathDataMap PathData; + using PathDataVector = decltype(Profile::Block::PathData); + for (const auto &P : {std::ref(L), std::ref(R)}) + for (const auto &Block : P.get()) + for (const auto &PathAndData : Block.PathData) { + auto &PathId = PathAndData.first; + auto &Data = PathAndData.second; + auto NewPathID = + Merged.internPath(cantFail(P.get().expandPath(PathId))); + PathDataMap::iterator PathDataIt; + bool Inserted; + std::tie(PathDataIt, Inserted) = PathData.insert({NewPathID, Data}); + if (!Inserted) { + auto &ExistingData = PathDataIt->second; + ExistingData.CallCount += Data.CallCount; + ExistingData.CumulativeLocalTime += Data.CumulativeLocalTime; + } + } + + // In the end there's a single Block, for thread 0. + PathDataVector Block; + Block.reserve(PathData.size()); + copy(PathData, std::back_inserter(Block)); + cantFail(Merged.addBlock({0, std::move(Block)})); + return Merged; +} + +Expected<Profile> loadProfile(StringRef Filename) { + Expected<sys::fs::file_t> FdOrErr = sys::fs::openNativeFileForRead(Filename); + if (!FdOrErr) + return FdOrErr.takeError(); + + uint64_t FileSize; + if (auto EC = sys::fs::file_size(Filename, FileSize)) + return make_error<StringError>( + Twine("Cannot get filesize of '") + Filename + "'", EC); + + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + *FdOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, + EC); + sys::fs::closeFile(*FdOrErr); + if (EC) + return make_error<StringError>( + Twine("Cannot mmap profile '") + Filename + "'", EC); + StringRef Data(MappedFile.data(), MappedFile.size()); + + Profile P; + uint64_t Offset = 0; + DataExtractor Extractor(Data, true, 8); + + // For each block we get from the file: + while (Offset != MappedFile.size()) { + auto HeaderOrError = readBlockHeader(Extractor, Offset); + if (!HeaderOrError) + return HeaderOrError.takeError(); + + // TODO: Maybe store this header information for each block, even just for + // debugging? + const auto &Header = HeaderOrError.get(); + + // Read in the path data. + auto PathOrError = readPath(Extractor, Offset); + if (!PathOrError) + return PathOrError.takeError(); + const auto &Path = PathOrError.get(); + + // For each path we encounter, we should intern it to get a PathID. + auto DataOrError = readData(Extractor, Offset); + if (!DataOrError) + return DataOrError.takeError(); + auto &Data = DataOrError.get(); + + if (auto E = + P.addBlock(Profile::Block{Profile::ThreadID{Header.Thread}, + {{P.internPath(Path), std::move(Data)}}})) + return std::move(E); + } + + return P; +} + +namespace { + +struct StackEntry { + uint64_t Timestamp; + Profile::FuncID FuncId; +}; + +} // namespace + +Expected<Profile> profileFromTrace(const Trace &T) { + Profile P; + + // The implementation of the algorithm re-creates the execution of + // the functions based on the trace data. To do this, we set up a number of + // data structures to track the execution context of every thread in the + // Trace. + DenseMap<Profile::ThreadID, std::vector<StackEntry>> ThreadStacks; + DenseMap<Profile::ThreadID, DenseMap<Profile::PathID, Profile::Data>> + ThreadPathData; + + // We then do a pass through the Trace to account data on a per-thread-basis. + for (const auto &E : T) { + auto &TSD = ThreadStacks[E.TId]; + switch (E.Type) { + case RecordTypes::ENTER: + case RecordTypes::ENTER_ARG: + + // Push entries into the function call stack. + TSD.push_back({E.TSC, E.FuncId}); + break; + + case RecordTypes::EXIT: + case RecordTypes::TAIL_EXIT: + + // Exits cause some accounting to happen, based on the state of the stack. + // For each function we pop off the stack, we take note of the path and + // record the cumulative state for this path. As we're doing this, we + // intern the path into the Profile. + while (!TSD.empty()) { + auto Top = TSD.back(); + auto FunctionLocalTime = AbsoluteDifference(Top.Timestamp, E.TSC); + SmallVector<Profile::FuncID, 16> Path; + transform(reverse(TSD), std::back_inserter(Path), + std::mem_fn(&StackEntry::FuncId)); + auto InternedPath = P.internPath(Path); + auto &TPD = ThreadPathData[E.TId][InternedPath]; + ++TPD.CallCount; + TPD.CumulativeLocalTime += FunctionLocalTime; + TSD.pop_back(); + + // If we've matched the corresponding entry event for this function, + // then we exit the loop. + if (Top.FuncId == E.FuncId) + break; + + // FIXME: Consider the intermediate times and the cumulative tree time + // as well. + } + + break; + + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // TODO: Support an extension point to allow handling of custom and typed + // events in profiles. + break; + } + } + + // Once we've gone through the Trace, we now create one Block per thread in + // the Profile. + for (const auto &ThreadPaths : ThreadPathData) { + const auto &TID = ThreadPaths.first; + const auto &PathsData = ThreadPaths.second; + if (auto E = P.addBlock({ + TID, + std::vector<std::pair<Profile::PathID, Profile::Data>>( + PathsData.begin(), PathsData.end()), + })) + return std::move(E); + } + + return P; +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/RecordInitializer.cpp b/contrib/libs/llvm16/lib/XRay/RecordInitializer.cpp new file mode 100644 index 00000000000..68ab3db0620 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/RecordInitializer.cpp @@ -0,0 +1,431 @@ +//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/FDRRecords.h" + +namespace llvm { +namespace xray { + +Error RecordInitializer::visit(BufferExtents &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, sizeof(uint64_t))) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a buffer extent (%" PRId64 ").", OffsetPtr); + + auto PreReadOffset = OffsetPtr; + R.Size = E.getU64(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Cannot read buffer extent at offset %" PRId64 ".", + OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset); + return Error::success(); +} + +Error RecordInitializer::visit(WallclockRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a wallclock record (%" PRId64 ").", OffsetPtr); + auto BeginOffset = OffsetPtr; + auto PreReadOffset = OffsetPtr; + R.Seconds = E.getU64(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read wall clock 'seconds' field at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + R.Nanos = E.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read wall clock 'nanos' field at offset %" PRId64 ".", + OffsetPtr); + + // Align to metadata record size boundary. + assert(OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize); + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset); + return Error::success(); +} + +Error RecordInitializer::visit(NewCPUIDRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a new cpu id record (%" PRId64 ").", OffsetPtr); + auto BeginOffset = OffsetPtr; + auto PreReadOffset = OffsetPtr; + R.CPUId = E.getU16(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Cannot read CPU id at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + R.TSC = E.getU64(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Cannot read CPU TSC at offset %" PRId64 ".", + OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset); + return Error::success(); +} + +Error RecordInitializer::visit(TSCWrapRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a new TSC wrap record (%" PRId64 ").", OffsetPtr); + + auto PreReadOffset = OffsetPtr; + R.BaseTSC = E.getU64(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read TSC wrap record at offset %" PRId64 ".", OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset); + return Error::success(); +} + +Error RecordInitializer::visit(CustomEventRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr); + + auto BeginOffset = OffsetPtr; + auto PreReadOffset = OffsetPtr; + R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a custom event record size field offset %" PRId64 ".", + OffsetPtr); + + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for custom event (size = %d) at offset %" PRId64 ".", + R.Size, OffsetPtr); + + PreReadOffset = OffsetPtr; + R.TSC = E.getU64(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a custom event TSC field at offset %" PRId64 ".", + OffsetPtr); + + // For version 4 onwards, of the FDR log, we want to also capture the CPU ID + // of the custom event. + if (Version >= 4) { + PreReadOffset = OffsetPtr; + R.CPU = E.getU16(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Missing CPU field at offset %" PRId64 ".", OffsetPtr); + } + + assert(OffsetPtr > BeginOffset && + OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize); + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset); + + // Next we read in a fixed chunk of data from the given offset. + if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Cannot read %d bytes of custom event data from offset %" PRId64 ".", + R.Size, OffsetPtr); + + std::vector<uint8_t> Buffer; + Buffer.resize(R.Size); + PreReadOffset = OffsetPtr; + if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data()) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading data into buffer of size %d at offset %" PRId64 ".", + R.Size, OffsetPtr); + + assert(OffsetPtr >= PreReadOffset); + if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size)) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading enough bytes for the custom event payload -- read " + "%" PRId64 " expecting %d bytes at offset %" PRId64 ".", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + + R.Data.assign(Buffer.begin(), Buffer.end()); + return Error::success(); +} + +Error RecordInitializer::visit(CustomEventRecordV5 &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr); + + auto BeginOffset = OffsetPtr; + auto PreReadOffset = OffsetPtr; + + R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a custom event record size field offset %" PRId64 ".", + OffsetPtr); + + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for custom event (size = %d) at offset %" PRId64 ".", + R.Size, OffsetPtr); + + PreReadOffset = OffsetPtr; + R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a custom event record TSC delta field at offset " + "%" PRId64 ".", + OffsetPtr); + + assert(OffsetPtr > BeginOffset && + OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize); + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset); + + // Next we read in a fixed chunk of data from the given offset. + if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Cannot read %d bytes of custom event data from offset %" PRId64 ".", + R.Size, OffsetPtr); + + std::vector<uint8_t> Buffer; + Buffer.resize(R.Size); + PreReadOffset = OffsetPtr; + if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data()) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading data into buffer of size %d at offset %" PRId64 ".", + R.Size, OffsetPtr); + + assert(OffsetPtr >= PreReadOffset); + if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size)) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading enough bytes for the custom event payload -- read " + "%" PRId64 " expecting %d bytes at offset %" PRId64 ".", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + + R.Data.assign(Buffer.begin(), Buffer.end()); + return Error::success(); +} + +Error RecordInitializer::visit(TypedEventRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a typed event record (%" PRId64 ").", OffsetPtr); + + auto BeginOffset = OffsetPtr; + auto PreReadOffset = OffsetPtr; + + R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a typed event record size field offset %" PRId64 ".", + OffsetPtr); + + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for typed event (size = %d) at offset %" PRId64 ".", + R.Size, OffsetPtr); + + PreReadOffset = OffsetPtr; + R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a typed event record TSC delta field at offset " + "%" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + R.EventType = E.getU16(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a typed event record type field at offset %" PRId64 ".", + OffsetPtr); + + assert(OffsetPtr > BeginOffset && + OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize); + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset); + + // Next we read in a fixed chunk of data from the given offset. + if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Cannot read %d bytes of custom event data from offset %" PRId64 ".", + R.Size, OffsetPtr); + + std::vector<uint8_t> Buffer; + Buffer.resize(R.Size); + PreReadOffset = OffsetPtr; + if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data()) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading data into buffer of size %d at offset %" PRId64 ".", + R.Size, OffsetPtr); + + assert(OffsetPtr >= PreReadOffset); + if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size)) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading enough bytes for the typed event payload -- read " + "%" PRId64 " expecting %d bytes at offset %" PRId64 ".", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + + R.Data.assign(Buffer.begin(), Buffer.end()); + return Error::success(); +} + +Error RecordInitializer::visit(CallArgRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a call argument record (%" PRId64 ").", + OffsetPtr); + + auto PreReadOffset = OffsetPtr; + R.Arg = E.getU64(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a call arg record at offset %" PRId64 ".", OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset); + return Error::success(); +} + +Error RecordInitializer::visit(PIDRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a process ID record (%" PRId64 ").", OffsetPtr); + + auto PreReadOffset = OffsetPtr; + R.PID = E.getSigned(&OffsetPtr, 4); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a process ID record at offset %" PRId64 ".", OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset); + return Error::success(); +} + +Error RecordInitializer::visit(NewBufferRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a new buffer record (%" PRId64 ").", OffsetPtr); + + auto PreReadOffset = OffsetPtr; + R.TID = E.getSigned(&OffsetPtr, sizeof(int32_t)); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Cannot read a new buffer record at offset %" PRId64 ".", OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset); + return Error::success(); +} + +Error RecordInitializer::visit(EndBufferRecord &R) { + if (!E.isValidOffsetForDataOfSize(OffsetPtr, + MetadataRecord::kMetadataBodySize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for an end-of-buffer record (%" PRId64 ").", + OffsetPtr); + + OffsetPtr += MetadataRecord::kMetadataBodySize; + return Error::success(); +} + +Error RecordInitializer::visit(FunctionRecord &R) { + // For function records, we need to retreat one byte back to read a full + // unsigned 32-bit value. The first four bytes will have the following + // layout: + // + // bit 0 : function record indicator (must be 0) + // bits 1..3 : function record type + // bits 4..32 : function id + // + if (OffsetPtr == 0 || !E.isValidOffsetForDataOfSize( + --OffsetPtr, FunctionRecord::kFunctionRecordSize)) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid offset for a function record (%" PRId64 ").", OffsetPtr); + + auto BeginOffset = OffsetPtr; + auto PreReadOffset = BeginOffset; + uint32_t Buffer = E.getU32(&OffsetPtr); + if (PreReadOffset == OffsetPtr) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Cannot read function id field from offset %" PRId64 ".", OffsetPtr); + + // To get the function record type, we shift the buffer one to the right + // (truncating the function record indicator) then take the three bits + // (0b0111) to get the record type as an unsigned value. + unsigned FunctionType = (Buffer >> 1) & 0x07u; + switch (FunctionType) { + case static_cast<unsigned>(RecordTypes::ENTER): + case static_cast<unsigned>(RecordTypes::ENTER_ARG): + case static_cast<unsigned>(RecordTypes::EXIT): + case static_cast<unsigned>(RecordTypes::TAIL_EXIT): + R.Kind = static_cast<RecordTypes>(FunctionType); + break; + default: + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Unknown function record type '%d' at offset %" PRId64 ".", + FunctionType, BeginOffset); + } + + R.FuncId = Buffer >> 4; + PreReadOffset = OffsetPtr; + R.Delta = E.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Failed reading TSC delta from offset %" PRId64 ".", OffsetPtr); + assert(FunctionRecord::kFunctionRecordSize == (OffsetPtr - BeginOffset)); + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/RecordPrinter.cpp b/contrib/libs/llvm16/lib/XRay/RecordPrinter.cpp new file mode 100644 index 00000000000..32d42104db9 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/RecordPrinter.cpp @@ -0,0 +1,108 @@ +//===- RecordPrinter.cpp - FDR Record Printer -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/RecordPrinter.h" + +#include "llvm/Support/FormatVariadic.h" + +namespace llvm { +namespace xray { + +Error RecordPrinter::visit(BufferExtents &R) { + OS << formatv("<Buffer: size = {0} bytes>", R.size()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(WallclockRecord &R) { + OS << formatv("<Wall Time: seconds = {0}.{1,0+6}>", R.seconds(), R.nanos()) + << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(NewCPUIDRecord &R) { + OS << formatv("<CPU: id = {0}, tsc = {1}>", R.cpuid(), R.tsc()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(TSCWrapRecord &R) { + OS << formatv("<TSC Wrap: base = {0}>", R.tsc()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(CustomEventRecord &R) { + OS << formatv( + "<Custom Event: tsc = {0}, cpu = {1}, size = {2}, data = '{3}'>", + R.tsc(), R.cpu(), R.size(), R.data()) + << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(CustomEventRecordV5 &R) { + OS << formatv("<Custom Event: delta = +{0}, size = {1}, data = '{2}'>", + R.delta(), R.size(), R.data()) + << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(TypedEventRecord &R) { + OS << formatv( + "<Typed Event: delta = +{0}, type = {1}, size = {2}, data = '{3}'", + R.delta(), R.eventType(), R.size(), R.data()) + << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(CallArgRecord &R) { + OS << formatv("<Call Argument: data = {0} (hex = {0:x})>", R.arg()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(PIDRecord &R) { + OS << formatv("<PID: {0}>", R.pid()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(NewBufferRecord &R) { + OS << formatv("<Thread ID: {0}>", R.tid()) << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(EndBufferRecord &R) { + OS << "<End of Buffer>" << Delim; + return Error::success(); +} + +Error RecordPrinter::visit(FunctionRecord &R) { + // FIXME: Support symbolization here? + switch (R.recordType()) { + case RecordTypes::ENTER: + OS << formatv("<Function Enter: #{0} delta = +{1}>", R.functionId(), + R.delta()); + break; + case RecordTypes::ENTER_ARG: + OS << formatv("<Function Enter With Arg: #{0} delta = +{1}>", + R.functionId(), R.delta()); + break; + case RecordTypes::EXIT: + OS << formatv("<Function Exit: #{0} delta = +{1}>", R.functionId(), + R.delta()); + break; + case RecordTypes::TAIL_EXIT: + OS << formatv("<Function Tail Exit: #{0} delta = +{1}>", R.functionId(), + R.delta()); + break; + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // TODO: Flag as a bug? + break; + } + OS << Delim; + return Error::success(); +} + +} // namespace xray +} // namespace llvm diff --git a/contrib/libs/llvm16/lib/XRay/Trace.cpp b/contrib/libs/llvm16/lib/XRay/Trace.cpp new file mode 100644 index 00000000000..b870adf5654 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/Trace.cpp @@ -0,0 +1,475 @@ +//===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// XRay log reader implementation. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/Trace.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/XRay/BlockIndexer.h" +#include "llvm/XRay/BlockVerifier.h" +#include "llvm/XRay/FDRRecordConsumer.h" +#include "llvm/XRay/FDRRecordProducer.h" +#include "llvm/XRay/FDRRecords.h" +#include "llvm/XRay/FDRTraceExpander.h" +#include "llvm/XRay/FileHeaderReader.h" +#include "llvm/XRay/YAMLXRayRecord.h" +#include <memory> +#include <vector> + +using namespace llvm; +using namespace llvm::xray; +using llvm::yaml::Input; + +namespace { + +Error loadNaiveFormatLog(StringRef Data, bool IsLittleEndian, + XRayFileHeader &FileHeader, + std::vector<XRayRecord> &Records) { + if (Data.size() < 32) + return make_error<StringError>( + "Not enough bytes for an XRay log.", + std::make_error_code(std::errc::invalid_argument)); + + if (Data.size() - 32 == 0 || Data.size() % 32 != 0) + return make_error<StringError>( + "Invalid-sized XRay data.", + std::make_error_code(std::errc::invalid_argument)); + + DataExtractor Reader(Data, IsLittleEndian, 8); + uint64_t OffsetPtr = 0; + auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); + if (!FileHeaderOrError) + return FileHeaderOrError.takeError(); + FileHeader = std::move(FileHeaderOrError.get()); + + // Each record after the header will be 32 bytes, in the following format: + // + // (2) uint16 : record type + // (1) uint8 : cpu id + // (1) uint8 : type + // (4) sint32 : function id + // (8) uint64 : tsc + // (4) uint32 : thread id + // (4) uint32 : process id + // (8) - : padding + while (Reader.isValidOffset(OffsetPtr)) { + if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Not enough bytes to read a full record at offset %" PRId64 ".", + OffsetPtr); + auto PreReadOffset = OffsetPtr; + auto RecordType = Reader.getU16(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading record type at offset %" PRId64 ".", OffsetPtr); + + switch (RecordType) { + case 0: { // Normal records. + Records.emplace_back(); + auto &Record = Records.back(); + Record.RecordType = RecordType; + + PreReadOffset = OffsetPtr; + Record.CPU = Reader.getU8(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading CPU field at offset %" PRId64 ".", OffsetPtr); + + PreReadOffset = OffsetPtr; + auto Type = Reader.getU8(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading record type field at offset %" PRId64 ".", + OffsetPtr); + + switch (Type) { + case 0: + Record.Type = RecordTypes::ENTER; + break; + case 1: + Record.Type = RecordTypes::EXIT; + break; + case 2: + Record.Type = RecordTypes::TAIL_EXIT; + break; + case 3: + Record.Type = RecordTypes::ENTER_ARG; + break; + default: + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Unknown record type '%d' at offset %" PRId64 ".", Type, OffsetPtr); + } + + PreReadOffset = OffsetPtr; + Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading function id field at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + Record.TSC = Reader.getU64(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading TSC field at offset %" PRId64 ".", OffsetPtr); + + PreReadOffset = OffsetPtr; + Record.TId = Reader.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading thread id field at offset %" PRId64 ".", OffsetPtr); + + PreReadOffset = OffsetPtr; + Record.PId = Reader.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading process id at offset %" PRId64 ".", OffsetPtr); + + break; + } + case 1: { // Arg payload record. + auto &Record = Records.back(); + + // We skip the next two bytes of the record, because we don't need the + // type and the CPU record for arg payloads. + OffsetPtr += 2; + PreReadOffset = OffsetPtr; + int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading function id field at offset %" PRId64 ".", + OffsetPtr); + + PreReadOffset = OffsetPtr; + auto TId = Reader.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading thread id field at offset %" PRId64 ".", OffsetPtr); + + PreReadOffset = OffsetPtr; + auto PId = Reader.getU32(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading process id field at offset %" PRId64 ".", + OffsetPtr); + + // Make a check for versions above 3 for the Pid field + if (Record.FuncId != FuncId || Record.TId != TId || + (FileHeader.Version >= 3 ? Record.PId != PId : false)) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Corrupted log, found arg payload following non-matching " + "function+thread record. Record for function %d != %d at offset " + "%" PRId64 ".", + Record.FuncId, FuncId, OffsetPtr); + + PreReadOffset = OffsetPtr; + auto Arg = Reader.getU64(&OffsetPtr); + if (OffsetPtr == PreReadOffset) + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Failed reading argument payload at offset %" PRId64 ".", + OffsetPtr); + + Record.CallArgs.push_back(Arg); + break; + } + default: + return createStringError( + std::make_error_code(std::errc::executable_format_error), + "Unknown record type '%d' at offset %" PRId64 ".", RecordType, + OffsetPtr); + } + // Advance the offset pointer enough bytes to align to 32-byte records for + // basic mode logs. + OffsetPtr += 8; + } + return Error::success(); +} + +/// Reads a log in FDR mode for version 1 of this binary format. FDR mode is +/// defined as part of the compiler-rt project in xray_fdr_logging.h, and such +/// a log consists of the familiar 32 bit XRayHeader, followed by sequences of +/// of interspersed 16 byte Metadata Records and 8 byte Function Records. +/// +/// The following is an attempt to document the grammar of the format, which is +/// parsed by this function for little-endian machines. Since the format makes +/// use of BitFields, when we support big-endian architectures, we will need to +/// adjust not only the endianness parameter to llvm's RecordExtractor, but also +/// the bit twiddling logic, which is consistent with the little-endian +/// convention that BitFields within a struct will first be packed into the +/// least significant bits the address they belong to. +/// +/// We expect a format complying with the grammar in the following pseudo-EBNF +/// in Version 1 of the FDR log. +/// +/// FDRLog: XRayFileHeader ThreadBuffer* +/// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. +/// Includes BufferSize +/// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB +/// BufSize: 8 byte unsigned integer indicating how large the buffer is. +/// NewBuffer: 16 byte metadata record with Thread Id. +/// WallClockTime: 16 byte metadata record with human readable time. +/// Pid: 16 byte metadata record with Pid +/// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. +/// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. +/// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord +/// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. +/// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. +/// +/// In Version 2, we make the following changes: +/// +/// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId +/// FunctionSequence +/// BufferExtents: 16 byte metdata record describing how many usable bytes are +/// in the buffer. This is measured from the start of the buffer +/// and must always be at least 48 (bytes). +/// +/// In Version 3, we make the following changes: +/// +/// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId +/// FunctionSequence +/// EOB: *deprecated* +/// +/// In Version 4, we make the following changes: +/// +/// CustomEventRecord now includes the CPU data. +/// +/// In Version 5, we make the following changes: +/// +/// CustomEventRecord and TypedEventRecord now use TSC delta encoding similar to +/// what FunctionRecord instances use, and we no longer need to include the CPU +/// id in the CustomEventRecord. +/// +Error loadFDRLog(StringRef Data, bool IsLittleEndian, + XRayFileHeader &FileHeader, std::vector<XRayRecord> &Records) { + + if (Data.size() < 32) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Not enough bytes for an XRay FDR log."); + DataExtractor DE(Data, IsLittleEndian, 8); + + uint64_t OffsetPtr = 0; + auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr); + if (!FileHeaderOrError) + return FileHeaderOrError.takeError(); + FileHeader = std::move(FileHeaderOrError.get()); + + // First we load the records into memory. + std::vector<std::unique_ptr<Record>> FDRRecords; + + { + FileBasedRecordProducer P(FileHeader, DE, OffsetPtr); + LogBuilderConsumer C(FDRRecords); + while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { + auto R = P.produce(); + if (!R) + return R.takeError(); + if (auto E = C.consume(std::move(R.get()))) + return E; + } + } + + // Next we index the records into blocks. + BlockIndexer::Index Index; + { + BlockIndexer Indexer(Index); + for (auto &R : FDRRecords) + if (auto E = R->apply(Indexer)) + return E; + if (auto E = Indexer.flush()) + return E; + } + + // Then we verify the consistency of the blocks. + { + for (auto &PTB : Index) { + auto &Blocks = PTB.second; + for (auto &B : Blocks) { + BlockVerifier Verifier; + for (auto *R : B.Records) + if (auto E = R->apply(Verifier)) + return E; + if (auto E = Verifier.verify()) + return E; + } + } + } + + // This is now the meat of the algorithm. Here we sort the blocks according to + // the Walltime record in each of the blocks for the same thread. This allows + // us to more consistently recreate the execution trace in temporal order. + // After the sort, we then reconstitute `Trace` records using a stateful + // visitor associated with a single process+thread pair. + { + for (auto &PTB : Index) { + auto &Blocks = PTB.second; + llvm::sort(Blocks, [](const BlockIndexer::Block &L, + const BlockIndexer::Block &R) { + return (L.WallclockTime->seconds() < R.WallclockTime->seconds() && + L.WallclockTime->nanos() < R.WallclockTime->nanos()); + }); + auto Adder = [&](const XRayRecord &R) { Records.push_back(R); }; + TraceExpander Expander(Adder, FileHeader.Version); + for (auto &B : Blocks) { + for (auto *R : B.Records) + if (auto E = R->apply(Expander)) + return E; + } + if (auto E = Expander.flush()) + return E; + } + } + + return Error::success(); +} + +Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader, + std::vector<XRayRecord> &Records) { + YAMLXRayTrace Trace; + Input In(Data); + In >> Trace; + if (In.error()) + return make_error<StringError>("Failed loading YAML Data.", In.error()); + + FileHeader.Version = Trace.Header.Version; + FileHeader.Type = Trace.Header.Type; + FileHeader.ConstantTSC = Trace.Header.ConstantTSC; + FileHeader.NonstopTSC = Trace.Header.NonstopTSC; + FileHeader.CycleFrequency = Trace.Header.CycleFrequency; + + if (FileHeader.Version != 1) + return make_error<StringError>( + Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), + std::make_error_code(std::errc::invalid_argument)); + + Records.clear(); + std::transform(Trace.Records.begin(), Trace.Records.end(), + std::back_inserter(Records), [&](const YAMLXRayRecord &R) { + return XRayRecord{R.RecordType, R.CPU, R.Type, + R.FuncId, R.TSC, R.TId, + R.PId, R.CallArgs, R.Data}; + }); + return Error::success(); +} +} // namespace + +Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { + Expected<sys::fs::file_t> FdOrErr = sys::fs::openNativeFileForRead(Filename); + if (!FdOrErr) + return FdOrErr.takeError(); + + uint64_t FileSize; + if (auto EC = sys::fs::file_size(Filename, FileSize)) { + return make_error<StringError>( + Twine("Cannot read log from '") + Filename + "'", EC); + } + if (FileSize < 4) { + return make_error<StringError>( + Twine("File '") + Filename + "' too small for XRay.", + std::make_error_code(std::errc::executable_format_error)); + } + + // Map the opened file into memory and use a StringRef to access it later. + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + *FdOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, + EC); + sys::fs::closeFile(*FdOrErr); + if (EC) { + return make_error<StringError>( + Twine("Cannot read log from '") + Filename + "'", EC); + } + auto Data = StringRef(MappedFile.data(), MappedFile.size()); + + // TODO: Lift the endianness and implementation selection here. + DataExtractor LittleEndianDE(Data, true, 8); + auto TraceOrError = loadTrace(LittleEndianDE, Sort); + if (!TraceOrError) { + DataExtractor BigEndianDE(Data, false, 8); + consumeError(TraceOrError.takeError()); + TraceOrError = loadTrace(BigEndianDE, Sort); + } + return TraceOrError; +} + +Expected<Trace> llvm::xray::loadTrace(const DataExtractor &DE, bool Sort) { + // Attempt to detect the file type using file magic. We have a slight bias + // towards the binary format, and we do this by making sure that the first 4 + // bytes of the binary file is some combination of the following byte + // patterns: (observe the code loading them assumes they're little endian) + // + // 0x01 0x00 0x00 0x00 - version 1, "naive" format + // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format + // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format + // + // YAML files don't typically have those first four bytes as valid text so we + // try loading assuming YAML if we don't find these bytes. + // + // Only if we can't load either the binary or the YAML format will we yield an + // error. + DataExtractor HeaderExtractor(DE.getData(), DE.isLittleEndian(), 8); + uint64_t OffsetPtr = 0; + uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); + uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); + + enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; + + Trace T; + switch (Type) { + case NAIVE_FORMAT: + if (Version == 1 || Version == 2 || Version == 3) { + if (auto E = loadNaiveFormatLog(DE.getData(), DE.isLittleEndian(), + T.FileHeader, T.Records)) + return std::move(E); + } else { + return make_error<StringError>( + Twine("Unsupported version for Basic/Naive Mode logging: ") + + Twine(Version), + std::make_error_code(std::errc::executable_format_error)); + } + break; + case FLIGHT_DATA_RECORDER_FORMAT: + if (Version >= 1 && Version <= 5) { + if (auto E = loadFDRLog(DE.getData(), DE.isLittleEndian(), T.FileHeader, + T.Records)) + return std::move(E); + } else { + return make_error<StringError>( + Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), + std::make_error_code(std::errc::executable_format_error)); + } + break; + default: + if (auto E = loadYAMLLog(DE.getData(), T.FileHeader, T.Records)) + return std::move(E); + } + + if (Sort) + llvm::stable_sort(T.Records, [&](const XRayRecord &L, const XRayRecord &R) { + return L.TSC < R.TSC; + }); + + return std::move(T); +} diff --git a/contrib/libs/llvm16/lib/XRay/ya.make b/contrib/libs/llvm16/lib/XRay/ya.make new file mode 100644 index 00000000000..99bb4eefeb3 --- /dev/null +++ b/contrib/libs/llvm16/lib/XRay/ya.make @@ -0,0 +1,41 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE(Apache-2.0 WITH LLVM-exception) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( + contrib/libs/llvm16 + contrib/libs/llvm16/lib/Object + contrib/libs/llvm16/lib/Support + contrib/libs/llvm16/lib/TargetParser +) + +ADDINCL( + contrib/libs/llvm16/lib/XRay +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + BlockIndexer.cpp + BlockPrinter.cpp + BlockVerifier.cpp + FDRRecordProducer.cpp + FDRRecords.cpp + FDRTraceExpander.cpp + FDRTraceWriter.cpp + FileHeaderReader.cpp + InstrumentationMap.cpp + LogBuilderConsumer.cpp + Profile.cpp + RecordInitializer.cpp + RecordPrinter.cpp + Trace.cpp +) + +END() |
