205 lines
6.8 KiB
C++
205 lines
6.8 KiB
C++
|
//===- 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
|