Table of Contents ChiLib Library Documentation

13. Debugging hints

13.1. Inspecting containers

During debugging it is often helpful or necessary to inspect the contents of containers. Every debugger has a command to show the fields of an object, given either the name of the object or a pointer to it. In fact, most debuggers have two: an inspect command to see the fields briefly, and a watch command, to put the object onto a permanent watch list. The inspect command is better for peeking inside a data structure.

To see the contents of a String object, inspect the _str pointer. It is a C character pointer, and the debugger should just show the string contents. There is a reference count somewhere, but you cannot see it.

To see the contents of an Array<X> object, inspect the _elements pointer. It is an X* pointer. Now you need to tell the debugger to expand the pointer to an array. Every debugger has such a command. You need to specify the number of elements you want to see. To find out how many you want, inspect the _low and _high fields of the array object. They give the current array range. You need to subtract _low from the index to translate between array indices and offsets into the _elements array.

To see the contents of a list or queue, inspect the _head pointer. It points to the first link. Follow it and you see the first link, with an _info and _next field. Inspect the _info field to see the first element, and follow the _next field to get to the next link. Keep doing that until _next is 0.

13.2. Inspecting self

ChiLib uses self to denote the implicit argument, but you need to tell the debugger to inspect this, the official C++ name for a pointer to the implicit argument.

A useful trick is to put *this onto the watch list. That way, you always see the current implicit argument.

13.3. Trapping errors

The errors you are most likely interested in trapping are precondition and bounds errors, because they are your fault. In contrast, there is little you can do about memory errors. If you run out of memory, the exact location where the exhaustion occurred is not too interesting. Invariant or postcondition errors can signify programming errors, and you will want to trap those that come from your code. They can also signify that an object has been overwritten by a wild pointer, in which case they are just a symptom of a worse problem elsewhere.

The trapping strategy depends on whether your compiler supports exception handling. If it does, run the debugger, wait for the exception to occur, and then inspect the call stack. It will contain the function producing the error. If not, place a breakpoint into Error::abort. When the breakpoint gets triggered, inspect the call stack. Don't wait until after abort actually aborts the program. Debuggers don't usually keep the call stack information after the program terminates.