Debugging Pliant programs

Understanding errors report

Here is a sample error report content:

  ----------------------------------------------------------------
  Cannot copy a Stream
  ----------------------------------------------------------------
  Actions stack is:
  pml connection from loopback
  ----------------------------------------------------------------
  Processor stack content is:
  error_notify_fatal (ErrorID Str) +14
  error_report +61
  error_notify (ErrorID Address Str) +85
  error  /pliant/language/debug/error1.pli (internals) 58 1
  copy  /pliant/language/stream/stream.pli (internals) 630 1 /pliant/language/stream/stream.pli (internals) 632 3
  . copy_instance (Type Address Address) +69
  copy Universal (Universal Universal Type) +20
  frozen expression at /pliant/appli/file.ui (internals) 335 1  /pliant/appli/file.ui (internals) 335 1 /pliant/appli/file.ui (internals) 348 17
  pliant ui server instruction function  /pliant/graphic/ui/server/instructions.pli (internals) 387 1 /pliant/graphic/ui/server/instructions.pli (internals) 392 9
  ui_service  /pliant/graphic/ui/server/main.pli (internals) 28 1 /pliant/graphic/ui/server/main.pli (internals) 69 5
  answer_request  /pliant/util/pml/multiplexer.pli (internals) 63 1 /pliant/util/pml/multiplexer.pli (internals) 88 7
  frozen expression at /pliant/util/pml/multiplexer.pli (internals) 124 1  /pliant/util/pml/multiplexer.pli (internals) 124 1 /pliant/util/pml/multiplexer.pli (internals) 127 46
  ----------------------------------------------------------------

 • 

The first section is providing the error message.

 • 

The 'Actions stack' is listing the messages provided through 'part' instruction (when used with two arguments), one per line from the innermost one to the outermost one.

 • 

The 'Processor stack' section is listing all functions calls leading to the error, one per line from the innermost one to the outermost one.

Error messages

Most error messages are the argument provided to 'error' instruction (see 'Errors handling' paragraph bellow).

Now, if you use Pliant (under Linux) intensively, you will soon discover the two following messages:

Exception 11

means the process accessed an memory address that maps no memory.
This is the worse scenario because the real error might have append much earlier, silently corrupting the memory layout, and the program might have run quite some time since so that finding the initial error might be challenging.

Exception 15

The Pliant process received a SIGTERM signal. It means that another Linux process killed the Pliant one.

Processor stack content

error_notify_fatal (ErrorID Str) +14

In this first sample, the exact position in the code is not known because 'error_notify_fatal' is part of the Pliant bootstrap code written in C. What we have between braces is the type of the arguments of the function, so that we can decide which one it was when several functions share the same identifier. Then the +14 at the end of the line is the offset in bytes in the processor native binary code between the beginning and the current execution point in the function, so it's not very helpful.

On the other hand, if the function has been defined in Pliant, the report will look like:

ui_service  /pliant/graphic/ui/server/main.pli (internals) 69 5

At the beginning of the line, there is the name of the function, then come the position of the code executing in the function when the error occurred. Position is provided as the module name, followed by line and column number.
If you find several positions on a single line (I will probably remove this in a future release), the first one is specifying the start position of the function in the code, so you are probably more interested with the last position on the line.

Errors handling

Raising an error

Raising an error is the the easiest part:

error "There is a bug here"

Alternatively, and error ID can also be provided:

error error_id_unexpected "There is a bug here"

The error ID is intended to ease errors filtering. Basic error ids are defined in /pliant/language/debug/error1.pli

error error_id_unexpected null "There is a fatal bug here" (null map List)

This is the rarely used most complete version that also enables to provide a list of action records.
The second parameter, here 'null' must be null or the address of a valid Pliant object and is intended to provide extra informations beyond the simple error id that the error handler might use to decide what to do.
Actions records are what Pliant uses to track what program is doing at any time. See 'part' with two arguments in the controls article.
The list last parameter can absent (it's address is null), but if it exists, all the objects it contains must have type 'ActionRecord'.

error_fatal error_id_unexpected null "There is a fatal bug here" (null map List)

Raises a fatal error that cannot be catched by 'safe' instruction bellow.

Catching an error

Catching the error is performed using 'safe' control (this is try/catch in C++):

safe
  ...
sucess
  ...
failure
  ...

and a more complete sample:

constant my_error_id error_allocate_id
...
safe
  ...
failure err
  console "error id is " err:id eol
  if err:attached<>null
    console "an object with type " (entry_type err:attached):name " has been provided" eol
  console "error message is " err:message eol
  console "error message with actions stack report is " error:extended_message eol
  if err:id<>my_error_id
    error err # propagate the error

'success' and 'failure' blocs are optional.
If the 'failure' instruction is provided an identifier parameter, it will be a variable with type 'ErrorHandler' while the failure bloc is executed.

Testing pending error

Now, the important thing to notice about Pliant errors handling is that when an error is raised by 'error' instruction, the control is not automatically and immediately transfered to the 'failure' bloc.

So, in a function, if you want to escape the function when there is a pending error, you need to include a bloc like this:

some_function_that_could_raise_an_error some_parameter
if iserror
  return

This is not very compact, so '?' has been introduced to make it shorter:

some_function_that_could_raise_an_error some_parameter ?

And it is still equivalent to the following:

some_function_that_could_raise_an_error some_parameter ? return

Basically, when '?' has no right part, it's meaning is return if there is a pending error.
If '?' has a right part, it's meaning is execute the right part if there is a pending error.

The reason for '?' with code on the right is to enable something like:

var Sem s
...
s request
...
some_function_that_could_raise_an_error some_parameter ? s release ; return
...

Please notice that the priority between '?' and ';' has been changed in release 106, so the example will work only with release 106 and above.

'iserror' is a boolean specifying if there is a pending error. 'current_error_id' is the ID associated with the pending error and 'current_error_message' is the associated message.

'shy' is defining a bloc that will automatically be escaped as soon as an error is raised. As a result:

shy
  some_function_that_could_raise_an_error some_parameter
  another_function
  ...

is the same as:

part shy_bloc
  some_function_that_could_raise_an_error some_parameter
  if iserror
    leave shy_bloc
  another_function
  if iserror
    leave shy_bloc
  ...

Please notice that Pliant current implementation for 'iserror' and '?' is slow. 'shy' is even worse.

Debugging a Pliant application

When your application is going to production, if you create a file:/log/ directory, all crashes will be logged to file:/log/pliant.log, at least on a Fullpliant system.