1515bc8c1Sserge-sans-paille#!/usr/bin/env python 2fc2ffbe1SDon Hinton 3fc2ffbe1SDon Hinton# ---------------------------------------------------------------------- 4fc2ffbe1SDon Hinton# Be sure to add the python path that points to the LLDB shared library. 5fc2ffbe1SDon Hinton# 6fc2ffbe1SDon Hinton# # To use this in the embedded python interpreter using "lldb" just 7fc2ffbe1SDon Hinton# import it with the full path using the "command script import" 8fc2ffbe1SDon Hinton# command 9fc2ffbe1SDon Hinton# (lldb) command script import /path/to/clandiag.py 10fc2ffbe1SDon Hinton# ---------------------------------------------------------------------- 11fc2ffbe1SDon Hinton 12b748c0e6SSerge Gueltonfrom __future__ import absolute_import, division, print_function 13fc2ffbe1SDon Hintonimport lldb 14fc2ffbe1SDon Hintonimport argparse 15fc2ffbe1SDon Hintonimport shlex 16fc2ffbe1SDon Hintonimport os 17fc2ffbe1SDon Hintonimport re 18fc2ffbe1SDon Hintonimport subprocess 19fc2ffbe1SDon Hinton 20*dd3c26a0STobias Hieta 21fc2ffbe1SDon Hintonclass MyParser(argparse.ArgumentParser): 22fc2ffbe1SDon Hinton def format_help(self): 23*dd3c26a0STobias Hieta return """ Commands for managing clang diagnostic breakpoints 24fc2ffbe1SDon Hinton 25fc2ffbe1SDon HintonSyntax: clangdiag enable [<warning>|<diag-name>] 26fc2ffbe1SDon Hinton clangdiag disable 27fc2ffbe1SDon Hinton clangdiag diagtool [<path>|reset] 28fc2ffbe1SDon Hinton 29fc2ffbe1SDon HintonThe following subcommands are supported: 30fc2ffbe1SDon Hinton 31fc2ffbe1SDon Hinton enable -- Enable clang diagnostic breakpoints. 32fc2ffbe1SDon Hinton disable -- Disable all clang diagnostic breakpoints. 33fc2ffbe1SDon Hinton diagtool -- Return, set, or reset diagtool path. 34fc2ffbe1SDon Hinton 35fc2ffbe1SDon HintonThis command sets breakpoints in clang, and clang based tools, that 36fc2ffbe1SDon Hintonemit diagnostics. When a diagnostic is emitted, and clangdiag is 37fc2ffbe1SDon Hintonenabled, it will use the appropriate diagtool application to determine 38fc2ffbe1SDon Hintonthe name of the DiagID, and set breakpoints in all locations that 39fc2ffbe1SDon Hinton'diag::name' appears in the source. Since the new breakpoints are set 40fc2ffbe1SDon Hintonafter they are encountered, users will need to launch the executable a 41fc2ffbe1SDon Hintonsecond time in order to hit the new breakpoints. 42fc2ffbe1SDon Hinton 43fc2ffbe1SDon HintonFor in-tree builds, the diagtool application, used to map DiagID's to 44fc2ffbe1SDon Hintonnames, is found automatically in the same directory as the target 45fc2ffbe1SDon Hintonexecutable. However, out-or-tree builds must use the 'diagtool' 46fc2ffbe1SDon Hintonsubcommand to set the appropriate path for diagtool in the clang debug 47fc2ffbe1SDon Hintonbin directory. Since this mapping is created at build-time, it's 48fc2ffbe1SDon Hintonimportant for users to use the same version that was generated when 49fc2ffbe1SDon Hintonclang was compiled, or else the id's won't match. 50fc2ffbe1SDon Hinton 51fc2ffbe1SDon HintonNotes: 52fc2ffbe1SDon Hinton- Substrings can be passed for both <warning> and <diag-name>. 53fc2ffbe1SDon Hinton- If <warning> is passed, only enable the DiagID(s) for that warning. 54fc2ffbe1SDon Hinton- If <diag-name> is passed, only enable that DiagID. 55fc2ffbe1SDon Hinton- Rerunning enable clears existing breakpoints. 56fc2ffbe1SDon Hinton- diagtool is used in breakpoint callbacks, so it can be changed 57fc2ffbe1SDon Hinton without the need to rerun enable. 58fc2ffbe1SDon Hinton- Adding this to your ~.lldbinit file makes clangdiag available at startup: 59fc2ffbe1SDon Hinton "command script import /path/to/clangdiag.py" 60fc2ffbe1SDon Hinton 61*dd3c26a0STobias Hieta""" 62*dd3c26a0STobias Hieta 63fc2ffbe1SDon Hinton 64fc2ffbe1SDon Hintondef create_diag_options(): 65*dd3c26a0STobias Hieta parser = MyParser(prog="clangdiag") 66fc2ffbe1SDon Hinton subparsers = parser.add_subparsers( 67*dd3c26a0STobias Hieta title="subcommands", dest="subcommands", metavar="" 68*dd3c26a0STobias Hieta ) 69*dd3c26a0STobias Hieta disable_parser = subparsers.add_parser("disable") 70*dd3c26a0STobias Hieta enable_parser = subparsers.add_parser("enable") 71*dd3c26a0STobias Hieta enable_parser.add_argument("id", nargs="?") 72*dd3c26a0STobias Hieta diagtool_parser = subparsers.add_parser("diagtool") 73*dd3c26a0STobias Hieta diagtool_parser.add_argument("path", nargs="?") 74fc2ffbe1SDon Hinton return parser 75fc2ffbe1SDon Hinton 76*dd3c26a0STobias Hieta 77fc2ffbe1SDon Hintondef getDiagtool(target, diagtool=None): 78fc2ffbe1SDon Hinton id = target.GetProcess().GetProcessID() 79*dd3c26a0STobias Hieta if "diagtool" not in getDiagtool.__dict__: 80fc2ffbe1SDon Hinton getDiagtool.diagtool = {} 81fc2ffbe1SDon Hinton if diagtool: 82*dd3c26a0STobias Hieta if diagtool == "reset": 83fc2ffbe1SDon Hinton getDiagtool.diagtool[id] = None 84fc2ffbe1SDon Hinton elif os.path.exists(diagtool): 85fc2ffbe1SDon Hinton getDiagtool.diagtool[id] = diagtool 86fc2ffbe1SDon Hinton else: 87*dd3c26a0STobias Hieta print("clangdiag: %s not found." % diagtool) 88fc2ffbe1SDon Hinton if not id in getDiagtool.diagtool or not getDiagtool.diagtool[id]: 89fc2ffbe1SDon Hinton getDiagtool.diagtool[id] = None 90fc2ffbe1SDon Hinton exe = target.GetExecutable() 91fc2ffbe1SDon Hinton if not exe.Exists(): 92*dd3c26a0STobias Hieta print("clangdiag: Target (%s) not set." % exe.GetFilename()) 93fc2ffbe1SDon Hinton else: 94*dd3c26a0STobias Hieta diagtool = os.path.join(exe.GetDirectory(), "diagtool") 95fc2ffbe1SDon Hinton if os.path.exists(diagtool): 96fc2ffbe1SDon Hinton getDiagtool.diagtool[id] = diagtool 97fc2ffbe1SDon Hinton else: 98*dd3c26a0STobias Hieta print("clangdiag: diagtool not found along side %s" % exe) 99fc2ffbe1SDon Hinton 100fc2ffbe1SDon Hinton return getDiagtool.diagtool[id] 101fc2ffbe1SDon Hinton 102*dd3c26a0STobias Hieta 103fc2ffbe1SDon Hintondef setDiagBreakpoint(frame, bp_loc, dict): 104fc2ffbe1SDon Hinton id = frame.FindVariable("DiagID").GetValue() 105fc2ffbe1SDon Hinton if id is None: 106*dd3c26a0STobias Hieta print("clangdiag: id is None") 107fc2ffbe1SDon Hinton return False 108fc2ffbe1SDon Hinton 109fc2ffbe1SDon Hinton # Don't need to test this time, since we did that in enable. 110fc2ffbe1SDon Hinton target = frame.GetThread().GetProcess().GetTarget() 111fc2ffbe1SDon Hinton diagtool = getDiagtool(target) 112*dd3c26a0STobias Hieta name = subprocess.check_output([diagtool, "find-diagnostic-id", id]).rstrip() 1132a8c18d9SAlexander Kornienko # Make sure we only consider errors, warnings, and extensions. 114fc2ffbe1SDon Hinton # FIXME: Make this configurable? 115*dd3c26a0STobias Hieta prefixes = ["err_", "warn_", "exp_"] 116*dd3c26a0STobias Hieta if len([prefix for prefix in prefixes + [""] if name.startswith(prefix)][0]): 117fc2ffbe1SDon Hinton bp = target.BreakpointCreateBySourceRegex(name, lldb.SBFileSpec()) 118fc2ffbe1SDon Hinton bp.AddName("clang::Diagnostic") 119fc2ffbe1SDon Hinton 120fc2ffbe1SDon Hinton return False 121fc2ffbe1SDon Hinton 122*dd3c26a0STobias Hieta 123fc2ffbe1SDon Hintondef enable(exe_ctx, args): 124fc2ffbe1SDon Hinton # Always disable existing breakpoints 125fc2ffbe1SDon Hinton disable(exe_ctx) 126fc2ffbe1SDon Hinton 127fc2ffbe1SDon Hinton target = exe_ctx.GetTarget() 128fc2ffbe1SDon Hinton numOfBreakpoints = target.GetNumBreakpoints() 129fc2ffbe1SDon Hinton 130fc2ffbe1SDon Hinton if args.id: 1312a8c18d9SAlexander Kornienko # Make sure we only consider errors, warnings, and extensions. 132fc2ffbe1SDon Hinton # FIXME: Make this configurable? 133*dd3c26a0STobias Hieta prefixes = ["err_", "warn_", "exp_"] 134*dd3c26a0STobias Hieta if len([prefix for prefix in prefixes + [""] if args.id.startswith(prefix)][0]): 135fc2ffbe1SDon Hinton bp = target.BreakpointCreateBySourceRegex(args.id, lldb.SBFileSpec()) 136fc2ffbe1SDon Hinton bp.AddName("clang::Diagnostic") 137fc2ffbe1SDon Hinton else: 138fc2ffbe1SDon Hinton diagtool = getDiagtool(target) 139*dd3c26a0STobias Hieta list = subprocess.check_output([diagtool, "list-warnings"]).rstrip() 140fc2ffbe1SDon Hinton for line in list.splitlines(True): 141*dd3c26a0STobias Hieta m = re.search(r" *(.*) .*\[\-W" + re.escape(args.id) + r".*].*", line) 142fc2ffbe1SDon Hinton # Make sure we only consider warnings. 143*dd3c26a0STobias Hieta if m and m.group(1).startswith("warn_"): 144*dd3c26a0STobias Hieta bp = target.BreakpointCreateBySourceRegex( 145*dd3c26a0STobias Hieta m.group(1), lldb.SBFileSpec() 146*dd3c26a0STobias Hieta ) 147fc2ffbe1SDon Hinton bp.AddName("clang::Diagnostic") 148fc2ffbe1SDon Hinton else: 149*dd3c26a0STobias Hieta print("Adding callbacks.") 150*dd3c26a0STobias Hieta bp = target.BreakpointCreateByName("DiagnosticsEngine::Report") 151*dd3c26a0STobias Hieta bp.SetScriptCallbackFunction("clangdiag.setDiagBreakpoint") 152fc2ffbe1SDon Hinton bp.AddName("clang::Diagnostic") 153fc2ffbe1SDon Hinton 154fc2ffbe1SDon Hinton count = target.GetNumBreakpoints() - numOfBreakpoints 155*dd3c26a0STobias Hieta print("%i breakpoint%s added." % (count, "s"[count == 1 :])) 156fc2ffbe1SDon Hinton 157fc2ffbe1SDon Hinton return 158fc2ffbe1SDon Hinton 159*dd3c26a0STobias Hieta 160fc2ffbe1SDon Hintondef disable(exe_ctx): 161fc2ffbe1SDon Hinton target = exe_ctx.GetTarget() 162fc2ffbe1SDon Hinton # Remove all diag breakpoints. 163fc2ffbe1SDon Hinton bkpts = lldb.SBBreakpointList(target) 164fc2ffbe1SDon Hinton target.FindBreakpointsByName("clang::Diagnostic", bkpts) 165fc2ffbe1SDon Hinton for i in range(bkpts.GetSize()): 166fc2ffbe1SDon Hinton target.BreakpointDelete(bkpts.GetBreakpointAtIndex(i).GetID()) 167fc2ffbe1SDon Hinton 168fc2ffbe1SDon Hinton return 169fc2ffbe1SDon Hinton 170*dd3c26a0STobias Hieta 171fc2ffbe1SDon Hintondef the_diag_command(debugger, command, exe_ctx, result, dict): 172fc2ffbe1SDon Hinton # Use the Shell Lexer to properly parse up command options just like a 173fc2ffbe1SDon Hinton # shell would 174fc2ffbe1SDon Hinton command_args = shlex.split(command) 175fc2ffbe1SDon Hinton parser = create_diag_options() 176fc2ffbe1SDon Hinton try: 177fc2ffbe1SDon Hinton args = parser.parse_args(command_args) 178fc2ffbe1SDon Hinton except: 179fc2ffbe1SDon Hinton return 180fc2ffbe1SDon Hinton 181*dd3c26a0STobias Hieta if args.subcommands == "enable": 182fc2ffbe1SDon Hinton enable(exe_ctx, args) 183*dd3c26a0STobias Hieta elif args.subcommands == "disable": 184fc2ffbe1SDon Hinton disable(exe_ctx) 185fc2ffbe1SDon Hinton else: 186fc2ffbe1SDon Hinton diagtool = getDiagtool(exe_ctx.GetTarget(), args.path) 187*dd3c26a0STobias Hieta print("diagtool = %s" % diagtool) 188fc2ffbe1SDon Hinton 189fc2ffbe1SDon Hinton return 190fc2ffbe1SDon Hinton 191*dd3c26a0STobias Hieta 192fc2ffbe1SDon Hintondef __lldb_init_module(debugger, dict): 193fc2ffbe1SDon Hinton # This initializer is being run from LLDB in the embedded command interpreter 194fc2ffbe1SDon Hinton # Make the options so we can generate the help text for the new LLDB 195fc2ffbe1SDon Hinton # command line command prior to registering it with LLDB below 196fc2ffbe1SDon Hinton parser = create_diag_options() 197fc2ffbe1SDon Hinton the_diag_command.__doc__ = parser.format_help() 198fc2ffbe1SDon Hinton # Add any commands contained in this module to LLDB 199*dd3c26a0STobias Hieta debugger.HandleCommand("command script add -f clangdiag.the_diag_command clangdiag") 200*dd3c26a0STobias Hieta print( 201*dd3c26a0STobias Hieta 'The "clangdiag" command has been installed, type "help clangdiag" or "clangdiag --help" for detailed help.' 202*dd3c26a0STobias Hieta ) 203