On 14/05/2015 18:43, Peter Maydell wrote: > 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 <peter.mayd...@linaro.org> > --- > 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) >
Hi Peter, what happened to this patch? Paolo