mirror of https://gitee.com/openkylin/qemu.git
638 lines
11 KiB
Bash
Executable File
638 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
|
|
}
|
|
|
|
# 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 for a trace event
|
|
get_fmt()
|
|
{
|
|
local fmt
|
|
fmt=${1#*\"}
|
|
fmt=${fmt%\"*}
|
|
echo "$fmt"
|
|
}
|
|
|
|
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
|