Message ID | 1431621835-7565-4-git-send-email-peter.maydell@linaro.org |
---|---|
State | Rejected |
Headers | show |
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.maydell@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
On 22 September 2015 at 07:52, Paolo Bonzini <pbonzini@redhat.com> wrote: > > > 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. >> > what happened to this patch? Stefan suggested that it was basically a manual reimplementation of gdb's support for static probepoints via systemtap/dtrace, so I put it to one side til I had time to investigate that gdb functionality. I committed the patches which were refactoring the script into modules, though. thanks -- PMM
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)
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.maydell@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