[Images/tutorial.gif]

A Small Guide to what can go wrong with Khoros programs.

Table of Contents - Index
Previous: Debugging Khoros code

[bar]

Introduction

Here is a small guide to what can go wrong in Khoros programs. I divided the possible errors and solutions (or at least lists of places that could be causing errors) in categories.

Important note: I didn't really try to create all errors that are mentioned here, and I cannot guarantee that the code will really generate an error. The examples in this sections are just basic guidelines on common errors that one can do while programming Khoros routines.

[bar]

My program gives a segmentation fault or similar panicky message.

[bar2]

Did you checked the return values for the functions ?

This is a common error if you write programs in a hurry just to see if they do what you want. It is better to spend a little time and check values returned by functions. For example, doing something like
...
kpds_create_mask(my_obj);
...
could bring you problems, specially if the program don't create a mask and later you try to access the mask data. Doing
...
if (!kpds_create_mask(my_obj))
  {
  kerror("library","routine","cannot create mask in my_obj");
  kexit(KEXIT_FAILURE);	
  }
...
is safer since it checks whether the mask was created or not. Doing
...
if (!kpds_query_mask(my_obj)) /* check if there is a mask */
  {                           /* if not */
  if (!kpds_create_mask(my_obj)) /* try to create it */ 
    {                         /* if can't create error and quit */
    kerror("library","routine","cannot create mask in my_obj");
    kexit(KEXIT_FAILURE);	
    }
  }
...
is even better because it checks for the existence of the mask before attempting to create one.

Note that almost all functions in Khoros has return values that indicate whether the routine was executed correctly or not. Please refer to the routines' kman pages for more information. Check also the next section for more instances of this problem.

[bar2]

Are you allocating the correct amount of memory ?

This is another place where things can go badly wrong. Suppose you want to get a plane of data and allocate it as:

double *plane;
...
plane = (double*)kmalloc(w*h*sizeof(double));
but without checking whether plane is really allocated. You will probably get a nasty error when trying to access the data.

There are several variations on this problem that won't be noticed by the compiler, like declaring a pointer to doubles but calling kmalloc with

plane = (double*)kmalloc(w*h*sizeof(int));
or simply not allocating the memory and trying to access it. A similar error would be create a routine that will process only data with the same size (say, 128x128x1x1x1) and allocate the memory for this kind of data and later try the same routine with data of different sizes.

[bar2]

Did you check if the files were really opened ?

You can open a file for an kobject with:
kobject in_obj;
in_obj = kpds_open_input_object("filename.dat");
...
but if you don't check whether the file was opened or not you could run into problems. I guess that routines that deal with kobjects can say whether the kobject is valid or not but it would be safer to do something like
kobject in_obj = NULL;
in_obj = kpds_open_input_object("filename.dat");
if (in_obj == NULL)
  {
  kerror("my lib","my routine", /* of course you should write meaningful stuff here */
  	 "Cannot open input object.");
  kexit(KEXIT_FAILURE);		
  }

[bar2]

When calling functions with variable parameters are you terminating the list with NULL ?

This is also an error that the compiler won't probably notice. Suppose you want to call kpds_set_attributes, which expect a variable list of parameters. Calling it like
kpds_set_attributes(my_obj,KPDS_VALUE_DATA_TYPE,KUINT,
			   KPDS_VALUE_SIZE,120,120,0,0,3);
will probably get you a nice segmentation fault sooner or later. The correct call should be
kpds_set_attributes(my_obj,KPDS_VALUE_DATA_TYPE,KUINT,
			   KPDS_VALUE_SIZE,120,120,0,0,3,
			   NULL);
All functions with variable attributes must have NULL passed as the last attribute.

[bar2]

Are you creating segments that already exist in the objects ?

I am not sure if Khoros will catch those errors and tell you what went wrong. Anyway if you have an kobject and try to do something like

...
kpds_create_value(my_obj);
...
without checking if the object already has or not a value segment, you could get runtime errors. Doing
...
if (!kpds_query_value(my_obj)) /* if there isn't a value segment */
  {
  if (!kpds_create_value(my_obj)) /* try to create it */
    {
    kerror("library","routine","cannot create value in my_obj");
    kexit(KEXIT_FAILURE);	
    }
  }
...
will check if the segment is there and will create only if it is not there, and will also check whether the segment was created or not.

[bar2]

Are you accessing data that wasn't open/created yet ?

In the code below:

kobject in_obj = NULL; /* input to serve as base */
...
if (clui_info->ibase_flag) /* ibase is optional, check the flag before opening it */
  {
  if ((in_obj = kpds_open_input_object(clui_info->ibase_file)) == KOBJECT_INVALID)
    {
    kerror(lib,rtn,"Cannot open input object %s",clui_info->ibase_file);
    kexit(KEXIT_FAILURE);
    }
  }    
...
if (!kpds_copy_object_attr(in_obj, out_obj))
  {
  kerror(lib,rtn,"Unable to copy attributes from input to output object.");
  kexit(KEXIT_FAILURE);
  }

You will get a segmentation fault, because if the ibase_flag was false, the in_obj was still NULL when you tried to copy it to another object.

[bar]

I get compiling errors or linking errors

[bar2]

Argh ! Zillions of _zg???? errors when linking my toolbox program !

This is a favorite one. Often the problem is with the Matrix Toolbox and its dependency in Fortran libraries. Check the following points:
  1. Check if you have the Matrix Toolbox installed. In case of doubt, reinstall it and check closely the installation results to see if the libraries were really created.
  2. Check the $TOOLBOX/objects/kroutine/MYPROGRAM/src/Imakefile file, see if there is a flag called #define FortranDependency , if it really depends on the Matrix toolbox it must be set to YES.
  3. Check the Khoros FAQ's

[bar]

My program does not work or is not working properly with my data.

[bar2]

It works with some data but not with my data.

Is the data in the expected dimensions and/or data type ?

Some routines will work with specific data dimensions or types. Poorly written routines will not give errors or warnings if the expected dimensions or data types are wrong. Specially problematic are routines that has calls like:

int *plane = NULL;
...
plane = (int)kmalloc(w*h*sizeof(int));
...
kpds_get_data(my_obj,KPDS_DATA_PLANE,(kaddr)plane);
...
This would work if the data is of integer type, but could cause weird errors if the input data is double since the program isn't checking or converting the data prior to accessing it. The program should issue warnings and errors and/or convert the data prior to accessing. See the section Recommendations for code that can be trusted , item 3.

Does the data has more or less segments than the expected ?

Most functions will operate only on the value segment, but some should consider also the mask, location, time and map segments. The program should check whether the required segments are present or not in the input data, and create them or issue warnings and/or errors.

[bar]

I compiled my program but cannot execute it.

[bar2]

Did you check the PATHs ?

Check whether the executable programs are being stored in a directory that is not in your PATH. Also check if the shared libraries are in a place in the LD_LIBRARY_PATH.

[bar2]

I just created my program and it does not appear in Cantata.

If you just created it with craftsman / composer and it does not appear in the glyph list in Cantata, check if you set the "Install in Cantata" option to "Yes" - from craftsman , select a toolbox and the object in the toolbox and click the menu button "Object Operations" and select "Object Attributes", or from composer , select the button "Attributes" and set "Install in Cantata" to "Yes".

Note that if you had Cantata running before you created the object, Cantata will not recognize it, you must quit Cantata and restart it so the object will appear on the glyph list.

[bar]

Real perverse errors

This is a small list of errors that will be really hard to track, even with gdb (see also Using gdb and Khoros ).

[bar2]

Negative indexes for arrays

In the code segment

int *vector = NULL;
int a;
...
vector = (int)kmalloc(e*sizeof(int));
...
a = -1;
...
vector[a] = 0;
...
there is an obvious error - the code is trying to access an element of the vector that does not exist. This error won't be caught by the compiler (nor by gdb). In a code similar to the above, I had a segmentation fault when free'ing the allocated memory - worse, sometimes the error happened and other times it didn't. The only way to avoid the problem was examining the code in detail.

[bar]

Recommendations for code that can be trusted

  1. Comment your code. Try to comment every part of your code that is not clear by itself. That will ensure that if you forget about some parts of it you will find information exactly on the code. I know that it is not as fun as writing the code, but trust me on that, when you're months later trying to figure out what you were thinking when you wrote that is not fun at all.
  2. Check the result of every function call. This is another part that is boring when you're writing programs, but could save you a lot of time if you have problems and cannot figure out where the errors are happening. Checking the function return values whenever possible and issuing error and warning messages will show exactly where possible errors are bound to happen.
  3. Access data with the "highest" possible data type and check input objects data types. If your program is made for processing integer data types only, do the internal processing as long and then convert back to the original data type if desired. That will ensure that your program will work nicely with all kinds of integer types. In this case before processing check whether the data type of the input objects is valid and issue errors or warnings if it is the case. The problem with this approach is the memory requirements, but at least it will ensure compatibility with other data types.
  4. Don't make assumptions on the input files. Or even better, don't limit the kind of data that can be processed unless you have a good reason to do so. The Polymorphic Data Services were designed to make possible the use of several types of data with the functions. If you write an operator that will work on a single plane of an image, some simple alterations will make it work on several planes, animations, etc. See the examples in this pages how it is done. Similarly, even if you are going to process only files of a determined size make the routines flexible so they will allow data of different sizes to be processed. The code won't be that hard to write and will be way more flexible.
  5. Use the versions of the functions in Khoros instead of C functions whenever possible. Instead of using sin, cos, etc. use ksin, kcos. Almost all (or all ?) C functions have a Khorified counterpart, even if most of them are just direct calls to the C functions using the "k" versions (ksin, kcos) will ensure portability. This is even more truth for the file manipulating functions.
  6. If porting an existing C algorithm or program to Khoros try do make it as Khoros-like as possible. This is more or less the same recommendation as above. Just adding code to an user interface created with composer / guise / ghostwriter will not get all possible functionality of Khoros. Try to rewrite the code so it will use the Polymorphic Data Services and calls to the "k" versions of the routines (see above).
  7. Use the latest version of Khoros. Khoros is under constant evolution, and with each release or patch the bugs are being fixed. Using the latest version with all available patches applied could reduce the risk of bugs in the Khoros libraries your program use.
  8. Distribute your code as a toolbox. If you have some software objects that can be useful for others, please by all means make it a neat toolbox and distribute to the other Khoros users. That will help other people that may be needing the software objects you have, and these people will (probably) give you feedback about the toolbox and routines, helping you to improve them and fix bugs. Remember that everybody could benefit of other peoples' work in the Khoros community.

[bar]

Table of Contents - Index
Previous: Debugging Khoros code

[bar]

These pages copyright © Rafael Santos (e-mail valid until March 1998). Please let me know if this tutorial is useful for you, I need to justify the time I used to develop it. Comments, requests and bug reports are also welcome, but please see the section Before you e-mail me...

[bar2]

Khoros copyright © Khoral Research, Inc. (KRI) - run klicense for more information.
Khoral Research Inc. is not responsible for or is supervising these pages.

[bar2]

The latest version of this document can be found at Ejima Lab Khoros Pages at Kyushu Institute of Technology, Japan (until March 1998).
Mirrors for this tutorial can be found at Universidade do Vale do Paraíba, Brasil and PUC/RS, Brasil.

[bar2]

Generated with StructHTML, 14:19 August 30, 1997