913540a376
The tracetool parser only picks up PRI*64 and other format string macros when enclosed between double quoted strings. Lift this restriction by extracting everything after the closing ')' as the format string: cpu_set_apic_base(uint64_t val) "%016"PRIx64 ^^ ^^ One trick here: it turns out that backslashes in the format string like "\n" were being interpreted by echo(1). Fix this by using the POSIX printf(1) command instead. Although it normally does not make sense to include backslashes in trace event format strings, an injected newline causes tracetool to emit a broken header file and I want to eliminate cases where broken output is emitted, even if the input was bad. Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
644 lines
11 KiB
Bash
Executable File
644 lines
11 KiB
Bash
Executable File
#!/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"
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
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 | tr '[:lower:]' '[:upper:]'`
|
|
|
|
# Define an empty function for the trace event
|
|
cat <<EOF
|
|
static inline void trace_$name($args) {
|
|
if (QEMU_${nameupper}_ENABLED()) {
|
|
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
|
|
# 'limit' is a reserved keyword
|
|
if [ "$arg" = "limit" ]; then
|
|
arg="_limit"
|
|
fi
|
|
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 disable
|
|
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"
|
|
else
|
|
"$process_line" "$str"
|
|
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
|