tracetool: Rewrite infrastructure as python modules

The tracetool script is written in shell and has hit several portability
problems due to shell quirks or external tools across host platforms.
Additionally the amount of string processing and lack of real data
structures makes it tough to implement code generator backends for
tracers that are more complex.

This patch replaces the shell version of tracetool with a Python
version.  The new tracetool design is:

  scripts/tracetool.py - top-level script
  scripts/tracetool/backend/ - tracer backends live here (simple, ust)
  scripts/tracetool/format/  - output formats live here (.c, .h)

There is common code for trace-events definition parsing so that
backends can focus on generating code rather than parsing input.

Support for all existing backends (nop, stderr, simple, ust,
and dtrace) is added back in follow-up patches.

[Commit description written by Stefan Hajnoczi]

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
This commit is contained in:
Lluís Vilanova 2012-04-03 20:47:39 +02:00 committed by Stefan Hajnoczi
parent 6e7a7f3d9b
commit 650ab98d1d
8 changed files with 592 additions and 677 deletions

View file

@ -378,12 +378,12 @@ else
trace.h: trace.h-timestamp
endif
trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(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,sh $(SRC_PATH)/scripts/tracetool --$(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)
@ -396,7 +396,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,sh $(SRC_PATH)/scripts/tracetool --$(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)

View file

@ -59,12 +59,13 @@ TARGET_TYPE=system
endif
$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \
--$(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")
$(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) \
< $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp")
else
stap:
endif

4
configure vendored
View file

@ -1097,7 +1097,7 @@ echo " --disable-docs disable documentation build"
echo " --disable-vhost-net disable vhost-net acceleration support"
echo " --enable-vhost-net enable vhost-net acceleration support"
echo " --enable-trace-backend=B Set trace backend"
echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends)
echo " Available backends:" $($python "$source_path"/scripts/tracetool.py --list-backends)
echo " --with-trace-file=NAME Full PATH,NAME of file to store traces"
echo " Default:trace-<pid>"
echo " --disable-spice disable spice"
@ -2670,7 +2670,7 @@ fi
##########################################
# check if trace backend exists
sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null
$python "$source_path/scripts/tracetool.py" "--backend=$trace_backend" --check-backend > /dev/null 2> /dev/null
if test "$?" -ne 0 ; then
echo
echo "Error: invalid trace backend"

View file

@ -1,666 +0,0 @@
#!/bin/sh
#
# Code generator for trace events
#
# Copyright IBM, Corp. 2010
#
# This work is licensed under the terms of the GNU GPL, version 2. See
# the COPYING file in the top-level directory.
# Disable pathname expansion, makes processing text with '*' characters simpler
set -f
usage()
{
cat >&2 <<EOF
usage: $0 [--nop | --simple | --stderr | --ust | --dtrace] [-h | -c]
Generate tracing code for a file on stdin.
Backends:
--nop Tracing disabled
--simple Simple built-in backend
--stderr Stderr built-in backend
--ust LTTng User Space Tracing backend
--dtrace DTrace/SystemTAP 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)
EOF
exit 1
}
# Print a line without interpreting backslash escapes
#
# The built-in echo command may interpret backslash escapes without an option
# to disable this behavior.
puts()
{
printf "%s\n" "$1"
}
# Get the name of a trace event
get_name()
{
local name
name=${1%%\(*}
echo "${name##* }"
}
# Get the given property of a trace event
# 1: trace-events line
# 2: property name
# -> return 0 if property is present, or 1 otherwise
has_property()
{
local props prop
props=${1%%\(*}
props=${props% *}
for prop in $props; do
if [ "$prop" = "$2" ]; then
return 0
fi
done
return 1
}
# Get the argument list of a trace event, including types and names
get_args()
{
local args
args=${1#*\(}
args=${args%%\)*}
echo "$args"
if (echo "$args" | grep "[ *]next\($\|[, ]\)" > /dev/null 2>&1); then
echo -e "\n#error 'next' is a bad argument name (clash with systemtap keyword)\n "
fi
}
# Get the argument name list of a trace event
get_argnames()
{
local nfields field name sep
nfields=0
sep="$2"
for field in $(get_args "$1"); do
nfields=$((nfields + 1))
# Drop pointer star
field=${field#\*}
# Only argument names have commas at the end
name=${field%,}
test "$field" = "$name" && continue
printf "%s%s " $name $sep
done
# Last argument name
if [ "$nfields" -gt 1 ]
then
printf "%s" "$name"
fi
}
# Get the number of arguments to a trace event
get_argc()
{
local name argc
argc=0
for name in $(get_argnames "$1", ","); do
argc=$((argc + 1))
done
echo $argc
}
# Get the format string including double quotes for a trace event
get_fmt()
{
puts "${1#*)}"
}
linetoh_begin_nop()
{
return
}
linetoh_nop()
{
local name args
name=$(get_name "$1")
args=$(get_args "$1")
# Define an empty function for the trace event
cat <<EOF
static inline void trace_$name($args)
{
}
EOF
}
linetoh_end_nop()
{
return
}
linetoc_begin_nop()
{
return
}
linetoc_nop()
{
# No need for function definitions in nop backend
return
}
linetod_nop()
{
# Used when "disabled" events are processed
return
}
linetostap_nop()
{
# Used when "disabled" events are processed
return
}
linetoc_end_nop()
{
return
}
linetoh_begin_simple()
{
cat <<EOF
#include "trace/simple.h"
EOF
simple_event_num=0
}
cast_args_to_uint64_t()
{
local arg
for arg in $(get_argnames "$1", ","); do
printf "%s" "(uint64_t)(uintptr_t)$arg"
done
}
linetoh_simple()
{
local name args argc trace_args
name=$(get_name "$1")
args=$(get_args "$1")
argc=$(get_argc "$1")
trace_args="$simple_event_num"
if [ "$argc" -gt 0 ]
then
trace_args="$trace_args, $(cast_args_to_uint64_t "$1")"
fi
cat <<EOF
static inline void trace_$name($args)
{
trace$argc($trace_args);
}
EOF
simple_event_num=$((simple_event_num + 1))
}
linetoh_end_simple()
{
cat <<EOF
#define NR_TRACE_EVENTS $simple_event_num
extern TraceEvent trace_list[NR_TRACE_EVENTS];
EOF
}
linetoc_begin_simple()
{
cat <<EOF
#include "trace.h"
TraceEvent trace_list[] = {
EOF
simple_event_num=0
}
linetoc_simple()
{
local name
name=$(get_name "$1")
cat <<EOF
{.tp_name = "$name", .state=0},
EOF
simple_event_num=$((simple_event_num + 1))
}
linetoc_end_simple()
{
cat <<EOF
};
EOF
}
#STDERR
linetoh_begin_stderr()
{
cat <<EOF
#include <stdio.h>
#include "trace/stderr.h"
extern TraceEvent trace_list[];
EOF
stderr_event_num=0
}
linetoh_stderr()
{
local name args argnames argc fmt
name=$(get_name "$1")
args=$(get_args "$1")
argnames=$(get_argnames "$1" ",")
argc=$(get_argc "$1")
fmt=$(get_fmt "$1")
if [ "$argc" -gt 0 ]; then
argnames=", $argnames"
fi
cat <<EOF
static inline void trace_$name($args)
{
if (trace_list[$stderr_event_num].state != 0) {
fprintf(stderr, "$name " $fmt "\n" $argnames);
}
}
EOF
stderr_event_num=$((stderr_event_num + 1))
}
linetoh_end_stderr()
{
cat <<EOF
#define NR_TRACE_EVENTS $stderr_event_num
EOF
}
linetoc_begin_stderr()
{
cat <<EOF
#include "trace.h"
TraceEvent trace_list[] = {
EOF
stderr_event_num=0
}
linetoc_stderr()
{
local name
name=$(get_name "$1")
cat <<EOF
{.tp_name = "$name", .state=0},
EOF
stderr_event_num=$(($stderr_event_num + 1))
}
linetoc_end_stderr()
{
cat <<EOF
};
EOF
}
#END OF STDERR
# Clean up after UST headers which pollute the namespace
ust_clean_namespace() {
cat <<EOF
#undef mutex_lock
#undef mutex_unlock
#undef inline
#undef wmb
EOF
}
linetoh_begin_ust()
{
echo "#include <ust/tracepoint.h>"
ust_clean_namespace
}
linetoh_ust()
{
local name args argnames
name=$(get_name "$1")
args=$(get_args "$1")
argnames=$(get_argnames "$1", ",")
cat <<EOF
DECLARE_TRACE(ust_$name, TP_PROTO($args), TP_ARGS($argnames));
#define trace_$name trace_ust_$name
EOF
}
linetoh_end_ust()
{
return
}
linetoc_begin_ust()
{
cat <<EOF
#include <ust/marker.h>
$(ust_clean_namespace)
#include "trace.h"
EOF
}
linetoc_ust()
{
local name args argnames fmt
name=$(get_name "$1")
args=$(get_args "$1")
argnames=$(get_argnames "$1", ",")
[ -z "$argnames" ] || argnames=", $argnames"
fmt=$(get_fmt "$1")
cat <<EOF
DEFINE_TRACE(ust_$name);
static void ust_${name}_probe($args)
{
trace_mark(ust, $name, $fmt$argnames);
}
EOF
# Collect names for later
names="$names $name"
}
linetoc_end_ust()
{
cat <<EOF
static void __attribute__((constructor)) trace_init(void)
{
EOF
for name in $names; do
cat <<EOF
register_trace_ust_$name(ust_${name}_probe);
EOF
done
echo "}"
}
linetoh_begin_dtrace()
{
cat <<EOF
#include "trace-dtrace.h"
EOF
}
linetoh_dtrace()
{
local name args argnames nameupper
name=$(get_name "$1")
args=$(get_args "$1")
argnames=$(get_argnames "$1", ",")
nameupper=`echo $name | LC_ALL=C tr '[a-z]' '[A-Z]'`
# Define an empty function for the trace event
cat <<EOF
static inline void trace_$name($args) {
QEMU_${nameupper}($argnames);
}
EOF
}
linetoh_end_dtrace()
{
return
}
linetoc_begin_dtrace()
{
return
}
linetoc_dtrace()
{
# No need for function definitions in dtrace backend
return
}
linetoc_end_dtrace()
{
return
}
linetod_begin_dtrace()
{
cat <<EOF
provider qemu {
EOF
}
linetod_dtrace()
{
local name args
name=$(get_name "$1")
args=$(get_args "$1")
# DTrace provider syntax expects foo() for empty
# params, not foo(void)
if [ "$args" = "void" ]; then
args=""
fi
# Define prototype for probe arguments
cat <<EOF
probe $name($args);
EOF
}
linetod_end_dtrace()
{
cat <<EOF
};
EOF
}
linetostap_begin_dtrace()
{
return
}
linetostap_dtrace()
{
local i arg name args arglist
name=$(get_name "$1")
args=$(get_args "$1")
arglist=$(get_argnames "$1", "")
# Define prototype for probe arguments
cat <<EOF
probe $probeprefix.$name = process("$binary").mark("$name")
{
EOF
i=1
for arg in $arglist
do
# postfix reserved words with '_'
case "$arg" in
limit|in|next|self)
arg="${arg}_"
;;
esac
cat <<EOF
$arg = \$arg$i;
EOF
i="$((i+1))"
done
cat <<EOF
}
EOF
}
linetostap_end_dtrace()
{
return
}
# Process stdin by calling begin, line, and end functions for the backend
convert()
{
local begin process_line end str name NAME enabled
begin="lineto$1_begin_$backend"
process_line="lineto$1_$backend"
end="lineto$1_end_$backend"
"$begin"
while read -r str; do
# Skip comments and empty lines
test -z "${str%%#*}" && continue
echo
# Process the line. The nop backend handles disabled lines.
if has_property "$str" "disable"; then
"lineto$1_nop" "$str"
enabled=0
else
"$process_line" "$str"
enabled=1
fi
if [ "$1" = "h" ]; then
name=$(get_name "$str")
NAME=$(echo $name | LC_ALL=C tr '[a-z]' '[A-Z]')
echo "#define TRACE_${NAME}_ENABLED ${enabled}"
fi
done
echo
"$end"
}
tracetoh()
{
cat <<EOF
#ifndef TRACE_H
#define TRACE_H
/* This file is autogenerated by tracetool, do not edit. */
#include "qemu-common.h"
EOF
convert h
echo "#endif /* TRACE_H */"
}
tracetoc()
{
echo "/* This file is autogenerated by tracetool, do not edit. */"
convert c
}
tracetod()
{
if [ $backend != "dtrace" ]; then
echo "DTrace probe generator not applicable to $backend backend"
exit 1
fi
echo "/* This file is autogenerated by tracetool, do not edit. */"
convert d
}
tracetostap()
{
if [ $backend != "dtrace" ]; then
echo "SystemTAP tapset generator not applicable to $backend backend"
exit 1
fi
if [ -z "$binary" ]; then
echo "--binary is required for SystemTAP tapset generator"
exit 1
fi
if [ -z "$probeprefix" -a -z "$targettype" ]; then
echo "--target-type is required for SystemTAP tapset generator"
exit 1
fi
if [ -z "$probeprefix" -a -z "$targetarch" ]; then
echo "--target-arch is required for SystemTAP tapset generator"
exit 1
fi
if [ -z "$probeprefix" ]; then
probeprefix="qemu.$targettype.$targetarch";
fi
echo "/* This file is autogenerated by tracetool, do not edit. */"
convert stap
}
backend=
output=
binary=
targettype=
targetarch=
probeprefix=
until [ -z "$1" ]
do
case "$1" in
"--nop" | "--simple" | "--stderr" | "--ust" | "--dtrace") backend="${1#--}" ;;
"--binary") shift ; binary="$1" ;;
"--target-arch") shift ; targetarch="$1" ;;
"--target-type") shift ; targettype="$1" ;;
"--probe-prefix") shift ; probeprefix="$1" ;;
"-h" | "-c" | "-d") output="${1#-}" ;;
"--stap") output="${1#--}" ;;
"--check-backend") exit 0 ;; # used by ./configure to test for backend
"--list-backends") # used by ./configure to list available backends
echo "nop simple stderr ust dtrace"
exit 0
;;
*)
usage;;
esac
shift
done
if [ "$backend" = "" -o "$output" = "" ]; then
usage
fi
gen="traceto$output"
"$gen"
exit 0

108
scripts/tracetool.py Executable file
View file

@ -0,0 +1,108 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Command-line wrapper for the tracetool machinery.
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
__copyright__ = "Copyright 2012, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
__email__ = "stefanha@linux.vnet.ibm.com"
import sys
import getopt
from tracetool import error_write, out
import tracetool.backend
import tracetool.format
_SCRIPT = ""
def error_opt(msg = None):
if msg is not None:
error_write("Error: " + msg + "\n")
backend_descr = "\n".join([ " %-15s %s" % (n, d)
for n,d in tracetool.backend.get_list() ])
format_descr = "\n".join([ " %-15s %s" % (n, d)
for n,d in tracetool.format.get_list() ])
error_write("""\
Usage: %(script)s --format=<format> --backend=<backend> [<options>]
Backends:
%(backends)s
Formats:
%(formats)s
Options:
--help This help message.
--list-backends Print list of available backends.
--check-backend Check if the given backend is valid.
""" % {
"script" : _SCRIPT,
"backends" : backend_descr,
"formats" : format_descr,
})
if msg is None:
sys.exit(0)
else:
sys.exit(1)
def main(args):
global _SCRIPT
_SCRIPT = args[0]
long_opts = [ "backend=", "format=", "help", "list-backends", "check-backend" ]
long_opts += [ "binary=", "target-type=", "target-arch=", "probe-prefix=" ]
try:
opts, args = getopt.getopt(args[1:], "", long_opts)
except getopt.GetoptError as err:
error_opt(str(err))
check_backend = False
arg_backend = ""
arg_format = ""
for opt, arg in opts:
if opt == "--help":
error_opt()
elif opt == "--backend":
arg_backend = arg
elif opt == "--format":
arg_format = arg
elif opt == "--list-backends":
backends = tracetool.backend.get_list()
out(", ".join([ b for b,_ in backends ]))
sys.exit(0)
elif opt == "--check-backend":
check_backend = True
else:
error_opt("unhandled option: %s" % opt)
if arg_backend is None:
error_opt("backend not set")
if check_backend:
if tracetool.backend.exists(arg_backend):
sys.exit(0)
else:
sys.exit(1)
try:
tracetool.generate(sys.stdin, arg_format, arg_backend)
except tracetool.TracetoolError as e:
error_opt(str(e))
if __name__ == "__main__":
main(sys.argv)

View file

@ -0,0 +1,262 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Machinery for generating tracing-related intermediate files.
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
__copyright__ = "Copyright 2012, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
__email__ = "stefanha@linux.vnet.ibm.com"
import re
import sys
import tracetool.format
import tracetool.backend
def error_write(*lines):
"""Write a set of error lines."""
sys.stderr.writelines("\n".join(lines) + "\n")
def error(*lines):
"""Write a set of error lines and exit."""
error_write(*lines)
sys.exit(1)
def out(*lines, **kwargs):
"""Write a set of output lines.
You can use kwargs as a shorthand for mapping variables when formating all
the strings in lines.
"""
lines = [ l % kwargs for l in lines ]
sys.stdout.writelines("\n".join(lines) + "\n")
class Arguments:
"""Event arguments description."""
def __init__(self, args):
"""
Parameters
----------
args :
List of (type, name) tuples.
"""
self._args = args
@staticmethod
def build(arg_str):
"""Build and Arguments instance from an argument string.
Parameters
----------
arg_str : str
String describing the event arguments.
"""
res = []
for arg in arg_str.split(","):
arg = arg.strip()
parts = arg.split()
head, sep, tail = parts[-1].rpartition("*")
parts = parts[:-1]
if tail == "void":
assert len(parts) == 0 and sep == ""
continue
arg_type = " ".join(parts + [ " ".join([head, sep]).strip() ]).strip()
res.append((arg_type, tail))
return Arguments(res)
def __iter__(self):
"""Iterate over the (type, name) pairs."""
return iter(self._args)
def __len__(self):
"""Number of arguments."""
return len(self._args)
def __str__(self):
"""String suitable for declaring function arguments."""
if len(self._args) == 0:
return "void"
else:
return ", ".join([ " ".join([t, n]) for t,n in self._args ])
def __repr__(self):
"""Evaluable string representation for this object."""
return "Arguments(\"%s\")" % str(self)
def names(self):
"""List of argument names."""
return [ name for _, name in self._args ]
def types(self):
"""List of argument types."""
return [ type_ for type_, _ in self._args ]
class Event(object):
"""Event description.
Attributes
----------
name : str
The event name.
fmt : str
The event format string.
properties : set(str)
Properties of the event.
args : Arguments
The event arguments.
"""
_CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?")
_VALID_PROPS = set(["disable"])
def __init__(self, name, props, fmt, args):
"""
Parameters
----------
name : string
Event name.
props : list of str
Property names.
fmt : str
Event printing format.
args : Arguments
Event arguments.
"""
self.name = name
self.properties = props
self.fmt = fmt
self.args = args
unknown_props = set(self.properties) - self._VALID_PROPS
if len(unknown_props) > 0:
raise ValueError("Unknown properties: %s" % ", ".join(unknown_props))
@staticmethod
def build(line_str):
"""Build an Event instance from a string.
Parameters
----------
line_str : str
Line describing the event.
"""
m = Event._CRE.match(line_str)
assert m is not None
groups = m.groupdict('')
name = groups["name"]
props = groups["props"].split()
fmt = groups["fmt"]
args = Arguments.build(groups["args"])
return Event(name, props, fmt, args)
def __repr__(self):
"""Evaluable string representation for this object."""
return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
self.name,
self.args,
self.fmt)
def _read_events(fobj):
res = []
for line in fobj:
if not line.strip():
continue
if line.lstrip().startswith('#'):
continue
res.append(Event.build(line))
return res
class TracetoolError (Exception):
"""Exception for calls to generate."""
pass
def try_import(mod_name, attr_name = None, attr_default = None):
"""Try to import a module and get an attribute from it.
Parameters
----------
mod_name : str
Module name.
attr_name : str, optional
Name of an attribute in the module.
attr_default : optional
Default value if the attribute does not exist in the module.
Returns
-------
A pair indicating whether the module could be imported and the module or
object or attribute value.
"""
try:
module = __import__(mod_name, fromlist=["__package__"])
if attr_name is None:
return True, module
return True, getattr(module, str(attr_name), attr_default)
except ImportError:
return False, None
def generate(fevents, format, backend):
"""Generate the output for the given (format, backend) pair.
Parameters
----------
fevents : file
Event description file.
format : str
Output format name.
backend : str
Output backend name.
"""
# fix strange python error (UnboundLocalError tracetool)
import tracetool
format = str(format)
if len(format) is 0:
raise TracetoolError("format not set")
mformat = format.replace("-", "_")
if not tracetool.format.exists(mformat):
raise TracetoolError("unknown format: %s" % format)
backend = str(backend)
if len(backend) is 0:
raise TracetoolError("backend not set")
mbackend = backend.replace("-", "_")
if not tracetool.backend.exists(mbackend):
raise TracetoolError("unknown backend: %s" % backend)
if not tracetool.backend.compatible(mbackend, mformat):
raise TracetoolError("backend '%s' not compatible with format '%s'" %
(backend, format))
events = _read_events(fevents)
if backend == "nop":
( e.properies.add("disable") for e in events )
tracetool.format.generate_begin(mformat, events)
tracetool.backend.generate("nop", format,
[ e
for e in events
if "disable" in e.properties ])
tracetool.backend.generate(backend, format,
[ e
for e in events
if "disable" not in e.properties ])
tracetool.format.generate_end(mformat, events)

View file

@ -0,0 +1,111 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Backend management.
Creating new backends
---------------------
A new backend named 'foo-bar' corresponds to Python module
'tracetool/backend/foo_bar.py'.
A backend module should provide a docstring, whose first non-empty line will be
considered its short description.
All backends must generate their contents through the 'tracetool.out' routine.
Backend functions
-----------------
======== =======================================================================
Function Description
======== =======================================================================
<format> Called to generate the format- and backend-specific code for each of
the specified events. If the function does not exist, the backend is
considered not compatible with the given format.
======== =======================================================================
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
__copyright__ = "Copyright 2012, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
__email__ = "stefanha@linux.vnet.ibm.com"
import pkgutil
import tracetool
def get_list():
"""Get a list of (name, description) pairs."""
res = [("nop", "Tracing disabled.")]
for _, modname, _ in pkgutil.iter_modules(tracetool.backend.__path__):
module = tracetool.try_import("tracetool.backend." + modname)
# just in case; should never fail unless non-module files are put there
if not module[0]:
continue
module = module[1]
doc = module.__doc__
if doc is None:
doc = ""
doc = doc.strip().split("\n")[0]
name = modname.replace("_", "-")
res.append((name, doc))
return res
def exists(name):
"""Return whether the given backend exists."""
if len(name) == 0:
return False
if name == "nop":
return True
name = name.replace("-", "_")
return tracetool.try_import("tracetool.backend." + name)[1]
def compatible(backend, format):
"""Whether a backend is compatible with the given format."""
if not exists(backend):
raise ValueError("unknown backend: %s" % backend)
backend = backend.replace("-", "_")
format = format.replace("-", "_")
if backend == "nop":
return True
else:
func = tracetool.try_import("tracetool.backend." + backend,
format, None)[1]
return func is not None
def _empty(events):
pass
def generate(backend, format, events):
"""Generate the per-event output for the given (backend, format) pair."""
if not compatible(backend, format):
raise ValueError("backend '%s' not compatible with format '%s'" %
(backend, format))
backend = backend.replace("-", "_")
format = format.replace("-", "_")
if backend == "nop":
func = tracetool.try_import("tracetool.format." + format,
"nop", _empty)[1]
else:
func = tracetool.try_import("tracetool.backend." + backend,
format, None)[1]
func(events)

View file

@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Format management.
Creating new formats
--------------------
A new format named 'foo-bar' corresponds to Python module
'tracetool/format/foo_bar.py'.
A format module should provide a docstring, whose first non-empty line will be
considered its short description.
All formats must generate their contents through the 'tracetool.out' routine.
Format functions
----------------
All the following functions are optional, and no output will be generated if
they do not exist.
======== =======================================================================
Function Description
======== =======================================================================
begin Called to generate the format-specific file header.
end Called to generate the format-specific file footer.
nop Called to generate the per-event contents when the event is disabled or
the selected backend is 'nop'.
======== =======================================================================
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
__copyright__ = "Copyright 2012, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
__email__ = "stefanha@linux.vnet.ibm.com"
import pkgutil
import tracetool
def get_list():
"""Get a list of (name, description) pairs."""
res = []
for _, modname, _ in pkgutil.iter_modules(tracetool.format.__path__):
module = tracetool.try_import("tracetool.format." + modname)
# just in case; should never fail unless non-module files are put there
if not module[0]:
continue
module = module[1]
doc = module.__doc__
if doc is None:
doc = ""
doc = doc.strip().split("\n")[0]
name = modname.replace("_", "-")
res.append((name, doc))
return res
def exists(name):
"""Return whether the given format exists."""
if len(name) == 0:
return False
name = name.replace("-", "_")
return tracetool.try_import("tracetool.format." + name)[1]
def _empty(events):
pass
def generate_begin(name, events):
"""Generate the header of the format-specific file."""
if not exists(name):
raise ValueError("unknown format: %s" % name)
name = name.replace("-", "_")
func = tracetool.try_import("tracetool.format." + name,
"begin", _empty)[1]
func(events)
def generate_end(name, events):
"""Generate the footer of the format-specific file."""
if not exists(name):
raise ValueError("unknown format: %s" % name)
name = name.replace("-", "_")
func = tracetool.try_import("tracetool.format." + name,
"end", _empty)[1]
func(events)