Loading

Debugging WarpScript

WarpScript is a stack language. Once you've read the basic concepts, you must know how to debug your scripts.

STOP instruction

This code has a bug. Click on execute button below:

NEWGTS 'myGTS' //create a new gts "myGTS" NOW //timestamp 0.0 //latitude 0.0 //longitude 0 //elevation 42 //value ADDVALUE

In the Warp 10 engine, the ADDVALUE raised an exception, with help text ADDVALUE operates on a single Geo Time Series or GTS Encoder. The error was raised during the execution of line 7 of the script. Why ?

Every WarpScript function expects some parameters on the stack. Look at the ADDVALUE signature. ADDVALUE expects 6 objects on the stack and will push 1 object on a stack after its execution.

gts tick latitude longitude elevation value ADDVALUE result

ADDVALUE will pop the first element of the stack: value. It will check the type. The allowed types are STRING, a BOOLEAN, a LONG or a DOUBLE. Here we put 42. It's a LONG, it should not be a problem.

ADDVALUE will pop the next element of the stack: elevation. It will check the type. The allowed types are LONG or NaN (Not a Number). Here we put 0. It's a LONG, it should not be a problem.

And so on... In this code, there is a signature error, and ADDVALUE expects somewhere a GTS or an ENCODER. The best way to see the stack before execution is to put a STOP before ADDVALUE, and look carefully at the 6 objects on the top of the stack:

NEWGTS 'myGTS' //create a new gts "myGTS" NOW //timestamp 1.2 //latitude 0.5 //longitude 0 //elevation 42 //value STOP ADDVALUE
Stack depthContentTypes Allowed by ADDVALUE
[TOP]42STRING, BOOLEAN, LONG, DOUBLE
10LONG
21.2DOUBLE
30.5DOUBLE
4412260704123842LONG
5"myGTS"GTS

"myGTS" is not a GTS. It is a STRING. The GTS is just below in the stack... The author forget to call the RENAME function. RENAME function pop a string, pop a GTS, and push a GTS on the stack. Here is a possible fix:

NEWGTS 'myGTS' RENAME //create a new gts "myGTS" NOW //timestamp 1.2 //latitude 0.5 //longitude 0 //elevation 42 //value ADDVALUE

LINEON and SECTION instruction

By default, the error has one line and some sections. The line is the line of the script being executed while the exception is raised. If the error is in a macro section called in a foreach loop, the error is on the FOREACH line. Try this code:

//division per zero <% 3 0 / //3 divided by zero... exception ! %> 'macro' STORE [ 4 5 6 ] <% 'i' STORE @macro %> FOREACH
ERROR line #13 in section '[TOP]':
  Exception at statement 'RUN' in section '[TOP]'
    (Exception at statement '/' in section '[TOP]' (/ by zero))
    (Exception at statement '/' in section '[TOP]' (/ by zero))

The error occurred on line #13, two sections below. But the real error is on line 5. The section name is always TOP. To locate the bug, you can use section names:

//division per zero <% 'my macro' SECTION 3 0 / //3 divided by zero... exception ! %> 'macro' STORE [ 4 5 6 ] <% 'i' STORE 'in my foreach' SECTION @macro %> FOREACH
ERROR line #13 in section '[TOP]':
  Exception at statement 'RUN' in section 'in my foreach'
    (Exception at statement '/' in section 'my macro' (/ by zero))
    (Exception at statement '/' in section 'my macro' (/ by zero))

SECTION has very few impact on the performance, and will help you to locate the error.

In case you do not have any sections defined in your code, you can use LINEON. LINEON will add one section per line.

//division per zero LINEON <% 3 0 / //3 divided by zero... exception ! %> 'macro' STORE [ 4 5 6 ] <% 'i' STORE @macro %> FOREACH
ERROR line #13 in section '[Line #9]':
  Exception at statement 'RUN' in section '[Line #12]'
    (Exception at statement '/' in section '[Line #5]' (/ by zero))
    (Exception at statement '/' in section '[Line #5]' (/ by zero))

LINEON is very handy, but it has an impact on performance, use it carefully, and remove it in production.

JSON object representation, TYPEOF and TDESCRIBE

WarpScript output is a JSON. It means Warp 10 serialize objects to output a string representation of each object on the stack whenever it is possible. For example, a GTS is represented as a MAP. But internally, it is not a MAP, it is a GTS object instance.

NEWGTS { 'c' '' 'l' {} 'a' {} 'v' [] }

Both object have the same representation.

Some objects cannot be represented:

( 'string 1' 'string 1' 'string 2' ) // a set 'Московский аэропорт' 'utf-8' ->BYTES // a byte array 100 100 '2D3' PGraphics // an image [ 0 1 0 ] ->VEC //a vector [ [ 1 2 3 4 ] [ 10 20 30 40 ] [ 100 200 300 400 ] ] ->MAT //a matrix NULL //null

null output can be a real NULL (non existing object), or an object Warp 10 cannot represent in JSON. TYPEOF function will help you to know what kind of object is on the top of the stack. This function return a string with the type.

( 'string 1' 'string 1' 'string 2' ) TYPEOF // a set 'Московский аэропорт' 'utf-8' ->BYTES TYPEOF // a byte array 100 100 '2D3' PGraphics TYPEOF // an image [ 0 1 0 ] ->VEC TYPEOF //a vector [ [ 1 2 3 4 ] [ 10 20 30 40 ] [ 100 200 300 400 ] ] ->MAT TYPEOF //a matrix NULL TYPEOF //null NEWGTS TYPEOF //a GTS { 'c' '' 'l' {} 'a' {} 'v' [] } TYPEOF //a MAP

Some functions can output LIST of objects, or LIST of LIST of objects. In your debug environment, you can activate TDESCRIBE extension to get a recursive description of an object.

warpscript.extension.debug=io.warp10.script.ext.debug.DebugWarpScriptExtension
[ [ NEWGTS ] ] TDESCRIBE //returns LIST [ LIST [ GTS ] ] [ { "key" { 2334 [ NEWGTS ] } } ] TDESCRIBE //returns LIST [ MAP { STRING : MAP { LONG : LIST [ GTS ] } } ]

Raise errors in your code

In unit tests or in your code, you raise an exception with ASSERTMSG, ASSERT, FAIL, or MSGFAIL. For example, this script will randomly fail:

42 //pushes 42 on the stack <% RAND 0.5 > %> <% DROP %> IFT //drop 42 randomly DEPTH 0 == 'Stack is not empty here, abnormal behavior 45' ASSERTMSG 'NCC1701' //push this string on the stack

If your code is running in the background (in a scheduled runner for example), you can also write messages and errors to the standard or error output. See DebugWarpScriptExtension, with functions STDERR, STDOUT, LOGMSG.

If you want to go further in alerting, you can do a WEBCALL to Slack or your favorite alerting channel.

Try Catch

Warp 10 has a try/catch/finally control structure. In the catch, you can get the full error stack with ERROR.

//catch the random failure and go on <% 42 //pushes 42 on the stack <% RAND 0.5 > %> <% DROP %> IFT //drop 42 randomly DEPTH 0 == 'Stack is not empty here, abnormal behavior 45' ASSERTMSG %> <% 'There was a failure, here is the message: ' ERROR //get the list of map of exception 0 GET //take the first in the list 'message' GET //take 'message' key in the map + //concatenate with previous string on the stack %> <% 'NCC1701' //push this string on the stack %> TRY
  • STOP allows you to stop execution to check the stack state.
  • LINEON allows you to locate an error.
  • TYPEOF and TDESCRIBE show you the type of an element.
  • TRY is the Warp 10 try/catch/finally control structure.