| //===- 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 |