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