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