diff options
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/XRay/FDRRecordProducer.cpp | 117 | ||||
-rw-r--r-- | llvm/lib/XRay/FDRRecords.cpp | 34 | ||||
-rw-r--r-- | llvm/lib/XRay/FDRTraceWriter.cpp | 6 | ||||
-rw-r--r-- | llvm/lib/XRay/RecordInitializer.cpp | 54 |
4 files changed, 188 insertions, 23 deletions
diff --git a/llvm/lib/XRay/FDRRecordProducer.cpp b/llvm/lib/XRay/FDRRecordProducer.cpp index 122578010c4..25b3ee8af21 100644 --- a/llvm/lib/XRay/FDRRecordProducer.cpp +++ b/llvm/lib/XRay/FDRRecordProducer.cpp @@ -9,29 +9,32 @@ #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) { - // 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, - }; if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker)) return createStringError(std::make_error_code(std::errc::invalid_argument), @@ -70,9 +73,70 @@ metadataRecordType(const XRayFileHeader &Header, uint8_t T) { 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 %d.", 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 = dyn_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: // @@ -90,11 +154,8 @@ Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() { std::make_error_code(std::errc::executable_format_error), "Failed reading one byte from offset %d.", OffsetPtr); - // Set up our result record. - std::unique_ptr<Record> R; - // For metadata records, handle especially here. - if (FirstByte & 0x01) { + if (isMetadataIntroducer(FirstByte)) { auto LoadedType = FirstByte >> 1; auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType); if (!MetadataRecordOrErr) @@ -113,6 +174,22 @@ Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() { 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 %d (over-read by %d bytes); Record Type " + "= %s.", + OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes, + Record::kindToString(R->getRecordType()).data()); + + CurrentBufferBytes -= OffsetPtr - PreReadOffset; + } assert(R != nullptr); return std::move(R); } diff --git a/llvm/lib/XRay/FDRRecords.cpp b/llvm/lib/XRay/FDRRecords.cpp index 2b68a73686f..2a40d5e0622 100644 --- a/llvm/lib/XRay/FDRRecords.cpp +++ b/llvm/lib/XRay/FDRRecords.cpp @@ -29,5 +29,39 @@ 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/llvm/lib/XRay/FDRTraceWriter.cpp b/llvm/lib/XRay/FDRTraceWriter.cpp index d5f96979986..c5224f4be09 100644 --- a/llvm/lib/XRay/FDRTraceWriter.cpp +++ b/llvm/lib/XRay/FDRTraceWriter.cpp @@ -43,7 +43,9 @@ template <size_t Index> struct IndexedWriter { template <uint8_t Kind, class... Values> Error writeMetadata(support::endian::Writer &OS, Values &&... Ds) { - uint8_t FirstByte = (Kind << 1) | uint8_t{0x01}; + // 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); @@ -112,7 +114,7 @@ Error FDRTraceWriter::visit(CustomEventRecordV5 &R) { } Error FDRTraceWriter::visit(TypedEventRecord &R) { - if (auto E = writeMetadata<7u>(OS, R.size(), R.delta(), R.eventType())) + 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()); diff --git a/llvm/lib/XRay/RecordInitializer.cpp b/llvm/lib/XRay/RecordInitializer.cpp index cc9dd460949..f136a1e456b 100644 --- a/llvm/lib/XRay/RecordInitializer.cpp +++ b/llvm/lib/XRay/RecordInitializer.cpp @@ -111,6 +111,12 @@ Error RecordInitializer::visit(CustomEventRecord &R) { std::make_error_code(std::errc::invalid_argument), "Cannot read a custom event record size field offset %d.", OffsetPtr); + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for custom event (size = %d) at offset %d.", R.Size, + OffsetPtr); + PreReadOffset = OffsetPtr; R.TSC = E.getU64(&OffsetPtr); if (PreReadOffset == OffsetPtr) @@ -142,11 +148,21 @@ Error RecordInitializer::visit(CustomEventRecord &R) { 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 %d.", 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 %d " + "expecting %d bytes at offset %d.", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + R.Data.assign(Buffer.begin(), Buffer.end()); return Error::success(); } @@ -167,6 +183,12 @@ Error RecordInitializer::visit(CustomEventRecordV5 &R) { std::make_error_code(std::errc::invalid_argument), "Cannot read a custom event record size field offset %d.", OffsetPtr); + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for custom event (size = %d) at offset %d.", R.Size, + OffsetPtr); + PreReadOffset = OffsetPtr; R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t)); if (PreReadOffset == OffsetPtr) @@ -188,11 +210,21 @@ Error RecordInitializer::visit(CustomEventRecordV5 &R) { 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 %d.", 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 %d " + "expecting %d bytes at offset %d.", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + R.Data.assign(Buffer.begin(), Buffer.end()); return Error::success(); } @@ -213,6 +245,12 @@ Error RecordInitializer::visit(TypedEventRecord &R) { std::make_error_code(std::errc::invalid_argument), "Cannot read a typed event record size field offset %d.", OffsetPtr); + if (R.Size <= 0) + return createStringError( + std::make_error_code(std::errc::bad_address), + "Invalid size for typed event (size = %d) at offset %d.", R.Size, + OffsetPtr); + PreReadOffset = OffsetPtr; R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t)); if (PreReadOffset == OffsetPtr) @@ -241,11 +279,21 @@ Error RecordInitializer::visit(TypedEventRecord &R) { 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 %d.", 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 %d " + "expecting %d bytes at offset %d.", + OffsetPtr - PreReadOffset, R.Size, PreReadOffset); + R.Data.assign(Buffer.begin(), Buffer.end()); return Error::success(); } @@ -337,7 +385,11 @@ Error RecordInitializer::visit(FunctionRecord &R) { return createStringError(std::make_error_code(std::errc::bad_address), "Cannot read function id field from offset %d.", OffsetPtr); - unsigned FunctionType = (Buffer >> 1) & 0x07; + + // 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): |