b3d08c029d
This introduces a new tracing backend that targets the SystemTAP implementation of DTrace userspace tracing. The core functionality should be applicable and standard across any DTrace implementation on Solaris, OS-X, *BSD, but the Makefile rules will likely need some small additional changes to cope with OS specific build requirements. This backend builds a little differently from the other tracing backends. Specifically there is no 'trace.c' file, because the 'dtrace' command line tool generates a '.o' file directly from the dtrace probe definition file. The probe definition is usually named with a '.d' extension but QEMU uses '.d' files for its external makefile dependancy tracking, so this uses '.dtrace' as the extension for the probe definition file. The 'tracetool' program gains the ability to generate a trace.h file for DTrace, and also to generate the trace.d file containing the dtrace probe definition. Example usage of a dtrace probe in systemtap looks like: probe process("qemu").mark("qemu_malloc") { printf("Malloc %d %p\n", $arg1, $arg2); } * .gitignore: Ignore trace-dtrace.* * Makefile: Extra rules for generating DTrace files * Makefile.obj: Don't build trace.o for DTrace, use trace-dtrace.o generated by 'dtrace' instead * tracetool: Support for generating DTrace data files Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
480 lines
7.9 KiB
Bash
Executable File
480 lines
7.9 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 | --ust] [-h | -c]
|
|
Generate tracing code for a file on stdin.
|
|
|
|
Backends:
|
|
--nop Tracing disabled
|
|
--simple Simple 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)
|
|
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
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# Choose backend
|
|
case "$1" in
|
|
"--nop" | "--simple" | "--ust" | "--dtrace") backend="${1#--}" ;;
|
|
*) usage ;;
|
|
esac
|
|
shift
|
|
|
|
case "$1" in
|
|
"-h") tracetoh ;;
|
|
"-c") tracetoc ;;
|
|
"-d") tracetod ;;
|
|
"--check-backend") exit 0 ;; # used by ./configure to test for backend
|
|
*) usage ;;
|
|
esac
|
|
|
|
exit 0
|