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. |
32 | void noOp() {} |
33 | |
34 | /// This test executes a stack of nested action and check that the backtrace is |
35 | /// as expect. |
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> ¤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(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 | |
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(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 | |
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(manager: &simpleManager); |
140 | simpleManager.addBreakpoint(tag: DebuggerAction::tag); |
141 | |
142 | executionCtx(callback, DebuggerAction{}); |
143 | EXPECT_EQ(counter, 1); |
144 | } |
145 | |
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(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 | |
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(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 | |
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(manager: &simpleManager); |
211 | simpleManager.addBreakpoint(tag: DebuggerAction::tag); |
212 | |
213 | executionCtx(callback, DebuggerAction{}); |
214 | EXPECT_EQ(counter, 2); |
215 | } |
216 | |
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(manager: &simpleManager); |
233 | simpleManager.addBreakpoint(tag: DebuggerAction::tag); |
234 | |
235 | executionCtx(callback, DebuggerAction{}); |
236 | EXPECT_EQ(counter, 2); |
237 | } |
238 | |
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(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 | |
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(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 | |
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(manager: &simpleManager); |
309 | simpleManager.addBreakpoint(tag: DebuggerAction::tag); |
310 | |
311 | executionCtx(callback, DebuggerAction{}); |
312 | EXPECT_EQ(counter, 1); |
313 | } |
314 | |
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(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 | |