Debugging Scripts

Top  Previous  Next



TLMDScriptDebugger component is the component that adds true debugging functionality to the LMD-ScriptPack. To enable debugging capabilities for any given script control, just link its Debugger property to the TLMDScriptDebugger component.


The advanced feature of the MS ActiveScripting technology is multi-language multi-module debugging. This feature is fully supported by LMD-ScriptPack. That is, to the same TLMDScriptDebugger component you can connect several TLMDScriptControl components. All they will be associated with the same debugger. These script controls can be initialized with different Language. However, the debugger can't mix Active Scripting languages with natively implemented PasScript and NativeVB.
The script code in one script control can call the routines from another script control. And the TLMDScriptDebugger will still provides all debugging features, including inter-module Step-Into and Step-Over and inter-module call-stack.


The main event of the script debugger is OnBreak event. As expected you will not be able to explore any debugging information from the debugger while script is executing code. All information becomes available only in the script execution break state. The OnBreak event fired when the script enters the break state because of encountered breakpoint, or because of the end of previously initiated StepInto or StepOver operation.


To stop script execution in some particular place you can set the breakpoint by calling SetBreakpoint method. Note that since script debugger support multi-module debugging you should specify in which script control code you setting breakpoint.


In break state you can explore the script call stack. Use StackFrameCount and StackFrames properties to iterate through call stack frames. Each call tack frame denotes executing script procedure. In the context of some call stack frame you can explore current values of the executing procedure local variables by calling TLMDStackFrame.GetVariables method. Note that different stack frames will return different variables. Also in the context of some call stack frame you can evaluate expressions by calling TLMDStackFrame.Eval methods.


To resume script execution call ResumeFromBreak method. Specifying the resume kind in this method call you can continue script execution normally or just perform only one StepOver/StepInto.


Threads and call-stacks


To be more precise, underlaying Debugger engine maintains a single call-stack for each thread in which script is currently executing. That is - one call-stack for one thread. The call-stack contains entries from all script modules executing in the corresponding thread and connected to this debugger. Through TLMDScriptDebugger you can access only call-stack corresponding to the thread in which break script is executing.


Important: Threading Issues and Synch Debugger Mode


With natively implemented PasScript and NativeVB languages it is possible to handle GUI, script execution and script debugging in a single thread - e.g. in the main VCL thread. Setting True to IsSynchBreaks debugger's property will activate a single threaded debugger mode. In this mode the OnBreak debugger event is fired directly from the script execution thread.
This is a great simplification over the Active Scripting languages (in which handing threading issues is not so simple).


In Active Scripting the thread in which script code is executing is suspended while the script stays in break state. This means that with Active Scripting languages debugger GUI should work in different thread from the thread that executes script code. Otherwise the debugger GUI will be broken.

Note also, that Active Scripting engine creates additional threads in which debugger works. These threads are for internal use of the debugging engine. But, OnBreak debugger event is always fired from one of these threads, when Active Scripting language is used, NOT from VCL main thread. Be accurate to provide any required thread related synchronization while writing debugger GUI.


Debug Modules


TLMDScriptDebugger component contains Modules property which represents debug-module collection. You can think about debug module as of abstraction for script source code unit. Since several script control can be connected to the debugger, the debugger need to maintain, for example, which breakpoints corresponds to which script control. Also, when the debugger GUI shows a call-stack it is required somehow to visualize, which item in the call-stack correspond to which script-control. All this is done using debug-modules. A debug-module has a Name, which abstractly denotes the name of the script source (unit) and can be shown in the debugger GUI.


Generally, you should create a single debug-module item for each script control, connected to the debugger. You can create a module manually, at run-time, using Debugger.Modules.Add method and setting its Name property. After debug-module creation you can associate it with a script control using TLMDScriptControl.DebugModule property.


In simple cases you can even omit the manual creation of the debug-module instance, assigning TLMDScriptControl.DebugName property at design-time. The corresponding debug-module will be created in the associated debugger automatically.


However, the notion of the debug-module has been added to LMD-ScriptPack architecture for more complex cases: you can, actually, associate more then one script control with a single debug-module.

What its for? Imagine the IDE that allows to develop some scripting application. Imagine that just like in Delphi, you can add units and forms to the scripting project. Imagine also, that just like in Delphi you can create more then one instance of the designed form at the scripting project run-time. Each of those forms will have each own instance of the script control to handle events, despite the fact that the source code in each of those script controls will be identical. That is, all of those forms (and corresponding script controls) will correspond to a single scripting project unit in our imagined IDE.

So, having a single debug-module for such unit will simplify debugger related things, such as visualizing the unit name in debugger GUI and especially managing breakpoints. Since breakpoints are set in debugger for debug-module, but not for concrete script control, they can be set only ones, independed of real count of the associated script controls, and all associated script controls will react to these breakpoints.


Another reason to have debug-modules notion, is that it allows to defer script controls creation till the scripting-project run-time, while still been able to maintain breakpoint list in the debugger at design-time to allow breakpoints visualization in developing IDE.


So, finally the relations of objects are following: A debugger holds a collection of debug-modules. And each of the debug-module can be associated with one or more script controls.