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
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 is
35/// as expect.
36TEST(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(manager: &simpleManager);
70 std::vector<TagBreakpoint *> breakpoints;
71 breakpoints.push_back(x: simpleManager.addBreakpoint(tag: DebuggerAction::tag));
72 breakpoints.push_back(x: simpleManager.addBreakpoint(tag: OtherAction::tag));
73 breakpoints.push_back(x: simpleManager.addBreakpoint(tag: 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/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(manager: &simpleManager);
105
106 executionCtx(noOp, DebuggerAction{});
107 EXPECT_EQ(match, 0);
108
109 Breakpoint *dbgBreakpoint = simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
140 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
162 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
184 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
211 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
233 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
255 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
281 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
309 simpleManager.addBreakpoint(tag: 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(manager: &simpleManager);
332 simpleManager.addBreakpoint(tag: DebuggerAction::tag);
333 Breakpoint *toBeDisabled = simpleManager.addBreakpoint(tag: 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(tag: 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

source code of mlir/unittests/Debug/ExecutionContextTest.cpp