xref: /illumos-kvm-cmd/scripts/tracetool (revision 68396ea9)
1#!/bin/sh
2#
3# Code generator for trace events
4#
5# Copyright IBM, Corp. 2010
6#
7# This work is licensed under the terms of the GNU GPL, version 2.  See
8# the COPYING file in the top-level directory.
9
10# Disable pathname expansion, makes processing text with '*' characters simpler
11set -f
12
13usage()
14{
15    cat >&2 <<EOF
16usage: $0 [--nop | --simple | --stderr | --ust | --dtrace] [-h | -c]
17Generate tracing code for a file on stdin.
18
19Backends:
20  --nop     Tracing disabled
21  --simple  Simple built-in backend
22  --stderr  Stderr built-in backend
23  --ust     LTTng User Space Tracing backend
24  --dtrace  DTrace/SystemTAP backend
25
26Output formats:
27  -h     Generate .h file
28  -c     Generate .c file
29  -d     Generate .d file (DTrace only)
30  --stap Generate .stp file (DTrace with SystemTAP only)
31
32Options:
33  --binary      [path]  Full path to QEMU binary
34  --target-arch [arch]  QEMU emulator target arch
35  --target-type [type]  QEMU emulator target type ('system' or 'user')
36
37EOF
38    exit 1
39}
40
41# Get the name of a trace event
42get_name()
43{
44    echo ${1%%\(*}
45}
46
47# Get the argument list of a trace event, including types and names
48get_args()
49{
50    local args
51    args=${1#*\(}
52    args=${args%\)*}
53    echo "$args"
54}
55
56# Get the argument name list of a trace event
57get_argnames()
58{
59    local nfields field name sep
60    nfields=0
61    sep="$2"
62    for field in $(get_args "$1"); do
63        nfields=$((nfields + 1))
64
65        # Drop pointer star
66        field=${field#\*}
67
68        # Only argument names have commas at the end
69        name=${field%,}
70        test "$field" = "$name" && continue
71
72        printf "%s%s " $name $sep
73    done
74
75    # Last argument name
76    if [ "$nfields" -gt 1 ]
77    then
78        printf "%s" "$name"
79    fi
80}
81
82# Get the number of arguments to a trace event
83get_argc()
84{
85    local name argc
86    argc=0
87    for name in $(get_argnames "$1", ","); do
88        argc=$((argc + 1))
89    done
90    echo $argc
91}
92
93# Get the format string for a trace event
94get_fmt()
95{
96    local fmt
97    fmt=${1#*\"}
98    fmt=${fmt%\"*}
99    echo "$fmt"
100}
101
102# Get the state of a trace event
103get_state()
104{
105    local str disable state
106    str=$(get_name "$1")
107    disable=${str##disable }
108    if [ "$disable" = "$str" ] ; then
109        state=1
110    else
111        state=0
112    fi
113    echo "$state"
114}
115
116linetoh_begin_nop()
117{
118    return
119}
120
121linetoh_nop()
122{
123    local name args
124    name=$(get_name "$1")
125    args=$(get_args "$1")
126
127    # Define an empty function for the trace event
128    cat <<EOF
129static inline void trace_$name($args)
130{
131}
132EOF
133}
134
135linetoh_end_nop()
136{
137    return
138}
139
140linetoc_begin_nop()
141{
142    return
143}
144
145linetoc_nop()
146{
147    # No need for function definitions in nop backend
148    return
149}
150
151linetoc_end_nop()
152{
153    return
154}
155
156linetoh_begin_simple()
157{
158    cat <<EOF
159#include "simpletrace.h"
160EOF
161
162    simple_event_num=0
163}
164
165cast_args_to_uint64_t()
166{
167    local arg
168    for arg in $(get_argnames "$1", ","); do
169        printf "%s" "(uint64_t)(uintptr_t)$arg"
170    done
171}
172
173linetoh_simple()
174{
175    local name args argc trace_args state
176    name=$(get_name "$1")
177    args=$(get_args "$1")
178    argc=$(get_argc "$1")
179    state=$(get_state "$1")
180    if [ "$state" = "0" ]; then
181        name=${name##disable }
182    fi
183
184    trace_args="$simple_event_num"
185    if [ "$argc" -gt 0 ]
186    then
187        trace_args="$trace_args, $(cast_args_to_uint64_t "$1")"
188    fi
189
190    cat <<EOF
191static inline void trace_$name($args)
192{
193    trace$argc($trace_args);
194}
195EOF
196
197    simple_event_num=$((simple_event_num + 1))
198}
199
200linetoh_end_simple()
201{
202    cat <<EOF
203#define NR_TRACE_EVENTS $simple_event_num
204extern TraceEvent trace_list[NR_TRACE_EVENTS];
205EOF
206}
207
208linetoc_begin_simple()
209{
210    cat <<EOF
211#include "trace.h"
212
213TraceEvent trace_list[] = {
214EOF
215    simple_event_num=0
216
217}
218
219linetoc_simple()
220{
221    local name state
222    name=$(get_name "$1")
223    state=$(get_state "$1")
224    if [ "$state" = "0" ] ; then
225        name=${name##disable }
226    fi
227    cat <<EOF
228{.tp_name = "$name", .state=$state},
229EOF
230    simple_event_num=$((simple_event_num + 1))
231}
232
233linetoc_end_simple()
234{
235    cat <<EOF
236};
237EOF
238}
239
240#STDERR
241linetoh_begin_stderr()
242{
243    cat <<EOF
244#include <stdio.h>
245EOF
246}
247
248linetoh_stderr()
249{
250    local name args argnames argc fmt
251    name=$(get_name "$1")
252    args=$(get_args "$1")
253    argnames=$(get_argnames "$1" ",")
254    argc=$(get_argc "$1")
255    fmt=$(get_fmt "$1")
256
257    if [ "$argc" -gt 0 ]; then
258        argnames=", $argnames"
259    fi
260
261    cat <<EOF
262static inline void trace_$name($args)
263{
264    fprintf(stderr, "$name $fmt\n" $argnames);
265}
266EOF
267}
268
269linetoh_end_stderr()
270{
271return
272}
273
274linetoc_begin_stderr()
275{
276return
277}
278
279linetoc_stderr()
280{
281return
282}
283
284linetoc_end_stderr()
285{
286return
287}
288#END OF STDERR
289
290# Clean up after UST headers which pollute the namespace
291ust_clean_namespace() {
292    cat <<EOF
293#undef mutex_lock
294#undef mutex_unlock
295#undef inline
296#undef wmb
297EOF
298}
299
300linetoh_begin_ust()
301{
302    echo "#include <ust/tracepoint.h>"
303    ust_clean_namespace
304}
305
306linetoh_ust()
307{
308    local name args argnames
309    name=$(get_name "$1")
310    args=$(get_args "$1")
311    argnames=$(get_argnames "$1", ",")
312
313    cat <<EOF
314DECLARE_TRACE(ust_$name, TP_PROTO($args), TP_ARGS($argnames));
315#define trace_$name trace_ust_$name
316EOF
317}
318
319linetoh_end_ust()
320{
321    return
322}
323
324linetoc_begin_ust()
325{
326    cat <<EOF
327#include <ust/marker.h>
328$(ust_clean_namespace)
329#include "trace.h"
330EOF
331}
332
333linetoc_ust()
334{
335    local name args argnames fmt
336    name=$(get_name "$1")
337    args=$(get_args "$1")
338    argnames=$(get_argnames "$1", ",")
339    fmt=$(get_fmt "$1")
340
341    cat <<EOF
342DEFINE_TRACE(ust_$name);
343
344static void ust_${name}_probe($args)
345{
346    trace_mark(ust, $name, "$fmt", $argnames);
347}
348EOF
349
350    # Collect names for later
351    names="$names $name"
352}
353
354linetoc_end_ust()
355{
356    cat <<EOF
357static void __attribute__((constructor)) trace_init(void)
358{
359EOF
360
361    for name in $names; do
362        cat <<EOF
363    register_trace_ust_$name(ust_${name}_probe);
364EOF
365    done
366
367    echo "}"
368}
369
370linetoh_begin_dtrace()
371{
372    cat <<EOF
373#include "trace-dtrace.h"
374EOF
375}
376
377linetoh_dtrace()
378{
379    local name args argnames state nameupper
380    name=$(get_name "$1")
381    args=$(get_args "$1")
382    argnames=$(get_argnames "$1", ",")
383    state=$(get_state "$1")
384    if [ "$state" = "0" ] ; then
385        name=${name##disable }
386    fi
387
388    nameupper=`echo $name | tr '[:lower:]' '[:upper:]'`
389
390    # Define an empty function for the trace event
391    cat <<EOF
392static inline void trace_$name($args) {
393    if (QEMU_${nameupper}_ENABLED()) {
394        QEMU_${nameupper}($argnames);
395    }
396}
397EOF
398}
399
400linetoh_end_dtrace()
401{
402    return
403}
404
405linetoc_begin_dtrace()
406{
407    return
408}
409
410linetoc_dtrace()
411{
412    # No need for function definitions in dtrace backend
413    return
414}
415
416linetoc_end_dtrace()
417{
418    return
419}
420
421linetod_begin_dtrace()
422{
423    cat <<EOF
424provider qemu {
425EOF
426}
427
428linetod_dtrace()
429{
430    local name args state
431    name=$(get_name "$1")
432    args=$(get_args "$1")
433    state=$(get_state "$1")
434    if [ "$state" = "0" ] ; then
435        name=${name##disable }
436    fi
437
438    # DTrace provider syntax expects foo() for empty
439    # params, not foo(void)
440    if [ "$args" = "void" ]; then
441       args=""
442    fi
443
444    # Define prototype for probe arguments
445    cat <<EOF
446        probe $name($args);
447EOF
448}
449
450linetod_end_dtrace()
451{
452    cat <<EOF
453};
454EOF
455}
456
457linetostap_begin_dtrace()
458{
459    return
460}
461
462linetostap_dtrace()
463{
464    local i arg name args arglist state
465    name=$(get_name "$1")
466    args=$(get_args "$1")
467    arglist=$(get_argnames "$1", "")
468    state=$(get_state "$1")
469    if [ "$state" = "0" ] ; then
470        name=${name##disable }
471    fi
472
473    # Define prototype for probe arguments
474    cat <<EOF
475probe qemu.$targettype.$targetarch.$name = process("$binary").mark("$name")
476{
477EOF
478
479    i=1
480    for arg in $arglist
481    do
482        # 'limit' is a reserved keyword
483        if [ "$arg" = "limit" ]; then
484          arg="_limit"
485        fi
486        cat <<EOF
487  $arg = \$arg$i;
488EOF
489	i="$((i+1))"
490    done
491
492    cat <<EOF
493}
494EOF
495}
496
497linetostap_end_dtrace()
498{
499    return
500}
501
502# Process stdin by calling begin, line, and end functions for the backend
503convert()
504{
505    local begin process_line end str disable
506    begin="lineto$1_begin_$backend"
507    process_line="lineto$1_$backend"
508    end="lineto$1_end_$backend"
509
510    "$begin"
511
512    while read -r str; do
513        # Skip comments and empty lines
514        test -z "${str%%#*}" && continue
515
516        # Process the line.  The nop backend handles disabled lines.
517        disable=${str%%disable *}
518        echo
519        if test -z "$disable"; then
520            # Pass the disabled state as an arg for the simple
521            # or DTrace backends which handle it dynamically.
522            # For all other backends, call lineto$1_nop()
523            if [ $backend = "simple" -o "$backend" = "dtrace" ]; then
524                "$process_line" "$str"
525            else
526                "lineto$1_nop" "${str##disable }"
527            fi
528        else
529            "$process_line" "$str"
530        fi
531    done
532
533    echo
534    "$end"
535}
536
537tracetoh()
538{
539    cat <<EOF
540#ifndef TRACE_H
541#define TRACE_H
542
543/* This file is autogenerated by tracetool, do not edit. */
544
545#include "qemu-common.h"
546EOF
547    convert h
548    echo "#endif /* TRACE_H */"
549}
550
551tracetoc()
552{
553    echo "/* This file is autogenerated by tracetool, do not edit. */"
554    convert c
555}
556
557tracetod()
558{
559    if [ $backend != "dtrace" ]; then
560       echo "DTrace probe generator not applicable to $backend backend"
561       exit 1
562    fi
563    echo "/* This file is autogenerated by tracetool, do not edit. */"
564    convert d
565}
566
567tracetostap()
568{
569    if [ $backend != "dtrace" ]; then
570       echo "SystemTAP tapset generator not applicable to $backend backend"
571       exit 1
572    fi
573    if [ -z "$binary" ]; then
574       echo "--binary is required for SystemTAP tapset generator"
575       exit 1
576    fi
577    if [ -z "$targettype" ]; then
578       echo "--target-type is required for SystemTAP tapset generator"
579       exit 1
580    fi
581    if [ -z "$targetarch" ]; then
582       echo "--target-arch is required for SystemTAP tapset generator"
583       exit 1
584    fi
585    echo "/* This file is autogenerated by tracetool, do not edit. */"
586    convert stap
587}
588
589
590backend=
591output=
592binary=
593targettype=
594targetarch=
595
596
597until [ -z "$1" ]
598do
599  case "$1" in
600    "--nop" | "--simple" | "--stderr" | "--ust" | "--dtrace") backend="${1#--}" ;;
601
602    "--binary") shift ; binary="$1" ;;
603    "--target-arch") shift ; targetarch="$1" ;;
604    "--target-type") shift ; targettype="$1" ;;
605
606    "-h" | "-c" | "-d") output="${1#-}" ;;
607    "--stap") output="${1#--}" ;;
608
609    "--check-backend") exit 0 ;; # used by ./configure to test for backend
610
611    "--list-backends") # used by ./configure to list available backends
612          echo "nop simple stderr ust dtrace"
613          exit 0
614          ;;
615
616    *)
617      usage;;
618  esac
619  shift
620done
621
622if [ "$backend" = "" -o "$output" = "" ]; then
623  usage
624fi
625
626gen="traceto$output"
627"$gen"
628
629exit 0
630