The VM DLL contains a trace module which is shared among all the instantiated VMs.
The trace module is able to store a list of events which at some point can be written to a file, and this file can then be loaded by the debugger as a list of trace events.
The list of trace events is generated by special trace instructions which are compiled into the bytecode - making the bytecode much larger. It means that bytecode compiled without these special commands can't produce any trace information at all.
This is a security feature which ensures a cracker can't make a change to the DLL to generate trace events.
Most trace events can be generated automatically without having to do any extra programming. Just open the project properties to set the check marks for including the trace commands in the compilation. This can automatically trace the code flow and explicit value assignments based on these := += -= *= /= assignment operators.
However, some statements produce results which are not traced automatically - e.g. indirect assignments using self, and in these cases it can be relevant to manually insert specific trace commands in the source code. These commands are included in the trace group of library functions.
i := 10; //This assignment can be traced automatically.
//The next statement is not traced automatically.
//We decide to do the trace manually using @Trace.
i.ParseString("99").Inc(); #0 @Trace(i.ToString());
//NB! Notice the #0 group line for the trace command
// which provides an easy on/off switch.
As demonstrated here, when using trace functions in the source code it is advisable to prefix these statements with a group line number. It makes it much easier later to eliminate them from the code simply by switching them off, once testing with trace information is done and the bytecode is to be compiled for release without any trace instructions included.
All instantiated VMs insert their events in the trace module in a sequential order of occurrence. This design works well for single thread testing and makes it easy for the debugger to present the flow of events across different projects - e.g. when one VM calls another VM to execute some other bytecode. The trace module is not intended to trace VMs working in parallel.
Controlling the Trace.
While executing your bytecode some loops or functions may produce many irrelevant events which you decide to ignore, and these trace functions can be inserted into your source code to allow for switching certain trace options on/off at runtime.
You can also change the default properties for storing the traced values - controlling the length of traced values to ensure large strings or blobs don't consume to much memory, and in case the list reaches a specified maximum number of events (runs full) a property controls whether to ignore new events or insert new events by dumping the first.
The stored trace events are finally saved to a text file using the library function @TraceWriteFile which also clears the trace module list.