11020150eSMehdi Amini //===- DebuggerExecutionContextHook.cpp - Debugger Support ----------------===// 21020150eSMehdi Amini // 31020150eSMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41020150eSMehdi Amini // See https://llvm.org/LICENSE.txt for license information. 51020150eSMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61020150eSMehdi Amini // 71020150eSMehdi Amini //===----------------------------------------------------------------------===// 81020150eSMehdi Amini 91020150eSMehdi Amini #include "mlir/Debug/DebuggerExecutionContextHook.h" 101020150eSMehdi Amini 111020150eSMehdi Amini #include "mlir/Debug/BreakpointManagers/FileLineColLocBreakpointManager.h" 121020150eSMehdi Amini #include "mlir/Debug/BreakpointManagers/TagBreakpointManager.h" 131020150eSMehdi Amini 141020150eSMehdi Amini using namespace mlir; 151020150eSMehdi Amini using namespace mlir::tracing; 161020150eSMehdi Amini 171020150eSMehdi Amini namespace { 181020150eSMehdi Amini /// This structure tracks the state of the interactive debugger. 191020150eSMehdi Amini struct DebuggerState { 201020150eSMehdi Amini /// This variable keeps track of the current control option. This is set by 211020150eSMehdi Amini /// the debugger when control is handed over to it. 221020150eSMehdi Amini ExecutionContext::Control debuggerControl = ExecutionContext::Apply; 231020150eSMehdi Amini 241020150eSMehdi Amini /// The breakpoint manager that allows the debugger to set breakpoints on 251020150eSMehdi Amini /// action tags. 261020150eSMehdi Amini TagBreakpointManager tagBreakpointManager; 271020150eSMehdi Amini 281020150eSMehdi Amini /// The breakpoint manager that allows the debugger to set breakpoints on 291020150eSMehdi Amini /// FileLineColLoc locations. 301020150eSMehdi Amini FileLineColLocBreakpointManager fileLineColLocBreakpointManager; 311020150eSMehdi Amini 321020150eSMehdi Amini /// Map of breakpoint IDs to breakpoint objects. 331020150eSMehdi Amini DenseMap<unsigned, Breakpoint *> breakpointIdsMap; 341020150eSMehdi Amini 351020150eSMehdi Amini /// The current stack of actiive actions. 361020150eSMehdi Amini const tracing::ActionActiveStack *actionActiveStack; 371020150eSMehdi Amini 381020150eSMehdi Amini /// This is a "cursor" in the IR, it is used for the debugger to navigate the 391020150eSMehdi Amini /// IR associated to the actions. 401020150eSMehdi Amini IRUnit cursor; 411020150eSMehdi Amini }; 421020150eSMehdi Amini } // namespace 431020150eSMehdi Amini 441020150eSMehdi Amini static DebuggerState &getGlobalDebuggerState() { 451020150eSMehdi Amini static LLVM_THREAD_LOCAL DebuggerState debuggerState; 461020150eSMehdi Amini return debuggerState; 471020150eSMehdi Amini } 481020150eSMehdi Amini 491020150eSMehdi Amini extern "C" { 501020150eSMehdi Amini void mlirDebuggerSetControl(int controlOption) { 511020150eSMehdi Amini getGlobalDebuggerState().debuggerControl = 521020150eSMehdi Amini static_cast<ExecutionContext::Control>(controlOption); 531020150eSMehdi Amini } 541020150eSMehdi Amini 551020150eSMehdi Amini void mlirDebuggerPrintContext() { 561020150eSMehdi Amini DebuggerState &state = getGlobalDebuggerState(); 571020150eSMehdi Amini if (!state.actionActiveStack) { 581020150eSMehdi Amini llvm::outs() << "No active action.\n"; 591020150eSMehdi Amini return; 601020150eSMehdi Amini } 611020150eSMehdi Amini const ArrayRef<IRUnit> &units = 621020150eSMehdi Amini state.actionActiveStack->getAction().getContextIRUnits(); 631020150eSMehdi Amini llvm::outs() << units.size() << " available IRUnits:\n"; 641020150eSMehdi Amini for (const IRUnit &unit : units) { 651020150eSMehdi Amini llvm::outs() << " - "; 661020150eSMehdi Amini unit.print( 671020150eSMehdi Amini llvm::outs(), 681020150eSMehdi Amini OpPrintingFlags().useLocalScope().skipRegions().enableDebugInfo()); 691020150eSMehdi Amini llvm::outs() << "\n"; 701020150eSMehdi Amini } 711020150eSMehdi Amini } 721020150eSMehdi Amini 731020150eSMehdi Amini void mlirDebuggerPrintActionBacktrace(bool withContext) { 741020150eSMehdi Amini DebuggerState &state = getGlobalDebuggerState(); 751020150eSMehdi Amini if (!state.actionActiveStack) { 761020150eSMehdi Amini llvm::outs() << "No active action.\n"; 771020150eSMehdi Amini return; 781020150eSMehdi Amini } 791020150eSMehdi Amini state.actionActiveStack->print(llvm::outs(), withContext); 801020150eSMehdi Amini } 811020150eSMehdi Amini 821020150eSMehdi Amini //===----------------------------------------------------------------------===// 831020150eSMehdi Amini // Cursor Management 841020150eSMehdi Amini //===----------------------------------------------------------------------===// 851020150eSMehdi Amini 861020150eSMehdi Amini void mlirDebuggerCursorPrint(bool withRegion) { 871020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 881020150eSMehdi Amini if (!state.cursor) { 891020150eSMehdi Amini llvm::outs() << "No active MLIR cursor, select from the context first\n"; 901020150eSMehdi Amini return; 911020150eSMehdi Amini } 921020150eSMehdi Amini state.cursor.print(llvm::outs(), OpPrintingFlags() 931020150eSMehdi Amini .skipRegions(!withRegion) 941020150eSMehdi Amini .useLocalScope() 951020150eSMehdi Amini .enableDebugInfo()); 961020150eSMehdi Amini llvm::outs() << "\n"; 971020150eSMehdi Amini } 981020150eSMehdi Amini 991020150eSMehdi Amini void mlirDebuggerCursorSelectIRUnitFromContext(int index) { 1001020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 1011020150eSMehdi Amini if (!state.actionActiveStack) { 1021020150eSMehdi Amini llvm::outs() << "No active MLIR Action stack\n"; 1031020150eSMehdi Amini return; 1041020150eSMehdi Amini } 1051020150eSMehdi Amini ArrayRef<IRUnit> units = 1061020150eSMehdi Amini state.actionActiveStack->getAction().getContextIRUnits(); 1071020150eSMehdi Amini if (index < 0 || index >= static_cast<int>(units.size())) { 1081020150eSMehdi Amini llvm::outs() << "Index invalid, bounds: [0, " << units.size() 1091020150eSMehdi Amini << "] but got " << index << "\n"; 1101020150eSMehdi Amini return; 1111020150eSMehdi Amini } 1121020150eSMehdi Amini state.cursor = units[index]; 1131020150eSMehdi Amini state.cursor.print(llvm::outs()); 1141020150eSMehdi Amini llvm::outs() << "\n"; 1151020150eSMehdi Amini } 1161020150eSMehdi Amini 1171020150eSMehdi Amini void mlirDebuggerCursorSelectParentIRUnit() { 1181020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 1191020150eSMehdi Amini if (!state.cursor) { 1201020150eSMehdi Amini llvm::outs() << "No active MLIR cursor, select from the context first\n"; 1211020150eSMehdi Amini return; 1221020150eSMehdi Amini } 1231020150eSMehdi Amini IRUnit *unit = &state.cursor; 12468f58812STres Popp if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) { 1251020150eSMehdi Amini state.cursor = op->getBlock(); 12668f58812STres Popp } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) { 1271020150eSMehdi Amini state.cursor = region->getParentOp(); 12868f58812STres Popp } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) { 1291020150eSMehdi Amini state.cursor = block->getParent(); 1301020150eSMehdi Amini } else { 1311020150eSMehdi Amini llvm::outs() << "Current cursor is not a valid IRUnit"; 1321020150eSMehdi Amini return; 1331020150eSMehdi Amini } 1341020150eSMehdi Amini state.cursor.print(llvm::outs()); 1351020150eSMehdi Amini llvm::outs() << "\n"; 1361020150eSMehdi Amini } 1371020150eSMehdi Amini 1381020150eSMehdi Amini void mlirDebuggerCursorSelectChildIRUnit(int index) { 1391020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 1401020150eSMehdi Amini if (!state.cursor) { 1411020150eSMehdi Amini llvm::outs() << "No active MLIR cursor, select from the context first\n"; 1421020150eSMehdi Amini return; 1431020150eSMehdi Amini } 1441020150eSMehdi Amini IRUnit *unit = &state.cursor; 14568f58812STres Popp if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) { 1461020150eSMehdi Amini if (index < 0 || index >= static_cast<int>(op->getNumRegions())) { 1471020150eSMehdi Amini llvm::outs() << "Index invalid, op has " << op->getNumRegions() 1481020150eSMehdi Amini << " but got " << index << "\n"; 1491020150eSMehdi Amini return; 1501020150eSMehdi Amini } 1511020150eSMehdi Amini state.cursor = &op->getRegion(index); 15268f58812STres Popp } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) { 1531020150eSMehdi Amini auto block = region->begin(); 1541020150eSMehdi Amini int count = 0; 1551020150eSMehdi Amini while (block != region->end() && count != index) { 1561020150eSMehdi Amini ++block; 1571020150eSMehdi Amini ++count; 1581020150eSMehdi Amini } 1591020150eSMehdi Amini 1601020150eSMehdi Amini if (block == region->end()) { 1611020150eSMehdi Amini llvm::outs() << "Index invalid, region has " << count << " block but got " 1621020150eSMehdi Amini << index << "\n"; 1631020150eSMehdi Amini return; 1641020150eSMehdi Amini } 1651020150eSMehdi Amini state.cursor = &*block; 16668f58812STres Popp } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) { 1671020150eSMehdi Amini auto op = block->begin(); 1681020150eSMehdi Amini int count = 0; 1691020150eSMehdi Amini while (op != block->end() && count != index) { 1701020150eSMehdi Amini ++op; 1711020150eSMehdi Amini ++count; 1721020150eSMehdi Amini } 1731020150eSMehdi Amini 1741020150eSMehdi Amini if (op == block->end()) { 1751020150eSMehdi Amini llvm::outs() << "Index invalid, block has " << count 1761020150eSMehdi Amini << "operations but got " << index << "\n"; 1771020150eSMehdi Amini return; 1781020150eSMehdi Amini } 1791020150eSMehdi Amini state.cursor = &*op; 1801020150eSMehdi Amini } else { 1811020150eSMehdi Amini llvm::outs() << "Current cursor is not a valid IRUnit"; 1821020150eSMehdi Amini return; 1831020150eSMehdi Amini } 1841020150eSMehdi Amini state.cursor.print(llvm::outs()); 1851020150eSMehdi Amini llvm::outs() << "\n"; 1861020150eSMehdi Amini } 1871020150eSMehdi Amini 1881020150eSMehdi Amini void mlirDebuggerCursorSelectPreviousIRUnit() { 1891020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 1901020150eSMehdi Amini if (!state.cursor) { 1911020150eSMehdi Amini llvm::outs() << "No active MLIR cursor, select from the context first\n"; 1921020150eSMehdi Amini return; 1931020150eSMehdi Amini } 1941020150eSMehdi Amini IRUnit *unit = &state.cursor; 19568f58812STres Popp if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) { 1961020150eSMehdi Amini Operation *previous = op->getPrevNode(); 1971020150eSMehdi Amini if (!previous) { 1981020150eSMehdi Amini llvm::outs() << "No previous operation in the current block\n"; 1991020150eSMehdi Amini return; 2001020150eSMehdi Amini } 2011020150eSMehdi Amini state.cursor = previous; 20268f58812STres Popp } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) { 2031020150eSMehdi Amini llvm::outs() << "Has region\n"; 2041020150eSMehdi Amini Operation *parent = region->getParentOp(); 2051020150eSMehdi Amini if (!parent) { 2061020150eSMehdi Amini llvm::outs() << "No parent operation for the current region\n"; 2071020150eSMehdi Amini return; 2081020150eSMehdi Amini } 2091020150eSMehdi Amini if (region->getRegionNumber() == 0) { 2101020150eSMehdi Amini llvm::outs() << "No previous region in the current operation\n"; 2111020150eSMehdi Amini return; 2121020150eSMehdi Amini } 2131020150eSMehdi Amini state.cursor = 2141020150eSMehdi Amini ®ion->getParentOp()->getRegion(region->getRegionNumber() - 1); 21568f58812STres Popp } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) { 2161020150eSMehdi Amini Block *previous = block->getPrevNode(); 2171020150eSMehdi Amini if (!previous) { 2181020150eSMehdi Amini llvm::outs() << "No previous block in the current region\n"; 2191020150eSMehdi Amini return; 2201020150eSMehdi Amini } 2211020150eSMehdi Amini state.cursor = previous; 2221020150eSMehdi Amini } else { 2231020150eSMehdi Amini llvm::outs() << "Current cursor is not a valid IRUnit"; 2241020150eSMehdi Amini return; 2251020150eSMehdi Amini } 2261020150eSMehdi Amini state.cursor.print(llvm::outs()); 2271020150eSMehdi Amini llvm::outs() << "\n"; 2281020150eSMehdi Amini } 2291020150eSMehdi Amini 2301020150eSMehdi Amini void mlirDebuggerCursorSelectNextIRUnit() { 2311020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 2321020150eSMehdi Amini if (!state.cursor) { 2331020150eSMehdi Amini llvm::outs() << "No active MLIR cursor, select from the context first\n"; 2341020150eSMehdi Amini return; 2351020150eSMehdi Amini } 2361020150eSMehdi Amini IRUnit *unit = &state.cursor; 23768f58812STres Popp if (auto *op = llvm::dyn_cast_if_present<Operation *>(*unit)) { 2381020150eSMehdi Amini Operation *next = op->getNextNode(); 2391020150eSMehdi Amini if (!next) { 2401020150eSMehdi Amini llvm::outs() << "No next operation in the current block\n"; 2411020150eSMehdi Amini return; 2421020150eSMehdi Amini } 2431020150eSMehdi Amini state.cursor = next; 24468f58812STres Popp } else if (auto *region = llvm::dyn_cast_if_present<Region *>(*unit)) { 2451020150eSMehdi Amini Operation *parent = region->getParentOp(); 2461020150eSMehdi Amini if (!parent) { 2471020150eSMehdi Amini llvm::outs() << "No parent operation for the current region\n"; 2481020150eSMehdi Amini return; 2491020150eSMehdi Amini } 2501020150eSMehdi Amini if (region->getRegionNumber() == parent->getNumRegions() - 1) { 2511020150eSMehdi Amini llvm::outs() << "No next region in the current operation\n"; 2521020150eSMehdi Amini return; 2531020150eSMehdi Amini } 2541020150eSMehdi Amini state.cursor = 2551020150eSMehdi Amini ®ion->getParentOp()->getRegion(region->getRegionNumber() + 1); 25668f58812STres Popp } else if (auto *block = llvm::dyn_cast_if_present<Block *>(*unit)) { 2571020150eSMehdi Amini Block *next = block->getNextNode(); 2581020150eSMehdi Amini if (!next) { 2591020150eSMehdi Amini llvm::outs() << "No next block in the current region\n"; 2601020150eSMehdi Amini return; 2611020150eSMehdi Amini } 2621020150eSMehdi Amini state.cursor = next; 2631020150eSMehdi Amini } else { 2641020150eSMehdi Amini llvm::outs() << "Current cursor is not a valid IRUnit"; 2651020150eSMehdi Amini return; 2661020150eSMehdi Amini } 2671020150eSMehdi Amini state.cursor.print(llvm::outs()); 2681020150eSMehdi Amini llvm::outs() << "\n"; 2691020150eSMehdi Amini } 2701020150eSMehdi Amini 2711020150eSMehdi Amini //===----------------------------------------------------------------------===// 2721020150eSMehdi Amini // Breakpoint Management 2731020150eSMehdi Amini //===----------------------------------------------------------------------===// 2741020150eSMehdi Amini 2751020150eSMehdi Amini void mlirDebuggerEnableBreakpoint(BreakpointHandle breakpoint) { 2761020150eSMehdi Amini reinterpret_cast<Breakpoint *>(breakpoint)->enable(); 2771020150eSMehdi Amini } 2781020150eSMehdi Amini 2791020150eSMehdi Amini void mlirDebuggerDisableBreakpoint(BreakpointHandle breakpoint) { 2801020150eSMehdi Amini reinterpret_cast<Breakpoint *>(breakpoint)->disable(); 2811020150eSMehdi Amini } 2821020150eSMehdi Amini 2831020150eSMehdi Amini BreakpointHandle mlirDebuggerAddTagBreakpoint(const char *tag) { 2841020150eSMehdi Amini DebuggerState &state = getGlobalDebuggerState(); 2851020150eSMehdi Amini Breakpoint *breakpoint = 2861020150eSMehdi Amini state.tagBreakpointManager.addBreakpoint(StringRef(tag, strlen(tag))); 2871020150eSMehdi Amini int breakpointId = state.breakpointIdsMap.size() + 1; 2881020150eSMehdi Amini state.breakpointIdsMap[breakpointId] = breakpoint; 2891020150eSMehdi Amini return reinterpret_cast<BreakpointHandle>(breakpoint); 2901020150eSMehdi Amini } 2911020150eSMehdi Amini 2921020150eSMehdi Amini void mlirDebuggerAddRewritePatternBreakpoint(const char *patternNameInfo) {} 2931020150eSMehdi Amini 2941020150eSMehdi Amini void mlirDebuggerAddFileLineColLocBreakpoint(const char *file, int line, 2951020150eSMehdi Amini int col) { 2961020150eSMehdi Amini getGlobalDebuggerState().fileLineColLocBreakpointManager.addBreakpoint( 2971020150eSMehdi Amini StringRef(file, strlen(file)), line, col); 2981020150eSMehdi Amini } 2991020150eSMehdi Amini 3001020150eSMehdi Amini } // extern "C" 3011020150eSMehdi Amini 3021020150eSMehdi Amini LLVM_ATTRIBUTE_NOINLINE void mlirDebuggerBreakpointHook() { 3031020150eSMehdi Amini static LLVM_THREAD_LOCAL void *volatile sink; 304*d5746d73SFrank Schlimbach sink = static_cast<void *>(const_cast<void **>(&sink)); 3051020150eSMehdi Amini } 3061020150eSMehdi Amini 3071020150eSMehdi Amini static void preventLinkerDeadCodeElim() { 3081020150eSMehdi Amini static void *volatile sink; 3091020150eSMehdi Amini static bool initialized = [&]() { 3101020150eSMehdi Amini sink = (void *)mlirDebuggerSetControl; 3111020150eSMehdi Amini sink = (void *)mlirDebuggerEnableBreakpoint; 3121020150eSMehdi Amini sink = (void *)mlirDebuggerDisableBreakpoint; 3131020150eSMehdi Amini sink = (void *)mlirDebuggerPrintContext; 3141020150eSMehdi Amini sink = (void *)mlirDebuggerPrintActionBacktrace; 3151020150eSMehdi Amini sink = (void *)mlirDebuggerCursorPrint; 3161020150eSMehdi Amini sink = (void *)mlirDebuggerCursorSelectIRUnitFromContext; 3171020150eSMehdi Amini sink = (void *)mlirDebuggerCursorSelectParentIRUnit; 3181020150eSMehdi Amini sink = (void *)mlirDebuggerCursorSelectChildIRUnit; 3191020150eSMehdi Amini sink = (void *)mlirDebuggerCursorSelectPreviousIRUnit; 3201020150eSMehdi Amini sink = (void *)mlirDebuggerCursorSelectNextIRUnit; 3211020150eSMehdi Amini sink = (void *)mlirDebuggerAddTagBreakpoint; 3221020150eSMehdi Amini sink = (void *)mlirDebuggerAddRewritePatternBreakpoint; 3231020150eSMehdi Amini sink = (void *)mlirDebuggerAddFileLineColLocBreakpoint; 324*d5746d73SFrank Schlimbach sink = static_cast<void *>(const_cast<void **>(&sink)); 3251020150eSMehdi Amini return true; 3261020150eSMehdi Amini }(); 3271020150eSMehdi Amini (void)initialized; 3281020150eSMehdi Amini } 3291020150eSMehdi Amini 3301020150eSMehdi Amini static tracing::ExecutionContext::Control 3311020150eSMehdi Amini debuggerCallBackFunction(const tracing::ActionActiveStack *actionStack) { 3321020150eSMehdi Amini preventLinkerDeadCodeElim(); 3331020150eSMehdi Amini // Invoke the breakpoint hook, the debugger is supposed to trap this. 3341020150eSMehdi Amini // The debugger controls the execution from there by invoking 3351020150eSMehdi Amini // `mlirDebuggerSetControl()`. 3361020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 3371020150eSMehdi Amini state.actionActiveStack = actionStack; 3381020150eSMehdi Amini getGlobalDebuggerState().debuggerControl = ExecutionContext::Apply; 3391020150eSMehdi Amini actionStack->getAction().print(llvm::outs()); 3401020150eSMehdi Amini llvm::outs() << "\n"; 3411020150eSMehdi Amini mlirDebuggerBreakpointHook(); 3421020150eSMehdi Amini return getGlobalDebuggerState().debuggerControl; 3431020150eSMehdi Amini } 3441020150eSMehdi Amini 3451020150eSMehdi Amini namespace { 3461020150eSMehdi Amini /// Manage the stack of actions that are currently active. 3471020150eSMehdi Amini class DebuggerObserver : public ExecutionContext::Observer { 3481020150eSMehdi Amini void beforeExecute(const ActionActiveStack *action, Breakpoint *breakpoint, 3491020150eSMehdi Amini bool willExecute) override { 3501020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 3511020150eSMehdi Amini state.actionActiveStack = action; 3521020150eSMehdi Amini } 3531020150eSMehdi Amini void afterExecute(const ActionActiveStack *action) override { 3541020150eSMehdi Amini auto &state = getGlobalDebuggerState(); 3551020150eSMehdi Amini state.actionActiveStack = action->getParent(); 3561020150eSMehdi Amini state.cursor = nullptr; 3571020150eSMehdi Amini } 3581020150eSMehdi Amini }; 3591020150eSMehdi Amini } // namespace 3601020150eSMehdi Amini 3611020150eSMehdi Amini void mlir::setupDebuggerExecutionContextHook( 3621020150eSMehdi Amini tracing::ExecutionContext &executionContext) { 3631020150eSMehdi Amini executionContext.setCallback(debuggerCallBackFunction); 3641020150eSMehdi Amini DebuggerState &state = getGlobalDebuggerState(); 3651020150eSMehdi Amini static DebuggerObserver observer; 3661020150eSMehdi Amini executionContext.registerObserver(&observer); 3671020150eSMehdi Amini executionContext.addBreakpointManager(&state.fileLineColLocBreakpointManager); 3681020150eSMehdi Amini executionContext.addBreakpointManager(&state.tagBreakpointManager); 3691020150eSMehdi Amini } 370