qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 3/3] scripts/qemu-gdb: Add support for printing trac


From: Peter Maydell
Subject: [Qemu-devel] [PATCH 3/3] scripts/qemu-gdb: Add support for printing trace events
Date: Thu, 14 May 2015 17:43:55 +0100

Add two new commands to our gdb support:
 qemu trace-enable eventname
 qemu trace-disable eventname

which allow dynamically enabling and disabling printing of QEMU
trace events during a debugging session. These work with the
"null" trace backend, by putting breakpoints on the stub
trace_eventname() functions.

Signed-off-by: Peter Maydell <address@hidden>
---
 scripts/qemu-gdb.py      |   4 +-
 scripts/qemugdb/trace.py | 188 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 scripts/qemugdb/trace.py

diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py
index 1c94b2a..6d27c06 100644
--- a/scripts/qemu-gdb.py
+++ b/scripts/qemu-gdb.py
@@ -23,7 +23,7 @@ import os, sys
 
 sys.path.append(os.path.dirname(__file__))
 
-from qemugdb import mtree, coroutine
+from qemugdb import mtree, coroutine, trace
 
 class QemuCommand(gdb.Command):
     '''Prefix for QEMU debug support commands'''
@@ -34,3 +34,5 @@ class QemuCommand(gdb.Command):
 QemuCommand()
 coroutine.CoroutineCommand()
 mtree.MtreeCommand()
+trace.TraceEnableCommand()
+trace.TraceDisableCommand()
diff --git a/scripts/qemugdb/trace.py b/scripts/qemugdb/trace.py
new file mode 100644
index 0000000..24543e1
--- /dev/null
+++ b/scripts/qemugdb/trace.py
@@ -0,0 +1,188 @@
+#!/usr/bin/python
+
+# GDB debugging support: selecting printing of trace events
+#
+# Copyright (c) 2015 Linaro Ltd
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>
+
+# qemu trace-enable trace-event-name
+# qemu trace-disable trace-event-name
+#  * enable/disable printing of tracing for QEMU trace events (works
+#    even if QEMU was built with the null trace backend; may not work
+#    with non-debug QEMU builds)
+
+import gdb
+import re, os
+
+# Assume the trace-events file is at ../../ relative to where we are
+
+trace_events_filename = os.path.join(os.path.dirname(__file__),
+                                     os.pardir,os.pardir,
+                                     "trace-events")
+
+def gdb_bp_list():
+    '''Like gdb.breakpoints(), but return empty list if no bps, not None'''
+    # The point is that this is always iterable
+    bplist = gdb.breakpoints()
+    if not bplist:
+        return []
+    return bplist
+
+class TraceEventInfo:
+    def __init__(self, name, formatstring, arglist):
+        self.name = name
+        self.formatstring = formatstring
+        self.arglist = arglist
+        self.argstr = ", ".join(arglist)
+        if self.argstr != "":
+            self.argstr = ", " + self.argstr
+
+# Hash of trace events read in from the 'trace-events' file;
+# values are TraceEventInfo objects
+trace_events = {}
+
+def extract_identifier(s):
+    '''Extract the identifier from a C argument declaration'''
+    # That is, given "const char *filename" return "filename".
+    r = re.sub(r'.*?([a-zA-Z_][a-zA-Z_0-9]*)\s*$', r'\1', s)
+    if r == 'void':
+        return None
+    return r
+
+# Preprocessor symbols which we know about.
+# These should work for both 32 bit and 64 bit Linux, at least.
+# If we needed to, we could determine whether the target was
+# 32 or 64 bit with
+#     is_64bit = gdb.lookup_type('void').pointer().sizeof == 8
+# but we can get away without it.
+fmtstr_dict = {
+    "PRIu8":"u",
+    "PRIx32":"x",
+    "PRIu32":"u",
+    "PRId32":"d",
+    "PRIx64":"llx",
+    "PRIu64":"llu",
+    "PRId64":"lld",
+    "PRIxPTR":"llx",
+}
+
+def fixup_fmtstr(s):
+    # fmtstr needs to have leading space and " removed,
+    # trailing " removed, and handling of interpolated PRIxfoo
+    # dealt with (including trailing PRIxfoo)
+    inquotes = False
+    inescape = False
+    new = ""
+    sym = ""
+    for c in s:
+        if inquotes:
+            if inescape:
+                new = new + c
+                inescape = False
+            elif c == '\\':
+                inescape = True
+                new = new + c
+            elif c == '"':
+                inquotes = False
+                sym = ""
+            else:
+                new = new + c
+        else:
+            if c == '"':
+                # end of unquoted section
+                sym = sym.strip()
+                if sym != "":
+                    new = new + fmtstr_dict[sym]
+                inquotes = True
+            else:
+                sym = sym + c
+
+    # gdb printf doesn't understand the 'z' length modifier,
+    # so convert to 'l'
+    return re.sub(r'(?<!%)%z', r'%l', new)
+    return new
+
+def read_trace_events_file(filename):
+    '''Populate the trace_events dictionary from the specified file'''
+    global trace_events
+    trace_events = {}
+    f = open(filename)
+    for line in iter(f):
+        try:
+            line = line.strip()
+            if line == "" or line.startswith('#'):
+                continue
+
+            # Very ugly ad-hoc parsing
+            (name, rest) = line.split('(', 1)
+            (rest, fmtstr) = rest.split(')', 1)
+
+            fmtstr = fixup_fmtstr(fmtstr)
+
+            arglist = rest.split(',')
+            arglist = [extract_identifier(x) for x in arglist]
+            arglist = [x for x in arglist if x is not None]
+            trace_events[name] = TraceEventInfo(name, fmtstr, arglist)
+        except:
+            gdb.write('Warning: ignoring line: %s\n' % line)
+            raise
+
+class QemuTraceBreakpoint(gdb.Breakpoint):
+    def __init__(self, eventname):
+        self.event = trace_events[eventname]
+        spec = "trace_" + eventname
+        # might want to make these internal bps later
+        gdb.Breakpoint.__init__(self, spec, gdb.BP_BREAKPOINT, False)
+
+    def stop(self):
+        gdb.write('%s: ' % self.event.name)
+        gdb.execute('printf "%s\\n"%s'
+                    % (self.event.formatstring, self.event.argstr))
+        # Tell gdb not to actually stop here
+        return False
+
+class TraceEnableCommand(gdb.Command):
+    '''Enable in-gdb tracing of the specified QEMU trace event'''
+    def __init__(self):
+        gdb.Command.__init__(self, 'qemu trace-enable', gdb.COMMAND_DATA,
+                             gdb.COMPLETE_NONE)
+
+    def invoke(self, arg, from_tty):
+        # place breakpoint on function trace_<eventname>
+        # set up breakpoint to print info and continue
+        # add bp to hash table with key being the event name
+        if arg not in trace_events:
+            gdb.write('Unknown trace event %s\n')
+            return
+        gdb.write("Enabled trace event %s\n" % arg)
+        QemuTraceBreakpoint(arg)
+
+class TraceDisableCommand(gdb.Command):
+    '''Disable in-gdb tracing of the specified QEMU trace event'''
+    def __init__(self):
+        gdb.Command.__init__(self, 'qemu trace-disable', gdb.COMMAND_DATA,
+                             gdb.COMPLETE_NONE)
+
+    def invoke(self, arg, from_tty):
+        # delete the bp set on trace_<eventname> by the enable command
+        for bp in gdb_bp_list():
+            if isinstance(bp,  QemuTraceBreakpoint) and bp.event.name == arg:
+                bp.delete()
+                gdb.write("Disabled trace event %s\n" % arg)
+                return
+        gdb.write("Can't disable trace event %s: unknown or not enabled\n" % 
arg)
+
+read_trace_events_file(trace_events_filename)
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]