In practice it is hard to write a correct MAL program the first time around. Instead, it is more often constructed by trial-and-error. As long as there are syntax and semantic errors the MAL compiler provides a sufficient handle to proceed. Once it passes the compiler we have to resort to a debugger to assess its behavior.
Note, the MAL debugger described here can be used in conjunction with the textual interface client mclient only. The ODBC and JDBC API's do not permit passing through information that 'violates' the protocol.
A powerful mechanism for debugging a program is to set breakpoints during the debugging session. The breakpoints are designated by a target variable name, a [module.]function name, or a MAL line number (#).
The snippet below illustrates the reaction to set a break point on assignment to variable 'i'.
mal>mdb.start();
#end main;
mdb>
mal>user.test(1);
# user.test(1);
mdb>break i
breakpoint on 'i' not set
mdb>n
# io.print(i);
mdb>break i
mdb>c
[ 1 ]
# i := calc.*(i,2);
mdb>
The breakpoints remain in effect over multiple function calls. They can be removed with the delete statement. A list of all remaining breakpoints is obtained with breakpoints.
The interpreter can be instructed to call the debugger as soon as an exception is raised.
Simply add the instruction mdb.setCatch(true)
.
To ease debugging and performance monitoring, the MAL interpreter comes with a gdb-like debugger. An illustrative session elicits the functionality offered.
mal>function test(i:int):str;
mal> io.print(i);
mal> i:= i*2;
mal> b:= bat.new(:int);
mal> bat.append(b,i);
mal> io.print(b);
mal> return test:= "ok";
mal>end test;
mal>user.test(1);
[ 1 ]
#-----------------#
# h t # name
# void int # type
#-----------------#
[ 0@0, 2 ]
The debugger can be entered at any time using the call mdb.start()
.
An overview of the available commands is readily available via help command.
mal>mdb.start();
#mdb !end main;
mdb>help
next -- Advance to next statement
continue -- Continue program being debugged
catch -- Catch the next exception
break [<var>] -- set breakpoint on current instruction or <var>
delete [<var>] -- remove break/trace point <var>
debug <int> -- set kernel debugging mask
step -- advance to next MAL instruction
module -- display a module signatures
atom -- show atom list
finish -- finish current call
exit -- terminate executionr
quit -- turn off debugging
list <obj> -- list current program block
list # [+#],-# -- list current program block slice
List <obj> [#] -- list with type information[slice]
list [#] <obj> -- list program block after optimizer <#>
List # [+#],-# -- list current program block slice
var <obj> -- print symbol table for module
optimizer <obj> -- display optimizer steps
print <var> -- display value of a variable
print <var> <cnt>[<first>] -- display BAT chunk
info <var> -- display bat variable properties
run -- restart current procedure
where -- print stack trace
down -- go down the stack
up -- go up the stack
trace <var> -- trace assignment to variables
trap <mod>.<fcn> -- catch MAL function call in console
help -- this message
mdb>
The term is an abbreviation for a MAL operation ., optionally extended with a version number, i.e. []. The var denotes a variable in the current stack frame. Debugger commands may be abbreviated.
We walk our way through a debugging session, highlighting the effects of the debugger commands.
The call to mdb.start()
has been encapsulated in a complete MAL function, as shown by issuing the list command.
A more detailed listing shows the binding to the C-routine and the result of type resolution.
mal>mdb.start();
#end main;
mdb>l
function user.main():void;
mdb.start();
end user.main;
mdb>L
function user.main():int; # 0 (main:int)
mdb.start(); # 1 MDBstart (_1:void)
end user.main; # 2
The user module is the default place for function defined at the console. The modules loaded can be shown typeing the command 'module' (or 'm' for short). The function signatures become visible using the module and optionally the function name.
mdb>m alarm
#command alarm.ctime():str address ALARMctime;
#command alarm.epoch():int address ALARMepoch;
#command alarm.sleep(secs:int):void address ALARMsleep;
#command alarm.time():int address ALARMtime;
#command alarm.usec():lng address ALARMusec;
mdb>m alarm.sleep
#command alarm.sleep(secs:int):void address ALARMsleep;
mdb>
The debugger mode is left with a . Any subsequent MAL instruction re-activates the debugger to await for commands. The default operation is to step through the execution using the 'next' ('n') or 'step' ('s) commands, as shown below.
mal>user.test(1);
# user.test(1);
mdb>n
# io.print(i=1);
mdb>
[ 1 ]
# i := calc.*(i=1,2:int);
mdb>
# b=nil:bat[:int] := bat.new(0:int);
mdb>
The last instruction shown is next to be executed. The result can be shown using a print statement, which contains the location of the variable on the stack frame, its name, its value and type. The complete stack frame becomes visible with 'values' ('v') command:
# bat.insert(b=<tmp_2>[0],i=2);
mdb>
# io.print(b=<tmp_2>[1]);
mdb>v
#Stack for 'test' size=32 top=11
#[0] test = nil:str
#[1] i = 4:int
#[2] _2 = 0:int unused
#[3] _3 = 2:int constant
#[4] b = <tmp_1226>:bat[:int,:int] count=1 lrefs=1 refs=0
#[5] _5 = 0:int type variable
#[6] _6 = nil:bat[:int,:int] unused
#[7] _7 = 1:int constant
#[8] _8 = 0:int unused
#[9] _9 = "ok":str constant
The variables marked 'unused' have been introduced as temporary variables, but which are not referenced in the remainder of the program. It also illustrates basic BAT properties, a complete description of which can be obtained using the 'info' ('i') command. A sample of the BAT content can be printed passing tuple indices, e.g. 'print b 10 10' prints the second batch of ten tuples.
To ease debugging and performance monitoring, the MAL interpreter comes with a gdb-like debugger. An illustrative session elicits the functionality offered.
mal>function test(i:int):str;
mal> io.print(i);
mal> i:= i*2;
mal> b:= bat.new(:int);
mal> bat.append(b,i);
mal> io.print(b);
mal> return test:= "ok";
mal>end test;
mal>user.test(1);
[ 1 ]
#-----------------#
# h t # name
# void int # type
#-----------------#
[ 0@0, 2 ]
The debugger can be entered at any time using the call mdb.start()
.
An overview of the available commands is readily available.
mal>mdb.start();
#mdb !end main;
mdb>help
next -- Advance to next statement
continue -- Continue program being debugged
catch -- Catch the next exception
break [<var>] -- set breakpoint on current instruction or <var>
delete [<var>] -- remove break/trace point <var>
debug <int> -- set kernel debugging mask
step -- advance to next MAL instruction
module -- display a module signatures
atom -- show atom list
finish -- finish current call
exit -- terminate executionr
quit -- turn off debugging
list <obj> -- list current program block
list # [+#],-# -- list current program block slice
List <obj> [#] -- list with type information[slice]
list [#] <obj> -- list program block after optimizer <#>
List # [+#],-# -- list current program block slice
var <obj> -- print symbol table for module
optimizer <obj> -- display optimizer steps
print <var> -- display value of a variable
print <var> <cnt>[<first>] -- display BAT chunk
info <var> -- display bat variable properties
run -- restart current procedure
where -- print stack trace
down -- go down the stack
up -- go up the stack
trace <var> -- trace assignment to variables
trap <mod>.<fcn> -- catch MAL function call in console
help -- this message
mdb>
The term is an abbreviation for a MAL operation ., optionally extended with a version number, i.e. []. The var denotes a variable in the current stack frame. Debugger commands may be abbreviated.
We walk our way through a debugging session, highlighting the effects of the debugger commands.
The call to mdb.start()
has been encapsulated in a complete MAL function, as shown by issuing the list command.
A more detailed listing shows the binding to the C-routine and the result of type resolution.
mal>mdb.start();
#end main;
mdb>l
function user.main():void;
mdb.start();
end user.main;
mdb>L
function user.main():int; # 0 (main:int)
mdb.start(); # 1 MDBstart (_1:void)
end user.main; # 2
The user module is the default place for function defined at the console. The modules loaded can be shown typeing the command 'module' (or 'm' for short). The function signatures become visible using the module and optionally the function name.
mdb>m alarm
#command alarm.ctime():str address ALARMctime;
#command alarm.epoch():int address ALARMepoch;
#command alarm.sleep(secs:int):void address ALARMsleep;
#command alarm.time():int address ALARMtime;
#command alarm.usec():lng address ALARMusec;
mdb>m alarm.sleep
#command alarm.sleep(secs:int):void address ALARMsleep;
mdb>
The debugger mode is left with a . Any subsequent MAL instruction re-activates the debugger to await for commands. The default operation is to step through the execution using the 'next' ('n') or 'step' ('s) commands, as shown below.
mal>user.test(1);
# user.test(1);
mdb>n
# io.print(i=1);
mdb>
[ 1 ]
# i := calc.*(i=1,2:int);
mdb>
# b=nil:bat[:int] := bat.new(0:int);
mdb>
The last instruction shown is next to be executed. The result can be shown using a print statement, which contains the location of the variable on the stack frame, its name, its value and type. The complete stack frame becomes visible with 'values' ('v') command:
# bat.insert(b=<tmp_2>[0],i=2);
mdb>
# io.print(b=<tmp_2>[1]);
mdb>v
#Stack for 'test' size=32 top=11
#[0] test = nil:str
#[1] i = 4:int
#[2] _2 = 0:int unused
#[3] _3 = 2:int constant
#[4] b = <tmp_1226>:bat[:int,:int] count=1 lrefs=1 refs=0
#[5] _5 = 0:int type variable
#[6] _6 = nil:bat[:int,:int] unused
#[7] _7 = 1:int constant
#[8] _8 = 0:int unused
#[9] _9 = "ok":str constant
The variables marked 'unused' have been introduced as temporary variables, but which are not referenced in the remainder of the program. It also illustrates basic BAT properties, a complete description of which can be obtained using the 'info' ('i') command. A sample of the BAT content can be printed passing tuple indices, e.g. 'print b 10 10' prints the second batch of ten tuples.
The debugger commands available for inspection of the program and symbol tables are:
list (List) [<mod>.<fcn>['['<nr>']']]
A listing of the current MAL block, or one designated by the <mod>.<fcn> is produced. The [<nr>] extension provides access to an element in the MAL block history. The alternative name 'List' also produces the type information.
optimizer [<mod>.<fcn>['['<nr>']']]
Gives an overview of the optimizer actions in the history of a SQL query. Intermediate results can be accessed using the list command.
atoms
Lists the atoms currently known
modules [<mod>]
Lists the modules currently known. An optional <mod> argument produces a list of all signatures within the module identified.
dot <mod>.<fcn>['['<nr>']'] [<file>]
A dataflow diagram can be produced using the dot command. It expects a function identifier with an optional history index and produces a file for the Linux program dot, which can produce a nice, multi-page graph to illustrate plan complexity.
Part of the debugger functionality can also be used directly with MAL instructions. The execution trace of a snippet of code can be visualized encapsulation with mdb.setTrace(true) and mdb.setTrace(false). The following snippet shows the effect of patching the test case.
mal> function test(i:int):str;
mal> mdb.setTrace(true);
mal> io.print(i);
mal> i:= i*2;
mal> b:= bat.new(:oid,:int);
mal> bat.insert(b,0@0,i);
mal> io.print(b);
mal> mdb.setTrace(false);
mal> return test:= "ok";
mal> end test;
mal> user.test(1);
# io.print(i=1);
[ 1 ]
# i := calc.*(i=1,2);
# b := bat.new(:oid,:int);
# bat.insert(b=<tmp_1001>[0],0@0,i=2);
# io.print(b=<tmp_1001>[1]);
#-----------------#
# h t # name
# void int # type
#-----------------#
[ 0@0, 2 ]
mal>
It is also possible to activate the debugger from within a program using mdb.start()
.
It remains in this mode until you either issue a quit command, or the command mdb.stop()
instruction
is encountered. The debugger is only activated when the user can direct its execution from the client interface.
Otherwise, there is no proper input channel and the debugger will run in trace mode.
The program listing functionality of the debugger is also captured in the MAL debugger module.
The current code block can be listed using mdb.list()
and mdb.List()
.
An arbitrary code block can be shown with mdb.list(_module_,_function_)
and mdb.List(_module_,_function_)
.
A BAT representation of the current function is return by mdb.getDefinition().
The symbol table and stack content, if available, can be shown with the operations mdb.var()
and
mdb.list(_module_,_function_)
. Access to the stack frames may be helpful in the context of exception handling.
The operation mdb.getStackDepth()
gives the depth and individual elements can be accessed as BATs using
mdb.getStackFrame(_n_)
. The top stack frame is accessed using mdb.getStackFrame()
.