1 | //===- FileLineColLocBreakpointManagerTest.cpp - --------------------------===// |
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/BreakpointManagers/FileLineColLocBreakpointManager.h" |
10 | #include "mlir/Debug/ExecutionContext.h" |
11 | #include "mlir/IR/Builders.h" |
12 | #include "mlir/IR/BuiltinAttributes.h" |
13 | #include "mlir/IR/BuiltinTypes.h" |
14 | #include "mlir/IR/Location.h" |
15 | #include "mlir/IR/OperationSupport.h" |
16 | #include "llvm/ADT/STLExtras.h" |
17 | #include "gtest/gtest.h" |
18 | |
19 | using namespace mlir; |
20 | using namespace mlir::tracing; |
21 | |
22 | static Operation *createOp(MLIRContext *context, Location loc, |
23 | StringRef operationName, |
24 | unsigned int numRegions = 0) { |
25 | context->allowUnregisteredDialects(); |
26 | return Operation::create(location: loc, name: OperationName(operationName, context), |
27 | resultTypes: std::nullopt, operands: std::nullopt, attributes: std::nullopt, |
28 | properties: OpaqueProperties(nullptr), successors: std::nullopt, numRegions); |
29 | } |
30 | |
31 | namespace { |
32 | struct FileLineColLocTestingAction |
33 | : public ActionImpl<FileLineColLocTestingAction> { |
34 | MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(FileLineColLocTestingAction) |
35 | static constexpr StringLiteral tag = "file-line-col-loc-testing-action" ; |
36 | FileLineColLocTestingAction(ArrayRef<IRUnit> irUnits) |
37 | : ActionImpl<FileLineColLocTestingAction>(irUnits) {} |
38 | }; |
39 | |
40 | TEST(FileLineColLocBreakpointManager, OperationMatch) { |
41 | // This test will process a sequence of operation and check various situation |
42 | // with a breakpoint hitting or not based on the location attached to the |
43 | // operation. When a breakpoint hits, the action is skipped and the counter is |
44 | // not incremented. |
45 | ExecutionContext executionCtx( |
46 | [](const ActionActiveStack *) { return ExecutionContext::Skip; }); |
47 | int counter = 0; |
48 | auto counterInc = [&]() { counter++; }; |
49 | |
50 | // Setup |
51 | |
52 | MLIRContext context; |
53 | // Miscellaneous information to define operations |
54 | std::vector<StringRef> fileNames = { |
55 | StringRef("foo.bar" ), StringRef("baz.qux" ), StringRef("quux.corge" )}; |
56 | std::vector<std::pair<unsigned, unsigned>> lineColLoc = {{42, 7}, {24, 3}}; |
57 | Location callee = UnknownLoc::get(&context), |
58 | caller = UnknownLoc::get(&context), loc = UnknownLoc::get(&context); |
59 | |
60 | // Set of operations over where we are going to be testing the functionality |
61 | std::vector<Operation *> operations = { |
62 | createOp(&context, CallSiteLoc::get(callee, caller), |
63 | "callSiteLocOperation" ), |
64 | createOp(&context, |
65 | FileLineColLoc::get(&context, fileNames[0], lineColLoc[0].first, |
66 | lineColLoc[0].second), |
67 | "fileLineColLocOperation" ), |
68 | createOp(&context, FusedLoc::get(&context, {}, Attribute()), |
69 | "fusedLocOperation" ), |
70 | createOp(&context, NameLoc::get(StringAttr::get(&context, fileNames[2])), |
71 | "nameLocOperation" ), |
72 | createOp(&context, OpaqueLoc::get<void *>(nullptr, loc), |
73 | "opaqueLocOperation" ), |
74 | createOp(&context, |
75 | FileLineColLoc::get(&context, fileNames[1], lineColLoc[1].first, |
76 | lineColLoc[1].second), |
77 | "anotherFileLineColLocOperation" ), |
78 | createOp(&context, UnknownLoc::get(&context), "unknownLocOperation" ), |
79 | }; |
80 | |
81 | FileLineColLocBreakpointManager breakpointManager; |
82 | executionCtx.addBreakpointManager(manager: &breakpointManager); |
83 | |
84 | // Test |
85 | |
86 | // Basic case is that no breakpoint is set and the counter is incremented for |
87 | // every op. |
88 | auto checkNoMatch = [&]() { |
89 | counter = 0; |
90 | for (auto enumeratedOp : llvm::enumerate(First&: operations)) { |
91 | executionCtx(counterInc, |
92 | FileLineColLocTestingAction({enumeratedOp.value()})); |
93 | EXPECT_EQ(counter, static_cast<int>(enumeratedOp.index() + 1)); |
94 | } |
95 | }; |
96 | checkNoMatch(); |
97 | |
98 | // Set a breakpoint matching only the second operation in the list. |
99 | auto *breakpoint = breakpointManager.addBreakpoint( |
100 | file: fileNames[0], line: lineColLoc[0].first, col: lineColLoc[0].second); |
101 | auto checkMatchIdxs = [&](const DenseSet<int> &idxs) { |
102 | counter = 0; |
103 | int reference = 0; |
104 | for (int i = 0; i < (int)operations.size(); ++i) { |
105 | executionCtx(counterInc, FileLineColLocTestingAction({operations[i]})); |
106 | if (!idxs.contains(V: i)) |
107 | reference++; |
108 | EXPECT_EQ(counter, reference); |
109 | } |
110 | }; |
111 | checkMatchIdxs({1}); |
112 | |
113 | // Check that disabling the breakpoing brings us back to the original |
114 | // behavior. |
115 | breakpoint->disable(); |
116 | checkNoMatch(); |
117 | |
118 | // Adding a breakpoint that won't match any location shouldn't affect the |
119 | // behavior. |
120 | breakpointManager.addBreakpoint(file: StringRef("random.file" ), line: 3, col: 14); |
121 | checkNoMatch(); |
122 | |
123 | // Set a breakpoint matching only the fifth operation in the list. |
124 | breakpointManager.addBreakpoint(file: fileNames[1], line: lineColLoc[1].first, |
125 | col: lineColLoc[1].second); |
126 | counter = 0; |
127 | checkMatchIdxs({5}); |
128 | |
129 | // Re-enable the breakpoint matching only the second operation in the list. |
130 | // We now expect matching of operations 1 and 5. |
131 | breakpoint->enable(); |
132 | checkMatchIdxs({1, 5}); |
133 | |
134 | for (auto *op : operations) { |
135 | op->destroy(); |
136 | } |
137 | } |
138 | |
139 | TEST(FileLineColLocBreakpointManager, BlockMatch) { |
140 | // This test will process a block and check various situation with |
141 | // a breakpoint hitting or not based on the location attached. |
142 | // When a breakpoint hits, the action is skipped and the counter is not |
143 | // incremented. |
144 | ExecutionContext executionCtx( |
145 | [](const ActionActiveStack *) { return ExecutionContext::Skip; }); |
146 | int counter = 0; |
147 | auto counterInc = [&]() { counter++; }; |
148 | |
149 | // Setup |
150 | |
151 | MLIRContext context; |
152 | std::vector<StringRef> fileNames = {StringRef("grault.garply" ), |
153 | StringRef("waldo.fred" )}; |
154 | std::vector<std::pair<unsigned, unsigned>> lineColLoc = {{42, 7}, {24, 3}}; |
155 | Operation *frontOp = createOp(&context, |
156 | FileLineColLoc::get(&context, fileNames.front(), |
157 | lineColLoc.front().first, |
158 | lineColLoc.front().second), |
159 | "firstOperation" ); |
160 | Operation *backOp = createOp(&context, |
161 | FileLineColLoc::get(&context, fileNames.back(), |
162 | lineColLoc.back().first, |
163 | lineColLoc.back().second), |
164 | "secondOperation" ); |
165 | Block block; |
166 | block.push_back(op: frontOp); |
167 | block.push_back(op: backOp); |
168 | |
169 | FileLineColLocBreakpointManager breakpointManager; |
170 | executionCtx.addBreakpointManager(manager: &breakpointManager); |
171 | |
172 | // Test |
173 | |
174 | executionCtx(counterInc, FileLineColLocTestingAction({&block})); |
175 | EXPECT_EQ(counter, 1); |
176 | |
177 | auto *breakpoint = breakpointManager.addBreakpoint( |
178 | file: fileNames.front(), line: lineColLoc.front().first, col: lineColLoc.front().second); |
179 | counter = 0; |
180 | executionCtx(counterInc, FileLineColLocTestingAction({&block})); |
181 | EXPECT_EQ(counter, 0); |
182 | breakpoint->disable(); |
183 | executionCtx(counterInc, FileLineColLocTestingAction({&block})); |
184 | EXPECT_EQ(counter, 1); |
185 | |
186 | breakpoint = breakpointManager.addBreakpoint( |
187 | file: fileNames.back(), line: lineColLoc.back().first, col: lineColLoc.back().second); |
188 | counter = 0; |
189 | executionCtx(counterInc, FileLineColLocTestingAction({&block})); |
190 | EXPECT_EQ(counter, 0); |
191 | breakpoint->disable(); |
192 | executionCtx(counterInc, FileLineColLocTestingAction({&block})); |
193 | EXPECT_EQ(counter, 1); |
194 | } |
195 | |
196 | TEST(FileLineColLocBreakpointManager, RegionMatch) { |
197 | // This test will process a region and check various situation with |
198 | // a breakpoint hitting or not based on the location attached. |
199 | // When a breakpoint hits, the action is skipped and the counter is not |
200 | // incremented. |
201 | ExecutionContext executionCtx( |
202 | [](const ActionActiveStack *) { return ExecutionContext::Skip; }); |
203 | int counter = 0; |
204 | auto counterInc = [&]() { counter++; }; |
205 | |
206 | // Setup |
207 | |
208 | MLIRContext context; |
209 | StringRef fileName("plugh.xyzzy" ); |
210 | unsigned line = 42, col = 7; |
211 | Operation *containerOp = |
212 | createOp(&context, FileLineColLoc::get(&context, fileName, line, col), |
213 | "containerOperation" , 1); |
214 | Region ®ion = containerOp->getRegion(index: 0); |
215 | |
216 | FileLineColLocBreakpointManager breakpointManager; |
217 | executionCtx.addBreakpointManager(manager: &breakpointManager); |
218 | |
219 | // Test |
220 | counter = 0; |
221 | executionCtx(counterInc, FileLineColLocTestingAction({®ion})); |
222 | EXPECT_EQ(counter, 1); |
223 | auto *breakpoint = breakpointManager.addBreakpoint(file: fileName, line, col); |
224 | executionCtx(counterInc, FileLineColLocTestingAction({®ion})); |
225 | EXPECT_EQ(counter, 1); |
226 | breakpoint->disable(); |
227 | executionCtx(counterInc, FileLineColLocTestingAction({®ion})); |
228 | EXPECT_EQ(counter, 2); |
229 | |
230 | containerOp->destroy(); |
231 | } |
232 | } // namespace |
233 | |