353 lines · cpp
1//===- ExecutionContextTest.cpp - Debug Execution Context first impl ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Debug/ExecutionContext.h"10#include "mlir/Debug/BreakpointManagers/TagBreakpointManager.h"11#include "llvm/ADT/MapVector.h"12#include "gmock/gmock.h"13 14using namespace mlir;15using namespace mlir::tracing;16 17namespace {18struct DebuggerAction : public ActionImpl<DebuggerAction> {19 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(DebuggerAction)20 static constexpr StringLiteral tag = "debugger-action";21};22struct OtherAction : public ActionImpl<OtherAction> {23 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAction)24 static constexpr StringLiteral tag = "other-action";25};26struct ThirdAction : public ActionImpl<ThirdAction> {27 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ThirdAction)28 static constexpr StringLiteral tag = "third-action";29};30 31// Simple action that does nothing.32void noOp() {}33 34/// This test executes a stack of nested action and check that the backtrace is35/// as expect.36TEST(ExecutionContext, ActionActiveStackTest) {37 38 // We'll break three time, once on each action, the backtraces should match39 // each of the entries here.40 std::vector<std::vector<StringRef>> expectedStacks = {41 {DebuggerAction::tag},42 {OtherAction::tag, DebuggerAction::tag},43 {ThirdAction::tag, OtherAction::tag, DebuggerAction::tag}};44 45 auto checkStacks = [&](const ActionActiveStack *backtrace,46 const std::vector<StringRef> ¤tStack) {47 ASSERT_EQ((int)currentStack.size(), backtrace->getDepth() + 1);48 for (StringRef stackEntry : currentStack) {49 ASSERT_NE(backtrace, nullptr);50 ASSERT_EQ(stackEntry, backtrace->getAction().getTag());51 backtrace = backtrace->getParent();52 }53 };54 55 std::vector<ExecutionContext::Control> controlSequence = {56 ExecutionContext::Step, ExecutionContext::Step, ExecutionContext::Apply};57 int idx = 0;58 StringRef current;59 int currentDepth = -1;60 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {61 current = backtrace->getAction().getTag();62 currentDepth = backtrace->getDepth();63 checkStacks(backtrace, expectedStacks[idx]);64 return controlSequence[idx++];65 };66 67 TagBreakpointManager simpleManager;68 ExecutionContext executionCtx(onBreakpoint);69 executionCtx.addBreakpointManager(&simpleManager);70 std::vector<TagBreakpoint *> breakpoints;71 breakpoints.push_back(simpleManager.addBreakpoint(DebuggerAction::tag));72 breakpoints.push_back(simpleManager.addBreakpoint(OtherAction::tag));73 breakpoints.push_back(simpleManager.addBreakpoint(ThirdAction::tag));74 75 auto third = [&]() {76 EXPECT_EQ(current, ThirdAction::tag);77 EXPECT_EQ(currentDepth, 2);78 };79 auto nested = [&]() {80 EXPECT_EQ(current, OtherAction::tag);81 EXPECT_EQ(currentDepth, 1);82 executionCtx(third, ThirdAction{});83 };84 auto original = [&]() {85 EXPECT_EQ(current, DebuggerAction::tag);86 EXPECT_EQ(currentDepth, 0);87 executionCtx(nested, OtherAction{});88 return;89 };90 91 executionCtx(original, DebuggerAction{});92}93 94TEST(ExecutionContext, DebuggerTest) {95 // Check matching and non matching breakpoints, with various enable/disable96 // schemes.97 int match = 0;98 auto onBreakpoint = [&match](const ActionActiveStack *backtrace) {99 match++;100 return ExecutionContext::Skip;101 };102 TagBreakpointManager simpleManager;103 ExecutionContext executionCtx(onBreakpoint);104 executionCtx.addBreakpointManager(&simpleManager);105 106 executionCtx(noOp, DebuggerAction{});107 EXPECT_EQ(match, 0);108 109 Breakpoint *dbgBreakpoint = simpleManager.addBreakpoint(DebuggerAction::tag);110 executionCtx(noOp, DebuggerAction{});111 EXPECT_EQ(match, 1);112 113 dbgBreakpoint->disable();114 executionCtx(noOp, DebuggerAction{});115 EXPECT_EQ(match, 1);116 117 dbgBreakpoint->enable();118 executionCtx(noOp, DebuggerAction{});119 EXPECT_EQ(match, 2);120 121 executionCtx(noOp, OtherAction{});122 EXPECT_EQ(match, 2);123}124 125TEST(ExecutionContext, ApplyTest) {126 // Test the "apply" control.127 std::vector<StringRef> tagSequence = {DebuggerAction::tag};128 std::vector<ExecutionContext::Control> controlSequence = {129 ExecutionContext::Apply};130 int idx = 0, counter = 0;131 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {132 ++counter;133 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());134 return controlSequence[idx++];135 };136 auto callback = [&]() { EXPECT_EQ(counter, 1); };137 TagBreakpointManager simpleManager;138 ExecutionContext executionCtx(onBreakpoint);139 executionCtx.addBreakpointManager(&simpleManager);140 simpleManager.addBreakpoint(DebuggerAction::tag);141 142 executionCtx(callback, DebuggerAction{});143 EXPECT_EQ(counter, 1);144}145 146TEST(ExecutionContext, SkipTest) {147 // Test the "skip" control.148 std::vector<StringRef> tagSequence = {DebuggerAction::tag,149 DebuggerAction::tag};150 std::vector<ExecutionContext::Control> controlSequence = {151 ExecutionContext::Apply, ExecutionContext::Skip};152 int idx = 0, counter = 0, executionCounter = 0;153 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {154 ++counter;155 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());156 return controlSequence[idx++];157 };158 auto callback = [&]() { ++executionCounter; };159 TagBreakpointManager simpleManager;160 ExecutionContext executionCtx(onBreakpoint);161 executionCtx.addBreakpointManager(&simpleManager);162 simpleManager.addBreakpoint(DebuggerAction::tag);163 164 executionCtx(callback, DebuggerAction{});165 executionCtx(callback, DebuggerAction{});166 EXPECT_EQ(counter, 2);167 EXPECT_EQ(executionCounter, 1);168}169 170TEST(ExecutionContext, StepApplyTest) {171 // Test the "step" control with a nested action.172 std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag};173 std::vector<ExecutionContext::Control> controlSequence = {174 ExecutionContext::Step, ExecutionContext::Apply};175 int idx = 0, counter = 0;176 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {177 ++counter;178 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());179 return controlSequence[idx++];180 };181 TagBreakpointManager simpleManager;182 ExecutionContext executionCtx(onBreakpoint);183 executionCtx.addBreakpointManager(&simpleManager);184 simpleManager.addBreakpoint(DebuggerAction::tag);185 auto nested = [&]() { EXPECT_EQ(counter, 2); };186 auto original = [&]() {187 EXPECT_EQ(counter, 1);188 executionCtx(nested, OtherAction{});189 };190 191 executionCtx(original, DebuggerAction{});192 EXPECT_EQ(counter, 2);193}194 195TEST(ExecutionContext, StepNothingInsideTest) {196 // Test the "step" control without a nested action.197 std::vector<StringRef> tagSequence = {DebuggerAction::tag,198 DebuggerAction::tag};199 std::vector<ExecutionContext::Control> controlSequence = {200 ExecutionContext::Step, ExecutionContext::Step};201 int idx = 0, counter = 0;202 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {203 ++counter;204 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());205 return controlSequence[idx++];206 };207 auto callback = [&]() { EXPECT_EQ(counter, 1); };208 TagBreakpointManager simpleManager;209 ExecutionContext executionCtx(onBreakpoint);210 executionCtx.addBreakpointManager(&simpleManager);211 simpleManager.addBreakpoint(DebuggerAction::tag);212 213 executionCtx(callback, DebuggerAction{});214 EXPECT_EQ(counter, 2);215}216 217TEST(ExecutionContext, NextTest) {218 // Test the "next" control.219 std::vector<StringRef> tagSequence = {DebuggerAction::tag,220 DebuggerAction::tag};221 std::vector<ExecutionContext::Control> controlSequence = {222 ExecutionContext::Next, ExecutionContext::Next};223 int idx = 0, counter = 0;224 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {225 ++counter;226 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());227 return controlSequence[idx++];228 };229 auto callback = [&]() { EXPECT_EQ(counter, 1); };230 TagBreakpointManager simpleManager;231 ExecutionContext executionCtx(onBreakpoint);232 executionCtx.addBreakpointManager(&simpleManager);233 simpleManager.addBreakpoint(DebuggerAction::tag);234 235 executionCtx(callback, DebuggerAction{});236 EXPECT_EQ(counter, 2);237}238 239TEST(ExecutionContext, FinishTest) {240 // Test the "finish" control.241 std::vector<StringRef> tagSequence = {DebuggerAction::tag, OtherAction::tag,242 DebuggerAction::tag};243 std::vector<ExecutionContext::Control> controlSequence = {244 ExecutionContext::Step, ExecutionContext::Finish,245 ExecutionContext::Apply};246 int idx = 0, counter = 0;247 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {248 ++counter;249 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());250 return controlSequence[idx++];251 };252 TagBreakpointManager simpleManager;253 ExecutionContext executionCtx(onBreakpoint);254 executionCtx.addBreakpointManager(&simpleManager);255 simpleManager.addBreakpoint(DebuggerAction::tag);256 auto nested = [&]() { EXPECT_EQ(counter, 2); };257 auto original = [&]() {258 EXPECT_EQ(counter, 1);259 executionCtx(nested, OtherAction{});260 EXPECT_EQ(counter, 2);261 };262 263 executionCtx(original, DebuggerAction{});264 EXPECT_EQ(counter, 3);265}266 267TEST(ExecutionContext, FinishBreakpointInNestedTest) {268 // Test the "finish" control with a breakpoint in the nested action.269 std::vector<StringRef> tagSequence = {OtherAction::tag, DebuggerAction::tag};270 std::vector<ExecutionContext::Control> controlSequence = {271 ExecutionContext::Finish, ExecutionContext::Apply};272 int idx = 0, counter = 0;273 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {274 ++counter;275 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());276 return controlSequence[idx++];277 };278 TagBreakpointManager simpleManager;279 ExecutionContext executionCtx(onBreakpoint);280 executionCtx.addBreakpointManager(&simpleManager);281 simpleManager.addBreakpoint(OtherAction::tag);282 283 auto nested = [&]() { EXPECT_EQ(counter, 1); };284 auto original = [&]() {285 EXPECT_EQ(counter, 0);286 executionCtx(nested, OtherAction{});287 EXPECT_EQ(counter, 1);288 };289 290 executionCtx(original, DebuggerAction{});291 EXPECT_EQ(counter, 2);292}293 294TEST(ExecutionContext, FinishNothingBackTest) {295 // Test the "finish" control without a nested action.296 std::vector<StringRef> tagSequence = {DebuggerAction::tag};297 std::vector<ExecutionContext::Control> controlSequence = {298 ExecutionContext::Finish};299 int idx = 0, counter = 0;300 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {301 ++counter;302 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());303 return controlSequence[idx++];304 };305 auto callback = [&]() { EXPECT_EQ(counter, 1); };306 TagBreakpointManager simpleManager;307 ExecutionContext executionCtx(onBreakpoint);308 executionCtx.addBreakpointManager(&simpleManager);309 simpleManager.addBreakpoint(DebuggerAction::tag);310 311 executionCtx(callback, DebuggerAction{});312 EXPECT_EQ(counter, 1);313}314 315TEST(ExecutionContext, EnableDisableBreakpointOnCallback) {316 // Test enabling and disabling breakpoints while executing the action.317 std::vector<StringRef> tagSequence = {DebuggerAction::tag, ThirdAction::tag,318 OtherAction::tag, DebuggerAction::tag};319 std::vector<ExecutionContext::Control> controlSequence = {320 ExecutionContext::Apply, ExecutionContext::Finish,321 ExecutionContext::Finish, ExecutionContext::Apply};322 int idx = 0, counter = 0;323 auto onBreakpoint = [&](const ActionActiveStack *backtrace) {324 ++counter;325 EXPECT_EQ(tagSequence[idx], backtrace->getAction().getTag());326 return controlSequence[idx++];327 };328 329 TagBreakpointManager simpleManager;330 ExecutionContext executionCtx(onBreakpoint);331 executionCtx.addBreakpointManager(&simpleManager);332 simpleManager.addBreakpoint(DebuggerAction::tag);333 Breakpoint *toBeDisabled = simpleManager.addBreakpoint(OtherAction::tag);334 335 auto third = [&]() { EXPECT_EQ(counter, 2); };336 auto nested = [&]() {337 EXPECT_EQ(counter, 1);338 executionCtx(third, ThirdAction{});339 EXPECT_EQ(counter, 2);340 };341 auto original = [&]() {342 EXPECT_EQ(counter, 1);343 toBeDisabled->disable();344 simpleManager.addBreakpoint(ThirdAction::tag);345 executionCtx(nested, OtherAction{});346 EXPECT_EQ(counter, 3);347 };348 349 executionCtx(original, DebuggerAction{});350 EXPECT_EQ(counter, 4);351}352} // namespace353