Sunday, October 10, 2010

Interpreting error output

When you run a PEBL script, the compiler records a bunch of logging messages (mostly at initialization) that can help you understand different initialization problems when they occur.  Also, when an error occurs during execution, messages will usually be displayed that can help you figure out the problems.

In windows or OSX, these display messages will appear in the terminal screen.  On windows, they get saved to two text files: stdout.txt and stderr.txt.  First, we'll start with stderr.txt.  I'm showing one created on linux, but it should be basically the same on other platforms

Stderr and stderr.txt

First, it should tell you where the executable it is using is:

Executable file located at: [/usr/local/bin/pebl].
Next, it will give you a list of the path. These are all the locations it searches, in order, when you tell it to load a file, font, image, etc.  It looks in the current directory first, but then looks in a number of other reasonable places.

Path List:

Next, PEBL reads in the .pbl file (or files) specified on the command line, parses all the functions, and reads them into a binary tree structure that it can later execute.  If you specify multiple .pbl files, it will read and parse them all, which allows you to have a library as well as a base experiment.  You can only have one Start function though.
Also, note that it always reads in files from PEBL's function library at this point.  These are PEBL functions that are written in PEBL. 
Loading filename:test1.pbl
Processing PEBL Source File1: ./test1.pbl
File [./test1.pbl] opened successfully.
Loading file name:Design.pbl
Processing PEBL Source File2: /usr/local/share/pebl/pebl-lib/Design.pbl
File [/usr/local/share/pebl/pebl-lib/Design.pbl] opened successfully.
Loading file name:Utility.pbl
Processing PEBL Source File2: /usr/local/share/pebl/pebl-lib/Utility.pbl
File [/usr/local/share/pebl/pebl-lib/Utility.pbl] opened successfully.
Loading file name:Math.pbl
Processing PEBL Source File2: /usr/local/share/pebl/pebl-lib/Math.pbl
File [/usr/local/share/pebl/pebl-lib/Math.pbl] opened successfully.
Loading file name:Graphics.pbl
Processing PEBL Source File2: /usr/local/share/pebl/pebl-lib/Graphics.pbl
File [/usr/local/share/pebl/pebl-lib/Graphics.pbl] opened successfully.
Now that all the functions had been read,and parsed, it puts each function name into a lookup table, which allows you to access a particular piece of code.  What happens if you define a function in an experiment that already exists elsewhere?  It uses your definition, instead of the original. Actually, since it reads your experiment file first, it ignores later definitions (which may have occurred in a built-in function), but this is sort of a minor point.  Note that below, the Dist() function is redefined in an experiment, but it exists in one of the utility functions, maybe Math.pbl.  This could get you into trouble if you define a function that is used by other built-in function (such as dist()), but define it in a different way.  The other functions may expect Dist to operate in a way that it no longer does.  This can be useful in some situations as well.
---------Loading Program---------
Error in function 'DIST'.  Function already exists.
Analyzing code for functions.
Loading Library functions.
Removing residual function tree

Next, PEBL prints out a list of all the functions 'used' in the program.  This list is a little deceptive.  There are really two types of functions in PEBL: built in compiled functiosn (listed below as PEBL_LIBRARYFUNCTION) and  ones defined within pebl code using the define keyword (listed as PEBL_LAMBDAFUNCTION).  These lambda functions could come from your own experiment, or from one of the built-in library functions.  PEBL does an analysis and pulls only the library functions it uses into the 'function map' (a lookup table that helps it locate a piece of code based on its function name), but it puts all of the lambda functions there, regardless of whether they get used or not.  It would be cleaner to parse out just the ones that are used, but this is not a trivial exercise, and the overhead of parsing and storing these functions is so small that it hardly matters.  But some day, I may trim the function map to only those used at run-time.
Note that the list is trimmed down from the original output:
Functions used in program:
    Function Map:
Function Name:  [ACOS::]
Function Name:  [ADDOBJECT::]
Function Name:  [APPEND::]
Function Name:  [ATAN::]
Function Name:  [BLOCKE::]
Function Name:  [CEILING::]
Function Name:  [CHOOSEN::]
Function Name:  [CONVERTIPSTRING::]
Function Name:  [CONVEXHULL::]
Function Name:  [COS::]
Function Name:  [CR::]

Function Name:  [YESNOTRIAL::]
Function Name:  [ZEROPAD::]
Function Name:  [ZOOMPOINTS::]
To improve performance on Linux and probably OSX, you may chose to run PEBL at a higher priority. This is called its 'nice' level, and I think lower negative numbers mean a higher priority.  PEBL reports its priority here:

Process running at a nice value of 0
It will next load some initial libraries for displaying windows, etc.

Successfully initialized SDL Graphics
Using audio driver: pulse
Now, to program has been parsed, and the variables and functions set up. The only thing left is to create an 'evaluator': the system that executes the parsed code tree.   PEBL reports when this evaluator has been created, and once the program starts.  Note that if you don't see these lines and PEBL does not work, that means you either have a misconfigured installation in some way, or a syntax error in your script.  PEBL has not started executing anything yet.  A syntax error

---------Creating Evaluator-----
---------Evaluating Program-----

At this point, all the pre-loading has been done, and the PEBL program will begin.  When you use the MakeWindow() function, some initialization of the display libraries is needed, so it reports details of this as well:

Hardware surfaces NOT available.
 Current Video Mode:
hw_available   Is it possible to create hardware surfaces?:           [0]
wm_available   Is there a window manager available:                   [1]
blit_hw        Are hardware to hardware blits accelerated?:           [0]
blit_hw_CC     Are hardware to hardware colorkey blits accelerated?:  [0]
blit_hw_A      Are hardware to hardware alpha blits accelerated?:     [0]
blit_sw        Are software to hardware blits accelerated?:           [0]
blit_sw_CC     Are software to hardware colorkey blits accelerated?:  [0]
blit_sw_A      Are software to hardware alpha blits accelerated?:     [0]
blit_fill      Are color fills accelerated?:                          [0]
video_mem      Total amount of video memory in Kilobytes:             [0]
Display Mode:  Width  (pixels)                                        [800]
Display Mode:  Height (pixels)                                        [600]
Display Mode:  Color Depth (bits)                                     [16]
---------------------------------- PIXEL FORMAT---------------------------------
SDL_Palette *palette (0 for realcolor):     [0]
Using Video Driver: x11
Surface refcount: 1
This will give details of drivers, screen resolutions, pixel depths, etc.   If you don't see this output in  stdout, you either didn't call MakeWindow(), or the program crashed before it got to that call.

A similar line will be printed if you use auditory functions like Play()

stdout and Stdout.txt
 Generally, nothing will be printed to stdout.txt unless you do it explicitly.  You can use stdout.txt to help debug a script you are writing, or to help understand a script you are modifying, using the Print(), Print_(), and PrintProperties() functions.  You can Print() just about any variable, and it will print out a message about what its value is.  For complex widgets, you can get a listing of all of its properties using the PrintProperties() function.  This can also help you localize the place where an error occurs, and help you log the values of different variable at different times in the program.  Also, you can use it to output data and data summaries, although a better option is the FilePrint function which lets you save that to a particular file.

So, consider this little program:
define Start(p)

   win <- MakeWindow()
   lab <- EasyLabel("Hello world",100,100,win,22)
   Print_("This is the label: ")


Below is what it prints out to stdout.  Print_() function prints without a line break; Print() prints with a line break.

This is the label: >
 Hello world
[HEIGHT]: 27
Common errors:

Usually, if you make an error, PEBL will tell you in stderr where the error is.  For example if you use an undefined variable

Error near line 5 of file ./testa.pbl:
    Trying to use an undefined variable:  WIGN
Exiting PEBL because of captured signal.

When you call a function, and the error happens deep within the function, you often need to backtrack the steps.  When such an error happens, PEBL will report the complete call stack. Consider the following program, which has an error because F2 calls Mean which requires a list:

define Start(p)
define F1(a)
   return F2(a)
define F2(a)
  return Mean(a)

The error output shows you that although error actually happened within the PEBL function Mean (which checks to see if you gave it a list), you may need to trace back to line 14, 8, and 3 of your script to identify
where the actual problem came from.

Called from function [] on line [3] of file [./testa.pbl]
   Called from function [] on line [8] of file [./testa.pbl]
      Called from function [] on line [14] of file [./testa.pbl]

Error near line 55 of file /usr/local/share/pebl/pebl-lib/Math.pbl:
    Argument of function [Mean()] is not a list
Exiting PEBL because of captured signal.

Here is a common error that can be hard to figure out.  The Quote symbol " is what is used to delineate text.  The Quote symbol allows line breaks in the quoted text.  If you forget to close a quote symbol, it will treat the entire remainder of the program (until the next quote) as a text string, and then have some trouble at the end of the program.  Consider the following error:

Processing PEBL Source File1: ./testa.pbl
File [./testa.pbl] opened successfully.
*** buffer overflow detected ***: pebl terminated
======= Backtrace: =========

The error reported may differ on different platforms, but this was caused by just such a situation.  What did the program look like?

define Start(p)
   Print("hello world)

Because of the way the parser works, it doesn't even know where the error is.  But change the program a little bit, and you get a different error
define Start(p)
   Print("hello world)
   Print("goodbye world")
Now, the first line still misses a quote, but now more quotes appear in the next line.

Executable file located at: [/usr/local/bin/pebl].
Path List:
Loading filename:testa.pbl
Processing PEBL Source File1: ./testa.pbl
File [./testa.pbl] opened successfully.
line 4 of ./testa.pbl: syntax error
It now recognizes the problem, but gives you line 4 as the error point.  Shouldn't it know that it is line 3?  That is a pretty difficult problem, because from the point of view of the parser, the error happened on line 4. 

So, if there are any PEBL errors that puzzle you, post them here and I'll do a follow-up trying to debug them.
Post a Comment