llvm-project
568 строк · 19.6 Кб
1#!/usr/bin/env python
2
3# ---------------------------------------------------------------------
4# Be sure to add the python path that points to the LLDB shared library.
5#
6# # To use this in the embedded python interpreter using "lldb" just
7# import it with the full path using the "command script import"
8# command
9# (lldb) command script import /path/to/cmdtemplate.py
10# ---------------------------------------------------------------------
11
12import inspect13import lldb14import argparse15import shlex16import sys17
18# Each new breakpoint gets a unique ID starting from 1.
19nextid = 120# List of breakpoint set from python, the key is the ID and the value the
21# actual breakpoint. These are NOT LLDB SBBreakpoint objects.
22breakpoints = dict()23
24exprOptions = lldb.SBExpressionOptions()25exprOptions.SetIgnoreBreakpoints()26exprOptions.SetLanguage(lldb.eLanguageTypeC)27
28
29class MlirDebug:30"""MLIR debugger commands31This is the class that hooks into LLDB and registers the `mlir` command.
32Other providers can register subcommands below this one.
33"""
34
35lldb_command = "mlir"36parser = None37
38def __init__(self, debugger, unused):39super().__init__()40self.create_options()41self.help_string = MlirDebug.parser.format_help()42
43@classmethod44def create_options(cls):45if MlirDebug.parser:46return MlirDebug.parser47usage = "usage: %s [options]" % (cls.lldb_command)48description = "TODO."49
50# Pass add_help_option = False, since this keeps the command in line51# with lldb commands, and we wire up "help command" to work by52# providing the long & short help methods below.53MlirDebug.parser = argparse.ArgumentParser(54prog=cls.lldb_command, usage=usage, description=description, add_help=False55)56MlirDebug.subparsers = MlirDebug.parser.add_subparsers(dest="command")57return MlirDebug.parser58
59def get_short_help(self):60return "MLIR debugger commands"61
62def get_long_help(self):63return self.help_string64
65def __call__(self, debugger, command, exe_ctx, result):66# Use the Shell Lexer to properly parse up command options just like a67# shell would68command_args = shlex.split(command)69
70try:71args = MlirDebug.parser.parse_args(command_args)72except:73result.SetError("option parsing failed")74raise75args.func(args, debugger, command, exe_ctx, result)76
77@classmethod78def on_process_start(frame, bp_loc, dict):79print("Process started")80
81
82class SetControl:83# Define the subcommands that controls what to do when a breakpoint is hit.84# The key is the subcommand name, the value is a tuple of the command ID to85# pass to MLIR and the help string.86commands = {87"apply": (1, "Apply the current action and continue the execution"),88"skip": (2, "Skip the current action and continue the execution"),89"step": (3, "Step into the current action"),90"next": (4, "Step over the current action"),91"finish": (5, "Step out of the current action"),92}93
94@classmethod95def register_mlir_subparser(cls):96for cmd, (cmdInt, help) in cls.commands.items():97parser = MlirDebug.subparsers.add_parser(98cmd,99help=help,100)101parser.set_defaults(func=cls.process_options)102
103@classmethod104def process_options(cls, options, debugger, command, exe_ctx, result):105frame = exe_ctx.GetFrame()106if not frame.IsValid():107result.SetError("No valid frame (program not running?)")108return109cmdInt = cls.commands.get(options.command, None)110if not cmdInt:111result.SetError("Invalid command: %s" % (options.command))112return113
114result = frame.EvaluateExpression(115"((bool (*)(int))mlirDebuggerSetControl)(%d)" % (cmdInt[0]),116exprOptions,117)118if not result.error.Success():119print("Error setting up command: %s" % (result.error))120return121debugger.SetAsync(True)122result = exe_ctx.GetProcess().Continue()123debugger.SetAsync(False)124
125
126class PrintContext:127@classmethod128def register_mlir_subparser(cls):129cls.parser = MlirDebug.subparsers.add_parser(130"context", help="Print the current context"131)132cls.parser.set_defaults(func=cls.process_options)133
134@classmethod135def process_options(cls, options, debugger, command, exe_ctx, result):136frame = exe_ctx.GetFrame()137if not frame.IsValid():138result.SetError("Can't print context without a valid frame")139return140result = frame.EvaluateExpression(141"((bool (*)())&mlirDebuggerPrintContext)()", exprOptions142)143if not result.error.Success():144print("Error printing context: %s" % (result.error))145return146
147
148class Backtrace:149@classmethod150def register_mlir_subparser(cls):151cls.parser = MlirDebug.subparsers.add_parser(152"backtrace", aliases=["bt"], help="Print the current backtrace"153)154cls.parser.set_defaults(func=cls.process_options)155cls.parser.add_argument("--context", default=False, action="store_true")156
157@classmethod158def process_options(cls, options, debugger, command, exe_ctx, result):159frame = exe_ctx.GetFrame()160if not frame.IsValid():161result.SetError(162"Can't backtrace without a valid frame (program not running?)"163)164result = frame.EvaluateExpression(165"((bool(*)(bool))mlirDebuggerPrintActionBacktrace)(%d)" % (options.context),166exprOptions,167)168if not result.error.Success():169print("Error printing breakpoints: %s" % (result.error))170return171
172
173###############################################################################
174# Cursor manipulation
175###############################################################################
176
177
178class PrintCursor:179@classmethod180def register_mlir_subparser(cls):181cls.parser = MlirDebug.subparsers.add_parser(182"cursor-print", aliases=["cursor-p"], help="Print the current cursor"183)184cls.parser.add_argument(185"--print-region", "--regions", "-r", default=False, action="store_true"186)187cls.parser.set_defaults(func=cls.process_options)188
189@classmethod190def process_options(cls, options, debugger, command, exe_ctx, result):191frame = exe_ctx.GetFrame()192if not frame.IsValid():193result.SetError(194"Can't print cursor without a valid frame (program not running?)"195)196result = frame.EvaluateExpression(197"((bool(*)(bool))mlirDebuggerCursorPrint)(%d)" % (options.print_region),198exprOptions,199)200if not result.error.Success():201print("Error printing cursor: %s" % (result.error))202return203
204
205class SelectCursorFromContext:206@classmethod207def register_mlir_subparser(cls):208cls.parser = MlirDebug.subparsers.add_parser(209"cursor-select-from-context",210aliases=["cursor-s"],211help="Select the cursor from the current context",212)213cls.parser.add_argument("index", type=int, help="Index in the context")214cls.parser.set_defaults(func=cls.process_options)215
216@classmethod217def process_options(cls, options, debugger, command, exe_ctx, result):218frame = exe_ctx.GetFrame()219if not frame.IsValid():220result.SetError(221"Can't manipulate cursor without a valid frame (program not running?)"222)223result = frame.EvaluateExpression(224"((bool(*)(int))mlirDebuggerCursorSelectIRUnitFromContext)(%d)"225% options.index,226exprOptions,227)228if not result.error.Success():229print("Error manipulating cursor: %s" % (result.error))230return231
232
233class CursorSelectParent:234@classmethod235def register_mlir_subparser(cls):236cls.parser = MlirDebug.subparsers.add_parser(237"cursor-parent", aliases=["cursor-up"], help="Select the cursor parent"238)239cls.parser.set_defaults(func=cls.process_options)240
241@classmethod242def process_options(cls, options, debugger, command, exe_ctx, result):243frame = exe_ctx.GetFrame()244if not frame.IsValid():245result.SetError(246"Can't manipulate cursor without a valid frame (program not running?)"247)248result = frame.EvaluateExpression(249"((bool(*)())mlirDebuggerCursorSelectParentIRUnit)()",250exprOptions,251)252if not result.error.Success():253print("Error manipulating cursor: %s" % (result.error))254return255
256
257class SelectCursorChild:258@classmethod259def register_mlir_subparser(cls):260cls.parser = MlirDebug.subparsers.add_parser(261"cursor-child", aliases=["cursor-c"], help="Select the nth child"262)263cls.parser.add_argument("index", type=int, help="Index of the child to select")264cls.parser.set_defaults(func=cls.process_options)265
266@classmethod267def process_options(cls, options, debugger, command, exe_ctx, result):268frame = exe_ctx.GetFrame()269if not frame.IsValid():270result.SetError(271"Can't manipulate cursor without a valid frame (program not running?)"272)273result = frame.EvaluateExpression(274"((bool(*)(int))mlirDebuggerCursorSelectChildIRUnit)(%d)" % options.index,275exprOptions,276)277if not result.error.Success():278print("Error manipulating cursor: %s" % (result.error))279return280
281
282class CursorSelecPrevious:283@classmethod284def register_mlir_subparser(cls):285cls.parser = MlirDebug.subparsers.add_parser(286"cursor-previous",287aliases=["cursor-prev"],288help="Select the cursor previous element",289)290cls.parser.set_defaults(func=cls.process_options)291
292@classmethod293def process_options(cls, options, debugger, command, exe_ctx, result):294frame = exe_ctx.GetFrame()295if not frame.IsValid():296result.SetError(297"Can't manipulate cursor without a valid frame (program not running?)"298)299result = frame.EvaluateExpression(300"((bool(*)())mlirDebuggerCursorSelectPreviousIRUnit)()",301exprOptions,302)303if not result.error.Success():304print("Error manipulating cursor: %s" % (result.error))305return306
307
308class CursorSelecNext:309@classmethod310def register_mlir_subparser(cls):311cls.parser = MlirDebug.subparsers.add_parser(312"cursor-next", aliases=["cursor-n"], help="Select the cursor next element"313)314cls.parser.set_defaults(func=cls.process_options)315
316@classmethod317def process_options(cls, options, debugger, command, exe_ctx, result):318frame = exe_ctx.GetFrame()319if not frame.IsValid():320result.SetError(321"Can't manipulate cursor without a valid frame (program not running?)"322)323result = frame.EvaluateExpression(324"((bool(*)())mlirDebuggerCursorSelectNextIRUnit)()",325exprOptions,326)327if not result.error.Success():328print("Error manipulating cursor: %s" % (result.error))329return330
331
332###############################################################################
333# Breakpoints
334###############################################################################
335
336
337class EnableBreakpoint:338@classmethod339def register_mlir_subparser(cls):340cls.parser = MlirDebug.subparsers.add_parser(341"enable", help="Enable a single breakpoint (given its ID)"342)343cls.parser.add_argument("id", help="ID of the breakpoint to enable")344cls.parser.set_defaults(func=cls.process_options)345
346@classmethod347def process_options(cls, options, debugger, command, exe_ctx, result):348bp = breakpoints.get(int(options.id), None)349if not bp:350result.SetError("No breakpoint with ID %d" % int(options.id))351return352bp.enable(exe_ctx.GetFrame())353
354
355class DisableBreakpoint:356@classmethod357def register_mlir_subparser(cls):358cls.parser = MlirDebug.subparsers.add_parser(359"disable", help="Disable a single breakpoint (given its ID)"360)361cls.parser.add_argument("id", help="ID of the breakpoint to disable")362cls.parser.set_defaults(func=cls.process_options)363
364@classmethod365def process_options(cls, options, debugger, command, exe_ctx, result):366bp = breakpoints.get(int(options.id), None)367if not bp:368result.SetError("No breakpoint with ID %s" % options.id)369return370bp.disable(exe_ctx.GetFrame())371
372
373class ListBreakpoints:374@classmethod375def register_mlir_subparser(cls):376cls.parser = MlirDebug.subparsers.add_parser(377"list", help="List all current breakpoints"378)379cls.parser.set_defaults(func=cls.process_options)380
381@classmethod382def process_options(cls, options, debugger, command, exe_ctx, result):383for id, bp in sorted(breakpoints.items()):384print(id, type(id), str(bp), "enabled" if bp.isEnabled else "disabled")385
386
387class Breakpoint:388def __init__(self):389global nextid390self.id = nextid391nextid += 1392breakpoints[self.id] = self393self.isEnabled = True394
395def enable(self, frame=None):396self.isEnabled = True397if not frame or not frame.IsValid():398return399# use a C cast to force the type of the breakpoint handle to be void * so400# that we don't rely on DWARF. Also add a fake bool return value otherwise401# LLDB can't signal any error with the expression evaluation (at least I don't know how).402cmd = (403"((bool (*)(void *))mlirDebuggerEnableBreakpoint)((void *)%s)" % self.handle404)405result = frame.EvaluateExpression(cmd, exprOptions)406if not result.error.Success():407print("Error enabling breakpoint: %s" % (result.error))408return409
410def disable(self, frame=None):411self.isEnabled = False412if not frame or not frame.IsValid():413return414# use a C cast to force the type of the breakpoint handle to be void * so415# that we don't rely on DWARF. Also add a fake bool return value otherwise416# LLDB can't signal any error with the expression evaluation (at least I don't know how).417cmd = (418"((bool (*)(void *)) mlirDebuggerDisableBreakpoint)((void *)%s)"419% self.handle420)421result = frame.EvaluateExpression(cmd, exprOptions)422if not result.error.Success():423print("Error disabling breakpoint: %s" % (result.error))424return425
426
427class TagBreakpoint(Breakpoint):428mlir_subcommand = "break-on-tag"429
430def __init__(self, tag):431super().__init__()432self.tag = tag433
434def __str__(self):435return "[%d] TagBreakpoint(%s)" % (self.id, self.tag)436
437@classmethod438def register_mlir_subparser(cls):439cls.parser = MlirDebug.subparsers.add_parser(440cls.mlir_subcommand, help="add a breakpoint on actions' tag matching"441)442cls.parser.set_defaults(func=cls.process_options)443cls.parser.add_argument("tag", help="tag to match")444
445@classmethod446def process_options(cls, options, debugger, command, exe_ctx, result):447breakpoint = TagBreakpoint(options.tag)448print("Added breakpoint %s" % str(breakpoint))449
450frame = exe_ctx.GetFrame()451if frame.IsValid():452breakpoint.install(frame)453
454def install(self, frame):455result = frame.EvaluateExpression(456'((void *(*)(const char *))mlirDebuggerAddTagBreakpoint)("%s")'457% (self.tag),458exprOptions,459)460if not result.error.Success():461print("Error installing breakpoint: %s" % (result.error))462return463# Save the handle, this is necessary to implement enable/disable.464self.handle = result.GetValue()465
466
467class FileLineBreakpoint(Breakpoint):468mlir_subcommand = "break-on-file"469
470def __init__(self, file, line, col):471super().__init__()472self.file = file473self.line = line474self.col = col475
476def __str__(self):477return "[%d] FileLineBreakpoint(%s, %d, %d)" % (478self.id,479self.file,480self.line,481self.col,482)483
484@classmethod485def register_mlir_subparser(cls):486cls.parser = MlirDebug.subparsers.add_parser(487cls.mlir_subcommand,488help="add a breakpoint that filters on location of the IR affected by an action. The syntax is file:line:col where file and col are optional",489)490cls.parser.set_defaults(func=cls.process_options)491cls.parser.add_argument("location", type=str)492
493@classmethod494def process_options(cls, options, debugger, command, exe_ctx, result):495split_loc = options.location.split(":")496file = split_loc[0]497line = int(split_loc[1]) if len(split_loc) > 1 else -1498col = int(split_loc[2]) if len(split_loc) > 2 else -1499breakpoint = FileLineBreakpoint(file, line, col)500print("Added breakpoint %s" % str(breakpoint))501
502frame = exe_ctx.GetFrame()503if frame.IsValid():504breakpoint.install(frame)505
506def install(self, frame):507result = frame.EvaluateExpression(508'((void *(*)(const char *, int, int))mlirDebuggerAddFileLineColLocBreakpoint)("%s", %d, %d)'509% (self.file, self.line, self.col),510exprOptions,511)512if not result.error.Success():513print("Error installing breakpoint: %s" % (result.error))514return515# Save the handle, this is necessary to implement enable/disable.516self.handle = result.GetValue()517
518
519def on_start(frame, bpno, err):520print("MLIR debugger attaching...")521for _, bp in sorted(breakpoints.items()):522if bp.isEnabled:523print("Installing breakpoint %s" % (str(bp)))524bp.install(frame)525else:526print("Skipping disabled breakpoint %s" % (str(bp)))527
528return True529
530
531def __lldb_init_module(debugger, dict):532target = debugger.GetTargetAtIndex(0)533debugger.SetAsync(False)534if not target:535print("No target is loaded, please load a target before loading this script.")536return537if debugger.GetNumTargets() > 1:538print(539"Multiple targets (%s) loaded, attaching MLIR debugging to %s"540% (debugger.GetNumTargets(), target)541)542
543# Register all classes that have a register_lldb_command method544module_name = __name__545parser = MlirDebug.create_options()546MlirDebug.__doc__ = parser.format_help()547
548# Add the MLIR entry point to LLDB as a command.549command = "command script add -o -c %s.%s %s" % (550module_name,551MlirDebug.__name__,552MlirDebug.lldb_command,553)554debugger.HandleCommand(command)555
556main_bp = target.BreakpointCreateByName("main")557main_bp.SetScriptCallbackFunction("action_debugging.on_start")558main_bp.SetAutoContinue(auto_continue=True)559
560on_breackpoint = target.BreakpointCreateByName("mlirDebuggerBreakpointHook")561
562print(563'The "{0}" command has been installed for target `{1}`, type "help {0}" or "{0} '564'--help" for detailed help.'.format(MlirDebug.lldb_command, target)565)566for _name, cls in inspect.getmembers(sys.modules[module_name]):567if inspect.isclass(cls) and getattr(cls, "register_mlir_subparser", None):568cls.register_mlir_subparser()569