REBOL 3 Docs Guide Concepts Functions Datatypes Errors
  TOC < Back Next >   Updated: 3-Aug-2010 Edit History  

REBOL 3 Functions: trace

trace  mode  /back  /function

Enables and disables evaluation tracing and backtrace.

Arguments:

mode [integer! logic!]

Refinements:

/back - Set mode ON to enable or integer for lines to display

/function - Traces functions only (less output)

See also:

echo   probe   stack  

Note this special requirement:

Security

In order to use trace, you must have secure debug properly set. You can use any of these methods to do so:

  • Run REBOL with -s option.
  • Type secure [debug allow] or put it in your rebol.r file.
  • Type secure none (but don't put it in your rebol.r file.)

Contents

Description

The trace lets you watch the evaluation of your script, expression by expression.

The three most common arguments to trace are shown here:

trace on   ; turn on trace
trace off  ; turn off trace
trace 5    ; turn on, but trace only 5 levels deep

Once enabled, when you evaluate an expression, you will see each step as a single line:

>> print 123
 1: print : native! [value]
 2: 123
--> print
123
<-- print == unset!

Understanding the format

The trace format uses these formatting notations to indicate what your code is doing:

Notation Meaning
(indent) The indentation for each line indicates the depth of the code.
N:
The index number of the value in the code block (that is to be evaluated.)
-->
Entry into a function, followed by its formal argument list.
<--
Return from a function, followed by the value it returned (==).

Simple example

To help understand the format, here's a description for each line in the earlier example:

Code Meaning
>> print 123
Typed into the console to evaluate.
1: print : native! [value]
The value at block index 1 is the word print. It's value is looked up and found to be a native! function that takes value as an argument.
2: 123
The value at block index 2 is the integer 123.
--> print
The argument is valid and the print function is entered. The --> means "enter into the function."
123
Output is printed.
<-- print == unset!
The print function returns, but it has no return value (it is unset.) The <-- means "return from the function."

Larger example

Here is a user defined function to compute the average of a block of numbers.

ave: func [nums [block!] /local val][
    val: 0
    foreach num nums [val: val + num]
    val / length? nums
]

Tracing the evaluation, you will see how each new level is indented and begins a new sequence of index numbers. Notice also the foreach loop.

>> ave [1 2 3]
 1: ave : function! [nums /local val]
 2: [1 2 3]
--> ave
     1: val:
     2: 0
     3: foreach : native! ['word data body]
     5: nums : [1 2 3]
     6: [val: val + num]
    --> foreach
         1: val:
         2: val : 0
         3: + : op! [value1 value2]
         4: num : 1
        --> +
        <-- + == 1
         1: val:
         2: val : 1
         3: + : op! [value1 value2]
         4: num : 2
        --> +
        <-- + == 3
         1: val:
         2: val : 3
         3: + : op! [value1 value2]
         4: num : 3
        --> +
        <-- + == 6
    <-- foreach == 6
     7: val : 6
     8: / : op! [value1 value2]
     9: length? : action! [series]
    10: nums : [1 2 3]
    --> length?
    <-- length? == 3
    --> /
    <-- / == 2
<-- ave == 2
== 2

Minimizing the output

At times the trace output will be a lot more than you want. The trick becomes how to cut it down without losing the information you need.. There are three methods:

  1. Specify a trace depth.
  2. Locate the trace on and off lines deeper within your code.
  3. Trace only functions, not all values.
  4. Use the backtrace option. (see more below)

Setting trace depth

Using the example above, set the trace depth to 2, and run it again. You will see:

>> trace 2
>> ave [1 2 3]
 1: ave : function! [nums /local val]
 2: [1 2 3]
--> ave
     1: val:
     2: 0
     3: foreach : native! ['word data body]
     5: nums : [1 2 3]
     6: [val: val + num]
    --> foreach
    <-- foreach == 6
     7: val : 6
     8: / : op! [value1 value2]
     9: length? : action! [series]
    10: nums : [1 2 3]
    --> length?
    <-- length? == 3
    --> /
    <-- / == 2
<-- ave == 2
== 2

The output has been reduced. You no longer see the foreach loop operate.

Locating trace within your code

Most of the time you don't need to trace your entire program, just part of it. So, it is useful just to put trace in your code where you need it.

Using the same example as above:

ave: func [nums [block!] /local val][
    val: 0
    trace on
    foreach num nums [val: val + num]
    trace off
    val / length? nums
]

You will now see:

>> ave [1 2 3]
<-- trace == unset!
 5: foreach : native! ['word data body]
 7: nums : [1 2 3]
 8: [val: val + num]
--> foreach
     1: val:
     2: val : 0
     3: + : op! [value1 value2]
     4: num : 1
    --> +
    <-- + == 1
     1: val:
     2: val : 1
     3: + : op! [value1 value2]
     4: num : 2
    --> +
    <-- + == 3
     1: val:
     2: val : 3
     3: + : op! [value1 value2]
     4: num : 3
    --> +
    <-- + == 6
<-- foreach == 6
 9: trace : native! [mode /back]
10: off : false
--> trace
== 2

Tracing functions only

With the /function refinement you can trace just function calls and their returns. The evaluation of each code block value is not shown, saving a few lines.

>> trace/function on
>> ave [1 2 3]
    --> ave [1 2 3] . .
        --> foreach num [1 2 3] [val: val + num]
            --> + 0 1
        <-- + == 1
            --> + 1 2
        <-- + == 3
            --> + 3 3
        <-- + == 6
    <-- foreach == 6
        --> length? [1 2 3]
    <-- length? == 3
        --> / 6 3
    <-- / == 2
<-- ave == 2

In this mode, the function call lines will show the arguments passed to the functions. (A dot is used to show NONE value slots, such as those for unused refinements or local variables.)

Backtrace

At times it is important to know what your code was doing immediately before a crash. In such cases, you don't want to see trace output until after the crash. That is the purpose of the /back refinement: to tell trace to redirect its output to an internal buffer that you can examine later.

To enable backtrace:

>> trace/back on

Then, run your code. When your crash occurs, type:

>> trace/back 20

to see the last 20 lines (or however many lines you want to see.)

You can also modify your trace depth as you would normally. For example:

>> trace/back on
>> trace 5

will only trace down five levels of code.

When you are done with the backtrace, you can disable it with:

>> trace/back off

and that will also free memory used by the backtrace buffer.

To use backtrace with the /function refinement:

>> trace/back/function on

This will also speed-up trace evaluation.

Example backtrace

Here is an example session:

>> trace/back on
>> test: func [a] [if integer? a [loop a [bug]]]
>> test 10
** Script error: bug has no value
** Where: loop if test
** Near: loop a [bug]

>> trace/back 10
    --> if
         1: loop : native! [count block]
         2: a : 10
         3: [bug]
        --> loop
             1: bug : unset!
            **: error : Script no-value
 1: trace/back
 2: 20
--> trace

So, it's not hard to see what was going on when the script crashed. Backtrace can be quite handy when you need it.

Important notes

The stack function can also be used to show stack related backtrace information.


  TOC < Back Next > REBOL.com - WIP Wiki Feedback Admin