xref: /llvm-project/mlir/unittests/Debug/ExecutionContextTest.cpp (revision ad09cd3f0dad5d34b803c85c94c048a2f6a89d0d)
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-exception
6 //
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 
14 using namespace mlir;
15 using namespace mlir::tracing;
16 
17 namespace {
18 struct DebuggerAction : public ActionImpl<DebuggerAction> {
19   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(DebuggerAction)
20   static constexpr StringLiteral tag = "debugger-action";
21 };
22 struct OtherAction : public ActionImpl<OtherAction> {
23   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OtherAction)
24   static constexpr StringLiteral tag = "other-action";
25 };
26 struct 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.
noOp()32 void noOp() {}
33 
34 /// This test executes a stack of nested action and check that the backtrace is
35 /// as expect.
TEST(ExecutionContext,ActionActiveStackTest)36 TEST(ExecutionContext, ActionActiveStackTest) {
37 
38   // We'll break three time, once on each action, the backtraces should match
39   // 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> &currentStack) {
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 
TEST(ExecutionContext,DebuggerTest)94 TEST(ExecutionContext, DebuggerTest) {
95   // Check matching and non matching breakpoints, with various enable/disable
96   // 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 
TEST(ExecutionContext,ApplyTest)125 TEST(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 
TEST(ExecutionContext,SkipTest)146 TEST(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 
TEST(ExecutionContext,StepApplyTest)170 TEST(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 
TEST(ExecutionContext,StepNothingInsideTest)195 TEST(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 
TEST(ExecutionContext,NextTest)217 TEST(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 
TEST(ExecutionContext,FinishTest)239 TEST(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 
TEST(ExecutionContext,FinishBreakpointInNestedTest)267 TEST(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 
TEST(ExecutionContext,FinishNothingBackTest)294 TEST(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 
TEST(ExecutionContext,EnableDisableBreakpointOnCallback)315 TEST(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 } // namespace
353