From patchwork Fri Feb 10 11:49:39 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Llu=C3=ADs_Vilanova?= X-Patchwork-Id: 140630 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 44C3AB6EF3 for ; Sat, 11 Feb 2012 00:32:40 +1100 (EST) Received: from localhost ([::1]:48291 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rvozg-0006EC-Px for incoming@patchwork.ozlabs.org; Fri, 10 Feb 2012 06:50:24 -0500 Received: from eggs.gnu.org ([140.186.70.92]:52169) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RvozE-0005op-4O for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:50:03 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Rvoz6-0001hq-KX for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:49:55 -0500 Received: from gw.ac.upc.edu ([147.83.30.3]:55470) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rvoz6-0001hf-3p for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:49:48 -0500 Received: from localhost (unknown [10.8.0.22]) by gw.ac.upc.edu (Postfix) with ESMTP id 116612D000D; Fri, 10 Feb 2012 12:49:47 +0100 (CET) To: qemu-devel@nongnu.org From: =?utf-8?b?TGx1w61z?= Vilanova Date: Fri, 10 Feb 2012 12:49:39 +0100 Message-ID: <20120210114939.5104.76788.stgit@ginnungagap.bsc.es> In-Reply-To: <20120210114849.5104.63589.stgit@ginnungagap.bsc.es> References: <20120210114849.5104.63589.stgit@ginnungagap.bsc.es> User-Agent: StGit/0.15 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 147.83.30.3 Cc: stefanha@gmail.com, harsh@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v3 09/11] trace: [tracetool] Automatically establish available backends and formats X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: LluĂ­s Vilanova --- Makefile.objs | 6 - Makefile.target | 3 scripts/tracetool.py | 357 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 230 insertions(+), 136 deletions(-) diff --git a/Makefile.objs b/Makefile.objs index 2b68739..941386b 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -354,12 +354,12 @@ else trace.h: trace.h-timestamp endif trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --backend=$(TRACE_BACKEND) -h < $< > $@," GEN trace.h") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=h --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.h") @cmp -s $@ trace.h || cp $@ trace.h trace.c: trace.c-timestamp trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --backend=$(TRACE_BACKEND) -c < $< > $@," GEN trace.c") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=c --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.c") @cmp -s $@ trace.c || cp $@ trace.c trace.o: trace.c $(GENERATED_HEADERS) @@ -372,7 +372,7 @@ trace-dtrace.h: trace-dtrace.dtrace # rule file. So we use '.dtrace' instead trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --backend=$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=d --backend=$(TRACE_BACKEND) < $< > $@," GEN trace-dtrace.dtrace") @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) diff --git a/Makefile.target b/Makefile.target index 2b24ea1..2bdf955 100644 --- a/Makefile.target +++ b/Makefile.target @@ -53,11 +53,12 @@ endif $(QEMU_PROG).stp: $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py \ + --format=stap \ --backend=$(TRACE_BACKEND) \ --binary=$(bindir)/$(QEMU_PROG) \ --target-arch=$(TARGET_ARCH) \ --target-type=$(TARGET_TYPE) \ - --stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") + < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") else stap: endif diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 91e7620..8ce39df 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -11,33 +11,109 @@ import sys import getopt -def usage(): - print "Tracetool: Generate tracing code for trace events file on stdin" - print "Usage:" - print sys.argv[0], " --backend=[nop|simple|stderr|dtrace|ust] [-h|-c|-d|--stap]" - print ''' -Backends: - --nop Tracing disabled - --simple Simple built-in backend - --stderr Stderr built-in backend - --dtrace DTrace/SystemTAP backend - --ust LTTng User Space Tracing backend - -Output formats: - -h Generate .h file - -c Generate .c file - -d Generate .d file (DTrace only) - --stap Generate .stp file (DTrace with SystemTAP only) -Options: - --binary [path] Full path to QEMU binary - --target-arch [arch] QEMU emulator target arch - --target-type [type] QEMU emulator target type ('system' or 'user') - --probe-prefix [prefix] Prefix for dtrace probe names - (default: qemu-targettype-targetarch) -''' - sys.exit(1) +###################################################################### +# format auto-registration + +class _Tag: + pass + +_formats = {} + +BEGIN = _Tag() +END = _Tag() +_DESCR = _Tag() + +def for_format(format_, when, descr = None): + """Decorator for format generator functions.""" + + if when is not BEGIN and when is not END: + raise ValueError("Invalid 'when' tag") + if format_ in _formats and when in _formats[format_]: + raise ValueError("Format '%s' already set for given 'when' tag" % format_) + + if format_ not in _formats: + _formats[format_] = {} + if descr is not None: + if _DESCR in _formats[format_]: + raise ValueError("Description already set") + _formats[format_][_DESCR] = descr + + def func(f): + _formats[format_][when] = f + return f + return func + +def get_format(format_, when): + """Get a format generator function.""" + + def nop(*args, **kwargs): + pass + if format_ in _formats and when in _formats[format_]: + return _formats[format_][when] + else: + return nop + +def get_format_descr(format_): + """Get the description of a format generator.""" + + if format_ in _formats and _DESCR in _formats[format_]: + return _formats[format_][_DESCR] + else: + return "" + + + +###################################################################### +# backend auto-registration and format compatibility + +_backends = {} + +def for_backend(backend, format_, descr = None): + if backend not in _backends: + _backends[backend] = {} + if format_ in _backends[backend]: + raise ValueError("Backend '%s' already set for backend '%s'" % (backend, format_)) + if format_ not in _formats: + raise ValueError("Unknown format '%s'" % format_) + + if descr is not None: + if _DESCR in _backends[backend]: + raise ValueError("Description already set") + _backends[backend][_DESCR] = descr + + def func(f): + _backends[backend][format_] = f + return f + return func + +def get_backend(format_, backend): + if backend not in _backends: + raise ValueError("Unknown backend '%s'" % backend) + if format_ not in _formats: + raise ValueError("Unknown format '%s'" % format_) + if format_ not in _backends[backend]: + raise ValueError("Format '%s' not supported with backend '%s'" % (format_, backend)) + return _backends[backend][format_] + +def get_backend_descr(backend): + """Get the description of a backend.""" + + if backend in _backends and _DESCR in _backends[backend]: + return _backends[backend][_DESCR] + else: + return "" + + + +###################################################################### +# formats + +################################################## +# format: h + +@for_format("h", BEGIN, "Generate .h file") def trace_h_begin(events): print '''#ifndef TRACE_H #define TRACE_H @@ -46,12 +122,27 @@ def trace_h_begin(events): #include "qemu-common.h"''' +@for_format("h", END) def trace_h_end(events): print '#endif /* TRACE_H */' + +################################################## +# format: c + +@for_format("c", BEGIN, "Generate .c file") def trace_c_begin(events): print '/* This file is autogenerated by tracetool, do not edit. */' + + +###################################################################### +# backends + +################################################## +# backend: nop + +@for_backend("nop", "h", "Tracing disabled") def nop_h(events): print for event in events: @@ -62,13 +153,16 @@ def nop_h(events): 'name': event.name, 'args': event.args } - return +@for_backend("nop", "c") def nop_c(events): - # nop, reqd for converters - return + pass + +################################################## +# backend: simple +@for_backend("simple", "h", "Simple built-in backend") def simple_h(events): print '#include "trace/simple.h"' print @@ -81,8 +175,7 @@ def simple_h(events): print '#define NR_TRACE_EVENTS %d' % len(events) print 'extern TraceEvent trace_list[NR_TRACE_EVENTS];' - return - +@for_backend("simple", "c") def simple_c(events): rec_off = 0 print '#include "trace.h"' @@ -164,8 +257,11 @@ def simple_c(events): ''' - return +################################################## +# backend: stderr + +@for_backend("stderr", "h", "Stderr built-in backend") def stderr_h(events): print '''#include #include "trace/stderr.h" @@ -192,6 +288,7 @@ static inline void trace_%(name)s(%(args)s) print print '#define NR_TRACE_EVENTS %d' % len(events) +@for_backend("stderr", "c") def stderr_c(events): print '''#include "trace.h" @@ -204,6 +301,11 @@ TraceEvent trace_list[] = { print print '};' + +################################################## +# backend: ust + +@for_backend("ust", "h", "LTTng User Space Tracing backend") def ust_h(events): print '''#include #undef mutex_lock @@ -227,8 +329,8 @@ _DECLARE_TRACEPOINT_NOARGS(ust_%(name)s); 'name': event.name, } print - return +@for_backend("ust", "c") def ust_c(events): print '''#include #undef mutex_lock @@ -274,8 +376,11 @@ static void __attribute__((constructor)) trace_init(void) } print '}' - return +################################################## +# backend: dtrace + +@for_backend("dtrace", "h", "DTrace/SystemTAP backend") def dtrace_h(events): print '#include "trace-dtrace.h"' print @@ -292,9 +397,16 @@ def dtrace_h(events): 'argnames': ", ".join(event.args.names()), } +@for_backend("dtrace", "c") def dtrace_c(events): - return # No need for function definitions in dtrace backend + pass + + +@for_format("d", BEGIN, "Generate .d file (DTrace probes)") +def trace_d_begin(events): + print '/* This file is autogenerated by tracetool, do not edit. */' +@for_backend("dtrace", "d") def dtrace_d(events): print 'provider qemu {' for event in events: @@ -315,9 +427,27 @@ def dtrace_d(events): print '};' return +@for_backend("nop", "d") def dtrace_nop_d(events): pass + +@for_format("stap", BEGIN, "Generate .stp file (SystemTAP tapsets)") +def trace_stap_begin(events): + global probeprefix + if binary == "": + print '--binary is required for SystemTAP tapset generator' + sys.exit(1) + if ((probeprefix == "") and (targettype == "")): + print '--target-type is required for SystemTAP tapset generator' + sys.exit(1) + if ((probeprefix == "") and (targetarch == "")): + print '--target-arch is required for SystemTAP tapset generator' + sys.exit(1) + if probeprefix == "": + probeprefix = 'qemu.' + targettype + '.' + targetarch + print '/* This file is autogenerated by tracetool, do not edit. */' + def dtrace_stp(events): for event in events: # Define prototype for probe arguments @@ -343,82 +473,11 @@ probe %(probeprefix)s.%(name)s = process("%(binary)s").mark("%(name)s") def dtrace_nop_stp(events): pass -def trace_stap_begin(events): - global probeprefix - if backend != "dtrace": - print 'SystemTAP tapset generator not applicable to %s backend' % backend - sys.exit(1) - if binary == "": - print '--binary is required for SystemTAP tapset generator' - sys.exit(1) - if ((probeprefix == "") and (targettype == "")): - print '--target-type is required for SystemTAP tapset generator' - sys.exit(1) - if ((probeprefix == "") and (targetarch == "")): - print '--target-arch is required for SystemTAP tapset generator' - sys.exit(1) - if probeprefix == "": - probeprefix = 'qemu.' + targettype + '.' + targetarch - print '/* This file is autogenerated by tracetool, do not edit. */' - -def trace_d_begin(events): - if backend != 'dtrace': - print 'DTrace probe generator not applicable to %s backend' % backend - sys.exit(1) - print '/* This file is autogenerated by tracetool, do not edit. */' -# Registry of backends and their converter functions -converters = { - 'simple': { - 'h': simple_h, - 'c': simple_c, - }, - - 'nop': { - 'h': nop_h, - 'c': nop_c, - 'd': dtrace_nop_d, - 'stap': dtrace_nop_stp, - }, - - 'stderr': { - 'h': stderr_h, - 'c': stderr_c, - }, - - 'dtrace': { - 'h': dtrace_h, - 'c': dtrace_c, - 'd': dtrace_d, - 'stap': dtrace_stp - }, - - 'ust': { - 'h': ust_h, - 'c': ust_c, - }, - -} - -# Trace file header and footer code generators -formats = { - 'h': { - 'begin': trace_h_begin, - 'end': trace_h_end, - }, - 'c': { - 'begin': trace_c_begin, - }, - 'd': { - 'begin': trace_d_begin, - }, - 'stap': { - 'begin': trace_stap_begin, - }, -} - +###################################################################### # Event arguments + def type_is_string(type_): strtype = ('const char*', 'char*', 'const char *', 'char *') return type_.startswith(strtype) @@ -459,7 +518,11 @@ class Arguments: res = "" return res + + +###################################################################### # A trace event + import re cre = re.compile("((?P.*)\s+)?(?P[^(\s]+)\((?P[^)]*)\)\s*(?P\".*)?") @@ -490,34 +553,54 @@ def read_events(fobj): res.append(Event(line)) return res + +###################################################################### +# Main + +format_ = "" backend = "" -output = "" binary = "" targettype = "" targetarch = "" probeprefix = "" +def usage(): + print "Tracetool: Generate tracing code for trace events file on stdin" + print "Usage:" + print sys.argv[0], " --format= --backend=" + print + print "Output formats:" + for f in _formats: + print " %-10s %s" % (f, get_format_descr(f)) + print + print "Backends:" + for b in _backends: + print " %-10s %s" % (b, get_backend_descr(b)) + print """ +Options: + --binary [path] Full path to QEMU binary + --target-arch [arch] QEMU emulator target arch + --target-type [type] QEMU emulator target type ('system' or 'user') + --probe-prefix [prefix] Prefix for dtrace probe names + (default: qemu-targettype-targetarch) +""" + + sys.exit(1) + def main(): - global backend, output, binary, targettype, targetarch, probeprefix - supported_backends = ["simple", "nop", "stderr", "dtrace", "ust"] - short_options = "hcd" - long_options = ["stap", "backend=", "binary=", "target-arch=", "target-type=", "probe-prefix=", "list-backends", "check-backend"] + global format_, backend, binary, targettype, targetarch, probeprefix + + long_options = ["stap", "format=", "backend=", "binary=", "target-arch=", "target-type=", "probe-prefix=", "list-backends", "check-backend"] try: - opts, args = getopt.getopt(sys.argv[1:], short_options, long_options) + opts, args = getopt.getopt(sys.argv[1:], "", long_options) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) for opt, arg in opts: - if opt == '-h': - output = 'h' - elif opt == '-c': - output = 'c' - elif opt == '-d': - output = 'd' - elif opt == '--stap': - output = 'stap' + if opt == '--format': + format_ = arg elif opt == '--backend': backend = arg elif opt == '--binary': @@ -529,30 +612,40 @@ def main(): elif opt == '--probe-prefix': probeprefix = arg elif opt == '--list-backends': - print 'simple, nop, stderr, dtrace' + print ', '.join(_backends) sys.exit(0) elif opt == "--check-backend": - if any(backend in s for s in supported_backends): + if backend in _backends: sys.exit(0) else: sys.exit(1) else: - #assert False, "unhandled option" print "unhandled option: ", opt usage() - if backend == "" or output == "": + if format_ not in _formats: + print "Unknown format: %s" % format_ + print + usage() + if backend not in _backends: + print "Unknown backend: %s" % backend + print usage() - sys.exit(0) events = read_events(sys.stdin) - if 'begin' in formats[output]: - formats[output]['begin'](events) - converters[backend][output]([ e for e in events if 'disable' not in e.properties ]) - converters['nop'][output]([ e for e in events if 'disable' in e.properties ]) - if 'end' in formats[output]: - formats[output]['end'](events) + try: + # just force format/backend compatibility check + bfun = get_backend(format_, backend) + bnop = get_backend(format_, "nop") + except Exception as e: + sys.stderr.write(str(e) + "\n\n") + usage() + + get_format(format_, BEGIN)(events) + bfun([ e for e in events if "disable" not in e.properties ]) + bnop([ e for e in events if "disable" in e.properties ]) + get_format(format_, END)(events) if __name__ == "__main__": main()