#!/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