Debugging
Contents
Overview
XMF provides a range of debugging functionality. There is essentially two modes of debugging: command line debugging and Eclipse IDE-debugging. The IDe debugging integrates XMF with the Eclipse debugging framework and allows you to set breakpoints in XOCL code and to step through an application (including the XMF system itself). The IDE-debugging environment for XMF on Eclipse is describes elsewhere. This document describes command line debugging facilities in XMF.To top.
Tracing Operation Calls
Operation calls (including message passing) can be traced using the following operations:// Print information about an operation each time it
// is called. If more than one operation is traced
// then indentation is used to show the nesting of
// the calls...
Operation::trace()
// Suppress tracing output for an operation...
Operation::untrace()
// Trace all the operations defined in the name-space...
NameSpace::traceAll()
// Untrace all the operations defined in the name-space...
NameSpace::untraceAll()
context Root
@Package MyOps
@Operation test1(n:Integer)
if n > 0
then test2(n-1)
else n
end
end
@Operation test2(n:Integer)
if n > 0
then test1(n-1)
else n
end
end
end
MyOps::test1.trace()
MyOps::test1(10);
Enter null.test1(10)
Enter null.test1(8)
Enter null.test1(6)
Enter null.test1(4)
Enter null.test1(2)
Enter null.test1(0)
Exit null.test1 = 0
Exit null.test1 = 0
Exit null.test1 = 0
Exit null.test1 = 0
Exit null.test1 = 0
Exit null.test1 = 0
MyOps::test2.trace()
Enter null.test1(10)
Enter null.test2(9)
Enter null.test1(8)
Enter null.test2(7)
Enter null.test1(6)
Enter null.test2(5)
Enter null.test1(4)
Enter null.test2(3)
Enter null.test1(2)
Enter null.test2(1)
Enter null.test1(0)
Exit null.test1 = 0
Exit null.test2 = 0
Exit null.test1 = 0
Exit null.test2 = 0
Exit null.test1 = 0
Exit null.test2 = 0
Exit null.test1 = 0
Exit null.test2 = 0
Exit null.test1 = 0
Exit null.test2 = 0
Exit null.test1 = 0
MyOps.traceAll()
MyOps.untraceAll()
OCL.traceAll()
[1] XMF> if 10 > 20 then Class.toString() else Object.toString() end;
Enter If(BinExp(IntExp(10),>,IntExp(20)),Send(Var(Class,2),toString,Seq{}),Send(Var(Object,2),toString,Seq{})).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Enter BinExp(IntExp(10),>,IntExp(20)).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Enter IntExp(20).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Exit IntExp(20).eval = 20
Enter IntExp(10).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Exit IntExp(10).eval = 10
Exit BinExp(IntExp(10),>,IntExp(20)).eval = false
Enter Send(Var(Object,2),toString,Seq{}).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Enter Var(Object,2).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Exit Var(Object,2).eval = <Class Object>
Exit Send(Var(Object,2),toString,Seq{}).eval = <Class Object>
Exit If(BinExp(IntExp(10),>,IntExp(20)),Send(Var(Class,2),toString,Seq{}),Send(Var(Object,2),toString,Seq{})).eval = <Class Object>
<Class Object>
[1] XMF>
[1] XMF> let x = 10 in x + 1 end;
Enter Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Enter ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10)).desugar()
Exit ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10)).desugar = ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))
Enter IntExp(10).eval(Loop(),[],Seq{<Package XCore>,<NameSpace Root>})
Exit IntExp(10).eval = 10
Enter Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).dropDeclarations(BinExp(Var(x,2),+,IntExp(1)))
Enter Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).isDeclaration(BinExp(Var(x,2),+,IntExp(1)))
Exit Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).isDeclaration = false
Exit Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).dropDeclarations = BinExp(Var(x,2),+,IntExp(1))
Enter BinExp(Var(x,2),+,IntExp(1)).eval(Loop(),[x=10],Seq{<Package XCore>,<NameSpace Root>})
Enter IntExp(1).eval(Loop(),[x=10],Seq{<Package XCore>,<NameSpace Root>})
Exit IntExp(1).eval = 1
Enter Var(x,2).eval(Loop(),[x=10],Seq{<Package XCore>,<NameSpace Root>})
Exit Var(x,2).eval = 10
Exit BinExp(Var(x,2),+,IntExp(1)).eval = 11
Exit Let(Seq{ValueBinding(x,NamedType(Seq{XCore,Element}),IntExp(10))},BinExp(Var(x,2),+,IntExp(1))).eval = 11
11
[1] XMF>
Exception Backtraces
When exceptions are created (usually at the same point at which they are thrown) the exception captures the current execution state which can be subsequently displayed in order to provide information that is useful for debugging. Here is a simple example of how this works. Given the following definition:context Root
@Operation throwError(n:Integer)
if n = 0
then throw Error("Reached 0")
else somethingElse(throwError(n-1))
end
end
[1] XMF> throwError(10);
Exception: Reached 0
Source file position: line = 0
Error(Reached 0)
The following backtrace shows recent message calls:
Root::throwError(0) aborted at line = 116
Root::throwError(1) aborted at line = 115
Root::throwError(2) aborted at line = 115
Root::throwError(3) aborted at line = 115
Root::throwError(4) aborted at line = 115
Root::throwError(5) aborted at line = 115
Root::throwError(6) aborted at line = 115
Root::throwError(7) aborted at line = 115
Root::throwError(8) aborted at line = 115
Root::throwError(9) aborted at line = 115
Root::throwError(10) aborted at line = 115
Loop::readEvalPrint(Engine(),[1] XMF> ,Pair[
left = Binding[name = return,value = <Op return>],
right = Pair[
left = Binding[
name = <a String>,
value = <a CompiledOperation>],
right = NullEnv[]]]) aborted at line = 182
Loop::anonymous() aborted at line = 121
Loop::loop() aborted at line = 72
?::anonymous(<Op anonymous>) aborted at line = 72
?::anonymous(Seq{Seq{...}})
[1] XMF>
Exception: Reached 0
Source file position: line = 0
Error(Reached 0)
The following backtrace shows recent message calls:
Root::throwError(n) aborted at line = 116
Argument values:
n = 0
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 1
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 2
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 3
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 4
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 5
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 6
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 7
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 8
Target of message:
self = null
----------------------------------
Root::throwError(n) aborted at line = 115
Argument values:
n = 9
Target of message:
self = null
----------------------------------
...more frames (increase Exception::backtraceLimit).
true
[1] XMF>
If you create your own exception types, then you should be careful to initialize the exception correctly via the constructor. The operation setBacktrace should be called in order to capture the current call-stack state. Here is an example of a simple exception definition:
context Exceptions
@Class FileNotFound extends Exception
@Attribute path : String end
@Constructor(path) !
self.setBacktrace();
self.message := "The file " + path + " cannot be found."
end
end
Type Checking
XMF is dynamically typed which means that it checks types just before it attempts to perform a VM operation. By default, type declarations for arguments, local variables and return values have no effect (although they can make the code more readable). The compiler has a switch:Compiler::checkTypes
?o checkTypes true
context Root
@Operation typeCheck(b:Boolean,s:Seq(Integer)):Boolean
let n:Integer = s->iterate(n i = 0 | i + n)
in b or n > 10
end
end
[1] XMF> typeCheck(10,Seq{1,2,3});
Exception: The arg b of operation typeCheck1 expects a value of type Root::XCore::Boolean got 10 of type Root::XCore::I
Source file position: line = 0
ArgTypeError(typeCheck1,b,10,<DataType Boolean>)
The following backtrace shows recent message calls:
...
[1] XMF> typeCheck1(true,Seq{"a","b","c"});
Exception: The arg s of operation typeCheck1 expects a value of type Seq(Integer) got Seq{a,b,c} of type Root::XCore::S
Source file position: line = 0
ArgTypeError(typeCheck1,s,Seq{a,b,c},<Seq Seq(Integer)>)
The following backtrace shows recent message calls:
...
context Root
@Operation typeCheck(b:Boolean,s:Seq(Integer)):Boolean
let n:Integer = s->iterate(n i = 0 | i + n) > 10
in b or n > 10
end
end
[1] XMF> typeCheck2(true,Seq{1,2,3});
Exception: The local n expects a value of type Root::XCore::Integer got false of type Root::XCore::Boolean
Source file position: line = 0
LocalTypeError(n,false,<DataType Integer>)
The following backtrace shows recent message calls:
...
context Root
@Operation typeCheck3(b:Boolean,s:Seq(Integer)):Integer
let n:Integer = s->iterate(n i = 0 | i + n)
in b or n > 10
end
end
[1] XMF> typeCheck3(true,Seq{1,2,3});
Exception: Operation typeCheck3 expects to return a value of type Root::XCore::Integer but returned true of type Root::
Source file position: line = 0
ResultTypeError(typeCheck3,true,<DataType Integer>)
The following backtrace shows recent message calls:
...