MLIR 22.0.0git
DebuggerExecutionContextHook.cpp
Go to the documentation of this file.
1//===- DebuggerExecutionContextHook.cpp - Debugger Support ----------------===//
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
10
13
14using namespace mlir;
15using namespace mlir::tracing;
16
17namespace {
18/// This structure tracks the state of the interactive debugger.
19struct DebuggerState {
20 /// This variable keeps track of the current control option. This is set by
21 /// the debugger when control is handed over to it.
23
24 /// The breakpoint manager that allows the debugger to set breakpoints on
25 /// action tags.
26 TagBreakpointManager tagBreakpointManager;
27
28 /// The breakpoint manager that allows the debugger to set breakpoints on
29 /// FileLineColLoc locations.
30 FileLineColLocBreakpointManager fileLineColLocBreakpointManager;
31
32 /// Map of breakpoint IDs to breakpoint objects.
33 DenseMap<unsigned, Breakpoint *> breakpointIdsMap;
34
35 /// The current stack of actiive actions.
36 const tracing::ActionActiveStack *actionActiveStack;
37
38 /// This is a "cursor" in the IR, it is used for the debugger to navigate the
39 /// IR associated to the actions.
40 IRUnit cursor;
41};
42} // namespace
43
44static DebuggerState &getGlobalDebuggerState() {
45 static LLVM_THREAD_LOCAL DebuggerState debuggerState;
46 return debuggerState;
47}
48
49extern "C" {
50void mlirDebuggerSetControl(int controlOption) {
51 getGlobalDebuggerState().debuggerControl =
52 static_cast<ExecutionContext::Control>(controlOption);
53}
54
56 DebuggerState &state = getGlobalDebuggerState();
57 if (!state.actionActiveStack) {
58 llvm::outs() << "No active action.\n";
59 return;
60 }
61 const ArrayRef<IRUnit> &units =
62 state.actionActiveStack->getAction().getContextIRUnits();
63 llvm::outs() << units.size() << " available IRUnits:\n";
64 for (const IRUnit &unit : units) {
65 llvm::outs() << " - ";
66 unit.print(
67 llvm::outs(),
68 OpPrintingFlags().useLocalScope().skipRegions().enableDebugInfo());
69 llvm::outs() << "\n";
70 }
71}
72
73void mlirDebuggerPrintActionBacktrace(bool withContext) {
74 DebuggerState &state = getGlobalDebuggerState();
75 if (!state.actionActiveStack) {
76 llvm::outs() << "No active action.\n";
77 return;
78 }
79 state.actionActiveStack->print(llvm::outs(), withContext);
80}
81
82//===----------------------------------------------------------------------===//
83// Cursor Management
84//===----------------------------------------------------------------------===//
85
86void mlirDebuggerCursorPrint(bool withRegion) {
87 auto &state = getGlobalDebuggerState();
88 if (!state.cursor) {
89 llvm::outs() << "No active MLIR cursor, select from the context first\n";
90 return;
91 }
92 state.cursor.print(llvm::outs(), OpPrintingFlags()
93 .skipRegions(!withRegion)
94 .useLocalScope()
95 .enableDebugInfo());
96 llvm::outs() << "\n";
97}
98
100 auto &state = getGlobalDebuggerState();
101 if (!state.actionActiveStack) {
102 llvm::outs() << "No active MLIR Action stack\n";
103 return;
104 }
105 ArrayRef<IRUnit> units =
106 state.actionActiveStack->getAction().getContextIRUnits();
107 if (index < 0 || index >= static_cast<int>(units.size())) {
108 llvm::outs() << "Index invalid, bounds: [0, " << units.size()
109 << "] but got " << index << "\n";
110 return;
111 }
112 state.cursor = units[index];
113 state.cursor.print(llvm::outs());
114 llvm::outs() << "\n";
115}
116
118 auto &state = getGlobalDebuggerState();
119 if (!state.cursor) {
120 llvm::outs() << "No active MLIR cursor, select from the context first\n";
121 return;
122 }
123 IRUnit *unit = &state.cursor;
124 if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) {
125 state.cursor = op->getBlock();
126 } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) {
127 state.cursor = region->getParentOp();
128 } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) {
129 state.cursor = block->getParent();
130 } else {
131 llvm::outs() << "Current cursor is not a valid IRUnit";
132 return;
133 }
134 state.cursor.print(llvm::outs());
135 llvm::outs() << "\n";
136}
137
139 auto &state = getGlobalDebuggerState();
140 if (!state.cursor) {
141 llvm::outs() << "No active MLIR cursor, select from the context first\n";
142 return;
143 }
144 IRUnit *unit = &state.cursor;
145 if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) {
146 if (index < 0 || index >= static_cast<int>(op->getNumRegions())) {
147 llvm::outs() << "Index invalid, op has " << op->getNumRegions()
148 << " but got " << index << "\n";
149 return;
150 }
151 state.cursor = &op->getRegion(index);
152 } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) {
153 auto block = region->begin();
154 int count = 0;
155 while (block != region->end() && count != index) {
156 ++block;
157 ++count;
158 }
159
160 if (block == region->end()) {
161 llvm::outs() << "Index invalid, region has " << count << " block but got "
162 << index << "\n";
163 return;
164 }
165 state.cursor = &*block;
166 } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) {
167 auto op = block->begin();
168 int count = 0;
169 while (op != block->end() && count != index) {
170 ++op;
171 ++count;
172 }
173
174 if (op == block->end()) {
175 llvm::outs() << "Index invalid, block has " << count
176 << "operations but got " << index << "\n";
177 return;
178 }
179 state.cursor = &*op;
180 } else {
181 llvm::outs() << "Current cursor is not a valid IRUnit";
182 return;
183 }
184 state.cursor.print(llvm::outs());
185 llvm::outs() << "\n";
186}
187
189 auto &state = getGlobalDebuggerState();
190 if (!state.cursor) {
191 llvm::outs() << "No active MLIR cursor, select from the context first\n";
192 return;
193 }
194 IRUnit *unit = &state.cursor;
195 if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) {
196 Operation *previous = op->getPrevNode();
197 if (!previous) {
198 llvm::outs() << "No previous operation in the current block\n";
199 return;
200 }
201 state.cursor = previous;
202 } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) {
203 llvm::outs() << "Has region\n";
204 Operation *parent = region->getParentOp();
205 if (!parent) {
206 llvm::outs() << "No parent operation for the current region\n";
207 return;
208 }
209 if (region->getRegionNumber() == 0) {
210 llvm::outs() << "No previous region in the current operation\n";
211 return;
212 }
213 state.cursor =
214 &region->getParentOp()->getRegion(region->getRegionNumber() - 1);
215 } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) {
216 Block *previous = block->getPrevNode();
217 if (!previous) {
218 llvm::outs() << "No previous block in the current region\n";
219 return;
220 }
221 state.cursor = previous;
222 } else {
223 llvm::outs() << "Current cursor is not a valid IRUnit";
224 return;
225 }
226 state.cursor.print(llvm::outs());
227 llvm::outs() << "\n";
228}
229
231 auto &state = getGlobalDebuggerState();
232 if (!state.cursor) {
233 llvm::outs() << "No active MLIR cursor, select from the context first\n";
234 return;
235 }
236 IRUnit *unit = &state.cursor;
237 if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) {
238 Operation *next = op->getNextNode();
239 if (!next) {
240 llvm::outs() << "No next operation in the current block\n";
241 return;
242 }
243 state.cursor = next;
244 } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) {
245 Operation *parent = region->getParentOp();
246 if (!parent) {
247 llvm::outs() << "No parent operation for the current region\n";
248 return;
249 }
250 if (region->getRegionNumber() == parent->getNumRegions() - 1) {
251 llvm::outs() << "No next region in the current operation\n";
252 return;
253 }
254 state.cursor =
255 &region->getParentOp()->getRegion(region->getRegionNumber() + 1);
256 } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) {
257 Block *next = block->getNextNode();
258 if (!next) {
259 llvm::outs() << "No next block in the current region\n";
260 return;
261 }
262 state.cursor = next;
263 } else {
264 llvm::outs() << "Current cursor is not a valid IRUnit";
265 return;
266 }
267 state.cursor.print(llvm::outs());
268 llvm::outs() << "\n";
269}
270
271//===----------------------------------------------------------------------===//
272// Breakpoint Management
273//===----------------------------------------------------------------------===//
274
276 reinterpret_cast<Breakpoint *>(breakpoint)->enable();
277}
278
280 reinterpret_cast<Breakpoint *>(breakpoint)->disable();
281}
282
284 DebuggerState &state = getGlobalDebuggerState();
285 Breakpoint *breakpoint =
286 state.tagBreakpointManager.addBreakpoint(StringRef(tag, strlen(tag)));
287 int breakpointId = state.breakpointIdsMap.size() + 1;
288 state.breakpointIdsMap[breakpointId] = breakpoint;
289 return reinterpret_cast<BreakpointHandle>(breakpoint);
290}
291
292void mlirDebuggerAddRewritePatternBreakpoint(const char *patternNameInfo) {}
293
294void mlirDebuggerAddFileLineColLocBreakpoint(const char *file, int line,
295 int col) {
296 getGlobalDebuggerState().fileLineColLocBreakpointManager.addBreakpoint(
297 StringRef(file, strlen(file)), line, col);
298}
299
300} // extern "C"
301
302LLVM_ATTRIBUTE_NOINLINE void mlirDebuggerBreakpointHook() {
303 static LLVM_THREAD_LOCAL void *volatile sink;
304 sink = static_cast<void *>(const_cast<void **>(&sink));
305}
306
308 static void *volatile sink;
309 static bool initialized = [&]() {
310 sink = (void *)mlirDebuggerSetControl;
311 sink = (void *)mlirDebuggerEnableBreakpoint;
312 sink = (void *)mlirDebuggerDisableBreakpoint;
313 sink = (void *)mlirDebuggerPrintContext;
315 sink = (void *)mlirDebuggerCursorPrint;
321 sink = (void *)mlirDebuggerAddTagBreakpoint;
324 sink = static_cast<void *>(const_cast<void **>(&sink));
325 return true;
326 }();
327 (void)initialized;
328}
329
333 // Invoke the breakpoint hook, the debugger is supposed to trap this.
334 // The debugger controls the execution from there by invoking
335 // `mlirDebuggerSetControl()`.
336 auto &state = getGlobalDebuggerState();
337 state.actionActiveStack = actionStack;
339 actionStack->getAction().print(llvm::outs());
340 llvm::outs() << "\n";
342 return getGlobalDebuggerState().debuggerControl;
343}
344
345namespace {
346/// Manage the stack of actions that are currently active.
347class DebuggerObserver : public ExecutionContext::Observer {
348 void beforeExecute(const ActionActiveStack *action, Breakpoint *breakpoint,
349 bool willExecute) override {
350 auto &state = getGlobalDebuggerState();
351 state.actionActiveStack = action;
352 }
353 void afterExecute(const ActionActiveStack *action) override {
354 auto &state = getGlobalDebuggerState();
355 state.actionActiveStack = action->getParent();
356 state.cursor = nullptr;
357 }
358};
359} // namespace
360
362 tracing::ExecutionContext &executionContext) {
363 executionContext.setCallback(debuggerCallBackFunction);
364 DebuggerState &state = getGlobalDebuggerState();
365 static DebuggerObserver observer;
366 executionContext.registerObserver(&observer);
367 executionContext.addBreakpointManager(&state.fileLineColLocBreakpointManager);
368 executionContext.addBreakpointManager(&state.tagBreakpointManager);
369}
void mlirDebuggerEnableBreakpoint(BreakpointHandle breakpoint)
Enable the provided breakpoint.
void mlirDebuggerCursorPrint(bool withRegion)
Print the current IR unit cursor.
void mlirDebuggerPrintActionBacktrace(bool withContext)
Print the current action backtrace.
static DebuggerState & getGlobalDebuggerState()
void mlirDebuggerCursorSelectChildIRUnit(int index)
Select the child IR unit at the provided index, print an error if the index is out of bound.
void mlirDebuggerPrintContext()
Print the available context for the current Action.
void mlirDebuggerCursorSelectIRUnitFromContext(int index)
Select the IR unit from the current context by ID.
void mlirDebuggerAddRewritePatternBreakpoint(const char *patternNameInfo)
Add a breakpoint matching a pattern by name.
static void preventLinkerDeadCodeElim()
void mlirDebuggerDisableBreakpoint(BreakpointHandle breakpoint)
Disable the provided breakpoint.
void mlirDebuggerAddFileLineColLocBreakpoint(const char *file, int line, int col)
Add a breakpoint matching a file, line and column.
LLVM_ATTRIBUTE_NOINLINE void mlirDebuggerBreakpointHook()
void mlirDebuggerSetControl(int controlOption)
This is used by the debugger to control what to do after a breakpoint is hit.
static tracing::ExecutionContext::Control debuggerCallBackFunction(const tracing::ActionActiveStack *actionStack)
void mlirDebuggerCursorSelectPreviousIRUnit()
Return the next IR unit logically in the IR.
void mlirDebuggerCursorSelectNextIRUnit()
Return the previous IR unit logically in the IR.
BreakpointHandle mlirDebuggerAddTagBreakpoint(const char *tag)
Add a breakpoint matching exactly the provided tag.
void mlirDebuggerCursorSelectParentIRUnit()
Select the parent IR unit of the provided IR unit, or print an error if the IR unit has no parent.
void mlirDebuggerEnableBreakpoint(BreakpointHandle breakpoint)
Enable the provided breakpoint.
void mlirDebuggerCursorPrint(bool withRegion)
Print the current IR unit cursor.
void mlirDebuggerPrintActionBacktrace(bool withContext)
Print the current action backtrace.
void mlirDebuggerCursorSelectChildIRUnit(int index)
Select the child IR unit at the provided index, print an error if the index is out of bound.
struct MLIRBreakpoint * BreakpointHandle
void mlirDebuggerPrintContext()
Print the available context for the current Action.
void mlirDebuggerCursorSelectIRUnitFromContext(int index)
Select the IR unit from the current context by ID.
void mlirDebuggerAddRewritePatternBreakpoint(const char *patternNameInfo)
Add a breakpoint matching a pattern by name.
void mlirDebuggerDisableBreakpoint(BreakpointHandle breakpoint)
Disable the provided breakpoint.
void mlirDebuggerAddFileLineColLocBreakpoint(const char *file, int line, int col)
Add a breakpoint matching a file, line and column.
void mlirDebuggerSetControl(int controlOption)
This is used by the debugger to control what to do after a breakpoint is hit.
void mlirDebuggerCursorSelectPreviousIRUnit()
Return the next IR unit logically in the IR.
void mlirDebuggerCursorSelectNextIRUnit()
Return the previous IR unit logically in the IR.
BreakpointHandle mlirDebuggerAddTagBreakpoint(const char *tag)
Add a breakpoint matching exactly the provided tag.
void mlirDebuggerCursorSelectParentIRUnit()
Select the parent IR unit of the provided IR unit, or print an error if the IR unit has no parent.
static const LLVM_THREAD_LOCAL ActionActiveStack * actionStack
Block represents an ordered list of Operations.
Definition Block.h:33
IRUnit is a union of the different types of IR objects that constitute the IR structure (other than T...
Definition Unit.h:28
void print(raw_ostream &os, OpPrintingFlags flags=OpPrintingFlags().skipRegions().useLocalScope()) const
Print the IRUnit to the given stream.
Definition Unit.cpp:49
Set of flags used to control the behavior of the various IR print methods (e.g.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:686
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:674
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
virtual ArrayRef< IRUnit > getContextIRUnits() const
Return the set of IR units that are associated with this action.
Definition Action.h:55
This abstract class represents a breakpoint.
The ExecutionContext is the main orchestration of the infrastructure, it acts as a handler in the MLI...
void registerObserver(Observer *observer)
Register a new Observer on this context.
Control
Enum that allows the client of the context to control the execution of the action.
void addBreakpointManager(BreakpointManager *manager)
Register a new BreakpointManager on this context.
void setCallback(CallbackTy callback)
Set the callback that is used to control the execution.
FileLineColLocBreakpoint * addBreakpoint(StringRef file, int64_t line, int64_t col=-1)
TagBreakpoint * addBreakpoint(StringRef tag)
Add a breakpoint to the manager for the given tag and return it.
Include the generated interface declarations.
void setupDebuggerExecutionContextHook(tracing::ExecutionContext &executionContext)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
This class is used to keep track of the active actions in the stack.
const Action & getAction() const
const ActionActiveStack * getParent() const
void print(raw_ostream &os, bool withContext) const
This abstract class defines the interface used to observe an Action execution.