353 lines
12 KiB
C++
353 lines
12 KiB
C++
|
//===- ExecutionContextTest.cpp - Debug Execution Context first impl ------===//
|
||
|
//
|
||
|
// 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 "mlir/Debug/ExecutionContext.h"
|
||
|
#include "mlir/Debug/BreakpointManagers/TagBreakpointManager.h"
|
||
|
#include "llvm/ADT/MapVector.h"
|
||
|
#include "gmock/gmock.h"
|
||
|
|
||
|
using namespace mlir;
|
||
|
using namespace mlir::tracing;
|
||
|
|
||
|
namespace {
|
||
|
struct DebuggerAction : public ActionImpl<DebuggerAction> {
|
||
|
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(DebuggerAction)
|
||
|
static constexpr StringLiteral tag = "debugger-action";
|
||
|
};
|
||
|
struct OtherAction : public ActionImpl<OtherAction> {
|
||
|
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAction)
|
||
|
static constexpr StringLiteral tag = "other-action";
|
||
|
};
|
||
|
struct ThirdAction : public ActionImpl<ThirdAction> {
|
||
|
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ThirdAction)
|
||
|
static constexpr StringLiteral tag = "third-action";
|
||
|
};
|
||
|
|
||
|
// Simple action that does nothing.
|
||
|
void noOp() {}
|
||
|
|
||
|
/// This test executes a stack of nested action and check that the backtrace is
|
||
|
/// as expect.
|
||
|
TEST(ExecutionContext, ActionActiveStackTest) {
|
||
|
|
||
|
// We'll break three time, once on each action, the backtraces should match
|
||
|
// each of the entries here.
|
||
|
std::vector<std::vector<StringRef>> expectedStacks = {
|
||
|
{DebuggerAction::tag},
|
||
|
{OtherAction::tag, DebuggerAction::tag},
|
||
|
{ThirdAction::tag, OtherAction::tag, DebuggerAction::tag}};
|
||
|
|
||
|
auto checkStacks = [&](const ActionActiveStack *backtrace,
|
||
|
const std::vector<StringRef> ¤tStack) {
|
||
|
ASSERT_EQ((int)currentStack.size(), backtrace->getDepth() + 1);
|
||
|
for (StringRef stackEntry : currentStack) {
|
||
|
ASSERT_NE(backtrace, nullptr);
|
||
|
ASSERT_EQ(stackEntry, backtrace->getAction().getTag());
|
||
|
backtrace = backtrace->getParent();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Step, ExecutionContext::Step, ExecutionContext::Apply};
|
||
|
int idx = 0;
|
||
|
StringRef current;
|
||
|
int currentDepth = -1;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
current = backtrace->getAction().getTag();
|
||
|
currentDepth = backtrace->getDepth();
|
||
|
checkStacks(backtrace, expectedStacks[idx]);
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
std::vector<TagBreakpoint *> breakpoints;
|
||
|
breakpoints.push_back(simpleManager.addBreakpoint(DebuggerAction::tag));
|
||
|
breakpoints.push_back(simpleManager.addBreakpoint(OtherAction::tag));
|
||
|
breakpoints.push_back(simpleManager.addBreakpoint(ThirdAction::tag));
|
||
|
|
||
|
auto third = [&]() {
|
||
|
EXPECT_EQ(current, ThirdAction::tag);
|
||
|
EXPECT_EQ(currentDepth, 2);
|
||
|
};
|
||
|
auto nested = [&]() {
|
||
|
EXPECT_EQ(current, OtherAction::tag);
|
||
|
EXPECT_EQ(currentDepth, 1);
|
||
|
executionCtx(third, ThirdAction{});
|
||
|
};
|
||
|
auto original = [&]() {
|
||
|
EXPECT_EQ(current, DebuggerAction::tag);
|
||
|
EXPECT_EQ(currentDepth, 0);
|
||
|
executionCtx(nested, OtherAction{});
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
executionCtx(original, DebuggerAction{});
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, DebuggerTest) {
|
||
|
// Check matching and non matching breakpoints, with various enable/disable
|
||
|
// schemes.
|
||
|
int match = 0;
|
||
|
auto onBreakpoint = [&match](const ActionActiveStack *backtrace) {
|
||
|
match++;
|
||
|
return ExecutionContext::Skip;
|
||
|
};
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
|
||
|
executionCtx(noOp, DebuggerAction{});
|
||
|
EXPECT_EQ(match, 0);
|
||
|
|
||
|
Breakpoint *dbgBreakpoint = simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
executionCtx(noOp, DebuggerAction{});
|
||
|
EXPECT_EQ(match, 1);
|
||
|
|
||
|
dbgBreakpoint->disable();
|
||
|
executionCtx(noOp, DebuggerAction{});
|
||
|
EXPECT_EQ(match, 1);
|
||
|
|
||
|
dbgBreakpoint->enable();
|
||
|
executionCtx(noOp, DebuggerAction{});
|
||
|
EXPECT_EQ(match, 2);
|
||
|
|
||
|
executionCtx(noOp, OtherAction{});
|
||
|
EXPECT_EQ(match, 2);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, ApplyTest) {
|
||
|
// Test the "apply" control.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Apply};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
auto callback = [&]() { EXPECT_EQ(counter, 1); };
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, SkipTest) {
|
||
|
// Test the "skip" control.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag,
|
||
|
DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Apply, ExecutionContext::Skip};
|
||
|
int idx = 0, counter = 0, executionCounter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
auto callback = [&]() { ++executionCounter; };
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
EXPECT_EQ(executionCounter, 1);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, StepApplyTest) {
|
||
|
// Test the "step" control with a nested action.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Step, ExecutionContext::Apply};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
auto nested = [&]() { EXPECT_EQ(counter, 2); };
|
||
|
auto original = [&]() {
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
executionCtx(nested, OtherAction{});
|
||
|
};
|
||
|
|
||
|
executionCtx(original, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, StepNothingInsideTest) {
|
||
|
// Test the "step" control without a nested action.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag,
|
||
|
DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Step, ExecutionContext::Step};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
auto callback = [&]() { EXPECT_EQ(counter, 1); };
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, NextTest) {
|
||
|
// Test the "next" control.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag,
|
||
|
DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Next, ExecutionContext::Next};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
auto callback = [&]() { EXPECT_EQ(counter, 1); };
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, FinishTest) {
|
||
|
// Test the "finish" control.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag,
|
||
|
DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Step, ExecutionContext::Finish,
|
||
|
ExecutionContext::Apply};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
auto nested = [&]() { EXPECT_EQ(counter, 2); };
|
||
|
auto original = [&]() {
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
executionCtx(nested, OtherAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
};
|
||
|
|
||
|
executionCtx(original, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 3);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, FinishBreakpointInNestedTest) {
|
||
|
// Test the "finish" control with a breakpoint in the nested action.
|
||
|
std::vector<StringRef> tagSequence = {OtherAction::tag, DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Finish, ExecutionContext::Apply};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(OtherAction::tag);
|
||
|
|
||
|
auto nested = [&]() { EXPECT_EQ(counter, 1); };
|
||
|
auto original = [&]() {
|
||
|
EXPECT_EQ(counter, 0);
|
||
|
executionCtx(nested, OtherAction{});
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
};
|
||
|
|
||
|
executionCtx(original, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, FinishNothingBackTest) {
|
||
|
// Test the "finish" control without a nested action.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Finish};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
auto callback = [&]() { EXPECT_EQ(counter, 1); };
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
|
||
|
executionCtx(callback, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
}
|
||
|
|
||
|
TEST(ExecutionContext, EnableDisableBreakpointOnCallback) {
|
||
|
// Test enabling and disabling breakpoints while executing the action.
|
||
|
std::vector<StringRef> tagSequence = {DebuggerAction::tag, ThirdAction::tag,
|
||
|
OtherAction::tag, DebuggerAction::tag};
|
||
|
std::vector<ExecutionContext::Control> controlSequence = {
|
||
|
ExecutionContext::Apply, ExecutionContext::Finish,
|
||
|
ExecutionContext::Finish, ExecutionContext::Apply};
|
||
|
int idx = 0, counter = 0;
|
||
|
auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
|
||
|
++counter;
|
||
|
EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());
|
||
|
return controlSequence[idx++];
|
||
|
};
|
||
|
|
||
|
TagBreakpointManager simpleManager;
|
||
|
ExecutionContext executionCtx(onBreakpoint);
|
||
|
executionCtx.addBreakpointManager(&simpleManager);
|
||
|
simpleManager.addBreakpoint(DebuggerAction::tag);
|
||
|
Breakpoint *toBeDisabled = simpleManager.addBreakpoint(OtherAction::tag);
|
||
|
|
||
|
auto third = [&]() { EXPECT_EQ(counter, 2); };
|
||
|
auto nested = [&]() {
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
executionCtx(third, ThirdAction{});
|
||
|
EXPECT_EQ(counter, 2);
|
||
|
};
|
||
|
auto original = [&]() {
|
||
|
EXPECT_EQ(counter, 1);
|
||
|
toBeDisabled->disable();
|
||
|
simpleManager.addBreakpoint(ThirdAction::tag);
|
||
|
executionCtx(nested, OtherAction{});
|
||
|
EXPECT_EQ(counter, 3);
|
||
|
};
|
||
|
|
||
|
executionCtx(original, DebuggerAction{});
|
||
|
EXPECT_EQ(counter, 4);
|
||
|
}
|
||
|
} // namespace
|