### 7.2 Tracing macro calls ¶

It is possible to trace macro calls and expansions through the builtins `traceon` and `traceoff`:

Builtin: traceon ([names…]) ¶
Builtin: traceoff ([names…]) ¶

  When called without any arguments, `traceon` and `traceoff` will turn tracing on and off, respectively, for all currently defined macros.

  When called with arguments, only the macros listed in names are affected, whether or not they are currently defined.

  The expansion of `traceon` and `traceoff` is void.

Whenever a traced macro is called and the arguments have been collected, the call is displayed. If the expansion of the macro call is not void, the expansion can be displayed after the call. The output is printed to the current debug file (defaulting to standard error, see Saving debugging output).

    $ m4 -d
    define(`foo', `Hello World.')
    ⇒
    define(`echo', `$@')
    ⇒
    traceon(`foo', `echo')
    ⇒
    foo
    error→m4trace: -1- foo -> `Hello World.'
    ⇒Hello World.
    echo(`gnus', `and gnats')
    error→m4trace: -1- echo(`gnus', `and gnats') -> ``gnus',`and gnats''
    ⇒gnus,and gnats

The number between dashes is the depth of the expansion. It is one most of the time, signifying an expansion at the outermost level, but it increases when macro arguments contain unquoted macro calls. The maximum number that will appear between dashes is controlled by the option `--nesting-limit` (or `-L`, see Invoking m4). Additionally, the option `--trace` (or `-t`) can be used to invoke `traceon(name)` before parsing input.

    $ m4 -L 3 -t ifelse
    ifelse(`one level')
    error→m4trace: -1- ifelse
    ⇒
    ifelse(ifelse(ifelse(`three levels')))
    error→m4trace: -3- ifelse
    error→m4trace: -2- ifelse
    error→m4trace: -1- ifelse
    ⇒
    ifelse(ifelse(ifelse(ifelse(`four levels'))))
    error→m4:stdin:3: recursion limit of 3 exceeded, use -L<N> to change it

Tracing by name is an attribute that is preserved whether the macro is defined or not. This allows the selection of macros to trace before those macros are defined.

    $ m4 -d
    traceoff(`foo')
    ⇒
    traceon(`foo')
    ⇒
    foo
    ⇒foo
    defn(`foo')
    ⇒
    define(`foo', `bar')
    ⇒
    foo
    error→m4trace: -1- foo -> `bar'
    ⇒bar
    undefine(`foo')
    ⇒
    ifdef(`foo', `yes', `no')
    ⇒no
    indir(`foo')
    error→m4:stdin:9: undefined macro `foo'
    ⇒
    define(`foo', `blah')
    ⇒
    foo
    error→m4trace: -1- foo -> `blah'
    ⇒blah
    traceoff
    ⇒
    foo
    ⇒blah

Tracing even works on builtins. However, `defn` (see Renaming macros) does not transfer tracing status.

    $ m4 -d
    traceon(`traceon')
    ⇒
    traceon(`traceoff')
    error→m4trace: -1- traceon(`traceoff')
    ⇒
    traceoff(`traceoff')
    error→m4trace: -1- traceoff(`traceoff')
    ⇒
    traceoff(`traceon')
    ⇒
    traceon(`eval', `m4_divnum')
    ⇒
    define(`m4_eval', defn(`eval'))
    ⇒
    define(`m4_divnum', defn(`divnum'))
    ⇒
    eval(divnum)
    error→m4trace: -1- eval(`0') -> `0'
    ⇒0
    m4_eval(m4_divnum)
    error→m4trace: -2- m4_divnum -> `0'
    ⇒0

See Controlling debugging output, for information on controlling the details of the display. The format of the trace output is not specified by POSIX, and varies between implementations of `m4`.