r316773 - New lldb python module for managing diagnostic breakpoints

Don Hinton via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 27 10:02:33 PDT 2017


Author: dhinton
Date: Fri Oct 27 10:02:33 2017
New Revision: 316773

URL: http://llvm.org/viewvc/llvm-project?rev=316773&view=rev
Log:
New lldb python module for managing diagnostic breakpoints

Summary:
Can be used to set breakpoints for either the diagnostics actually
emitted for the current compilation, a particular DiagID, or all
DiagIDs for a particular warning.

Differential Revision: https://reviews.llvm.org/D36347

Added:
    cfe/trunk/utils/clangdiag.py   (with props)

Added: cfe/trunk/utils/clangdiag.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/clangdiag.py?rev=316773&view=auto
==============================================================================
--- cfe/trunk/utils/clangdiag.py (added)
+++ cfe/trunk/utils/clangdiag.py Fri Oct 27 10:02:33 2017
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# Be sure to add the python path that points to the LLDB shared library.
+#
+# # To use this in the embedded python interpreter using "lldb" just
+# import it with the full path using the "command script import"
+# command
+#   (lldb) command script import /path/to/clandiag.py
+#----------------------------------------------------------------------
+
+import lldb
+import argparse
+import commands
+import shlex
+import os
+import re
+import subprocess
+
+class MyParser(argparse.ArgumentParser):
+    def format_help(self):
+        return '''     Commands for managing clang diagnostic breakpoints
+
+Syntax: clangdiag enable [<warning>|<diag-name>]
+        clangdiag disable
+        clangdiag diagtool [<path>|reset]
+
+The following subcommands are supported:
+
+      enable   -- Enable clang diagnostic breakpoints.
+      disable  -- Disable all clang diagnostic breakpoints.
+      diagtool -- Return, set, or reset diagtool path.
+
+This command sets breakpoints in clang, and clang based tools, that
+emit diagnostics.  When a diagnostic is emitted, and clangdiag is
+enabled, it will use the appropriate diagtool application to determine
+the name of the DiagID, and set breakpoints in all locations that
+'diag::name' appears in the source.  Since the new breakpoints are set
+after they are encountered, users will need to launch the executable a
+second time in order to hit the new breakpoints.
+
+For in-tree builds, the diagtool application, used to map DiagID's to
+names, is found automatically in the same directory as the target
+executable.  However, out-or-tree builds must use the 'diagtool'
+subcommand to set the appropriate path for diagtool in the clang debug
+bin directory.  Since this mapping is created at build-time, it's
+important for users to use the same version that was generated when
+clang was compiled, or else the id's won't match.
+
+Notes:
+- Substrings can be passed for both <warning> and <diag-name>.
+- If <warning> is passed, only enable the DiagID(s) for that warning.
+- If <diag-name> is passed, only enable that DiagID.
+- Rerunning enable clears existing breakpoints.
+- diagtool is used in breakpoint callbacks, so it can be changed
+  without the need to rerun enable.
+- Adding this to your ~.lldbinit file makes clangdiag available at startup:
+  "command script import /path/to/clangdiag.py"
+
+'''
+
+def create_diag_options():
+    parser = MyParser(prog='clangdiag')
+    subparsers = parser.add_subparsers(
+        title='subcommands',
+        dest='subcommands',
+        metavar='')
+    disable_parser = subparsers.add_parser('disable')
+    enable_parser = subparsers.add_parser('enable')
+    enable_parser.add_argument('id', nargs='?')
+    diagtool_parser = subparsers.add_parser('diagtool')
+    diagtool_parser.add_argument('path', nargs='?')
+    return parser
+
+def getDiagtool(target, diagtool = None):
+    id = target.GetProcess().GetProcessID()
+    if 'diagtool' not in getDiagtool.__dict__:
+        getDiagtool.diagtool = {}
+    if diagtool:
+        if diagtool == 'reset':
+            getDiagtool.diagtool[id] = None
+        elif os.path.exists(diagtool):
+            getDiagtool.diagtool[id] = diagtool
+        else:
+            print('clangdiag: %s not found.' % diagtool)
+    if not id in getDiagtool.diagtool or not getDiagtool.diagtool[id]:
+        getDiagtool.diagtool[id] = None
+        exe = target.GetExecutable()
+        if not exe.Exists():
+            print('clangdiag: Target (%s) not set.' % exe.GetFilename())
+        else:
+            diagtool = os.path.join(exe.GetDirectory(), 'diagtool')
+            if os.path.exists(diagtool):
+                getDiagtool.diagtool[id] = diagtool
+            else:
+                print('clangdiag: diagtool not found along side %s' % exe)
+
+    return getDiagtool.diagtool[id]
+
+def setDiagBreakpoint(frame, bp_loc, dict):
+    id = frame.FindVariable("DiagID").GetValue()
+    if id is None:
+        print('clangdiag: id is None')
+        return False
+
+    # Don't need to test this time, since we did that in enable.
+    target = frame.GetThread().GetProcess().GetTarget()
+    diagtool = getDiagtool(target)
+    name = subprocess.check_output([diagtool, "find-diagnostic-id", id]).rstrip();
+    # Make sure we only consider errors, warnings, and extentions.
+    # FIXME: Make this configurable?
+    prefixes = ['err_', 'warn_', 'exp_']
+    if len([prefix for prefix in prefixes+[''] if name.startswith(prefix)][0]):
+        bp = target.BreakpointCreateBySourceRegex(name, lldb.SBFileSpec())
+        bp.AddName("clang::Diagnostic")
+
+    return False
+
+def enable(exe_ctx, args):
+    # Always disable existing breakpoints
+    disable(exe_ctx)
+
+    target = exe_ctx.GetTarget()
+    numOfBreakpoints = target.GetNumBreakpoints()
+
+    if args.id:
+        # Make sure we only consider errors, warnings, and extentions.
+        # FIXME: Make this configurable?
+        prefixes = ['err_', 'warn_', 'exp_']
+        if len([prefix for prefix in prefixes+[''] if args.id.startswith(prefix)][0]):
+            bp = target.BreakpointCreateBySourceRegex(args.id, lldb.SBFileSpec())
+            bp.AddName("clang::Diagnostic")
+        else:
+            diagtool = getDiagtool(target)
+            list = subprocess.check_output([diagtool, "list-warnings"]).rstrip();
+            for line in list.splitlines(True):
+                m = re.search(r' *(.*) .*\[\-W' + re.escape(args.id) + r'.*].*', line)
+                # Make sure we only consider warnings.
+                if m and m.group(1).startswith('warn_'):
+                    bp = target.BreakpointCreateBySourceRegex(m.group(1), lldb.SBFileSpec())
+                    bp.AddName("clang::Diagnostic")
+    else:
+        print('Adding callbacks.')
+        bp = target.BreakpointCreateByName('DiagnosticsEngine::Report')
+        bp.SetScriptCallbackFunction('clangdiag.setDiagBreakpoint')
+        bp.AddName("clang::Diagnostic")
+
+    count = target.GetNumBreakpoints() - numOfBreakpoints
+    print('%i breakpoint%s added.' % (count, "s"[count==1:]))
+
+    return
+
+def disable(exe_ctx):
+    target = exe_ctx.GetTarget()
+    # Remove all diag breakpoints.
+    bkpts = lldb.SBBreakpointList(target)
+    target.FindBreakpointsByName("clang::Diagnostic", bkpts)
+    for i in range(bkpts.GetSize()):
+        target.BreakpointDelete(bkpts.GetBreakpointAtIndex(i).GetID())
+
+    return
+
+def the_diag_command(debugger, command, exe_ctx, result, dict):
+    # Use the Shell Lexer to properly parse up command options just like a
+    # shell would
+    command_args = shlex.split(command)
+    parser = create_diag_options()
+    try:
+        args = parser.parse_args(command_args)
+    except:
+        return
+
+    if args.subcommands == 'enable':
+        enable(exe_ctx, args)
+    elif args.subcommands == 'disable':
+        disable(exe_ctx)
+    else:
+        diagtool = getDiagtool(exe_ctx.GetTarget(), args.path)
+        print('diagtool = %s' % diagtool)
+
+    return
+
+def __lldb_init_module(debugger, dict):
+    # This initializer is being run from LLDB in the embedded command interpreter
+    # Make the options so we can generate the help text for the new LLDB
+    # command line command prior to registering it with LLDB below
+    parser = create_diag_options()
+    the_diag_command.__doc__ = parser.format_help()
+    # Add any commands contained in this module to LLDB
+    debugger.HandleCommand(
+        'command script add -f clangdiag.the_diag_command clangdiag')
+    print 'The "clangdiag" command has been installed, type "help clangdiag" or "clangdiag --help" for detailed help.'

Propchange: cfe/trunk/utils/clangdiag.py
------------------------------------------------------------------------------
    svn:executable = *




More information about the cfe-commits mailing list