qemu/scripts/tracetool

630 lines
10 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')
EOF
exit 1
}
# Get the name of a trace event
get_name()
{
echo ${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 for a trace event
get_fmt()
{
local fmt
fmt=${1#*\"}
fmt=${fmt%\"*}
echo "$fmt"
}
# Get the state of a trace event
get_state()
{
local str disable state
str=$(get_name "$1")
disable=${str##disable }
if [ "$disable" = "$str" ] ; then
state=1
else
state=0
fi
echo "$state"
}
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 "simpletrace.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 state
name=$(get_name "$1")
args=$(get_args "$1")
argc=$(get_argc "$1")
state=$(get_state "$1")
if [ "$state" = "0" ]; then
name=${name##disable }
fi
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 state
name=$(get_name "$1")
state=$(get_state "$1")
if [ "$state" = "0" ] ; then
name=${name##disable }
fi
cat <<EOF
{.tp_name = "$name", .state=$state},
EOF
simple_event_num=$((simple_event_num + 1))
}
linetoc_end_simple()
{
cat <<EOF
};
EOF
}
#STDERR
linetoh_begin_stderr()
{
cat <<EOF
#include <stdio.h>
EOF
}
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)
{
fprintf(stderr, "$name $fmt\n" $argnames);
}
EOF
}
linetoh_end_stderr()
{
return
}
linetoc_begin_stderr()
{
return
}
linetoc_stderr()
{
return
}
linetoc_end_stderr()
{
return
}
#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", ",")
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 state nameupper
name=$(get_name "$1")
args=$(get_args "$1")
argnames=$(get_argnames "$1", ",")
state=$(get_state "$1")
if [ "$state" = "0" ] ; then
name=${name##disable }
fi
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 state
name=$(get_name "$1")
args=$(get_args "$1")
state=$(get_state "$1")
if [ "$state" = "0" ] ; then
name=${name##disable }
fi
# 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 state
name=$(get_name "$1")
args=$(get_args "$1")
arglist=$(get_argnames "$1", "")
state=$(get_state "$1")
if [ "$state" = "0" ] ; then
name=${name##disable }
fi
# Define prototype for probe arguments
cat <<EOF
probe qemu.$targettype.$targetarch.$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
# Process the line. The nop backend handles disabled lines.
disable=${str%%disable *}
echo
if test -z "$disable"; then
# Pass the disabled state as an arg for the simple
# or DTrace backends which handle it dynamically.
# For all other backends, call lineto$1_nop()
if [ $backend = "simple" -o "$backend" = "dtrace" ]; then
"$process_line" "$str"
else
"lineto$1_nop" "${str##disable }"
fi
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 "$targettype" ]; then
echo "--target-type is required for SystemTAP tapset generator"
exit 1
fi
if [ -z "$targetarch" ]; then
echo "--target-arch is required for SystemTAP tapset generator"
exit 1
fi
echo "/* This file is autogenerated by tracetool, do not edit. */"
convert stap
}
backend=
output=
binary=
targettype=
targetarch=
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" ;;
"-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