I just read this article on assertions: http://www.cuj.com/documents/s=8464/cujcexp0308alexandr/, and want to integrate these ideas with some of my own. Here are some of the features I put into the World Foundry assertion/debugging system which I think are good ideas worth using:
  • Stream output
    Instead of the pre-processor tricks used in the article I observed that fragments of ostream printing work fine as a macro argument (since it typically doesn't contain any commas), so our assert with printing looks like this
          ~AssertMsg(retVal != -1,"Unable to load string <" << key << "> from section <" << section << "> in ini file <" << filename << ">");  //taken from wftools/attribedit/attribedit.cc. line 79
      
  • Common checking in separate macros Most assertions do the same few comparisons, so we created a few variations which simplify those checks
RangeCheck(min,val,max)
Verifies val is greater than or equal to min, and less than max
RangeCheckInclusive(min,val,max)
Verifies val is greater than or equal to min, and less than or equal to max
RangeCheck(min,val,max)
Verifies val is greater than min, and less than max
AssertEq(first, second)
Verifies first is equal to second, if not prints both and asserts
AssertNe(first, second)
Verifies first is equal to second, if not prints both and asserts
ValidatePtr(pointer)
Verifies parameter is a legal pointer (always checks for NULL, on some processors also checks for word alignment, on some OSs verifies is in valid memory space)
ValidateObject(object)
calls object.Validate() (most World Foundry classes have a Validate function which asserts the class is in a valid state
ValidateObjectPtr(pointer)
Calls ValidatePtr then ValidateObject
Fail(msg)
same as AssertMsg(0,msg);
  • Reproducable runs
    Since all user input in the game goes through the input class (and the engine is deterministic), it is possible to reproduce a previous run precisely by logging all joystick input to a file (along with each frames time stamp and the orignal random # seed).
  • Logging
    World Foundry has a powerfull logging system with many separately enableable output streams (this is accomplished through implementing a "NULL" output stream which just discards all output. By default most log streams point to the null stream, but each can be pointed at stdout, stderr, a file, or even a window on the monochrome monitor. When combined with reproduceable runs this makes debugging easy.

  • Code flow logging
    Reading the article gave me the idea of having every non-failing assert log file and line to generate a log of code flow (so the more assertions you have the better documented your code flow is).


Older, unformatted notes

If you intend to try to debug World Foundry there are several debugging features:

First be sure to run the debug build ( BUILDMODE=debug)

The engine has a multitude of built in debugging streams, each sub-system prints a lot of diagnostic information to these streams. They can be turned on & off individually, so you can talor a debug dump to your needs. See Command Line Arguments for details.

there is a command line switch (-breaktime) which causes the game to call a stub function (called breakpoint) at the specified game time. So it is easy to start tracing on frame 150 by placing a breakpoint on the stub function.

The best way to see what is going on is to turn on the SHOW_COLLISION_AS_BOXES switch (this is a build time switch, found in GNUMakefile.bld, uncomment it and touch game/actor.cc and re-build). Instead of the 3D geometry being rendered you will see the collision boxes that the physics engine uses. (I really ought to make this a run time switch, at least in the debug version)

There is another command line switch (-paranoid) which enables more validation (particularly in the physics system), so you might learn something by playing your joystick recording with that switch on. (btw, -joy is the command line option to play back joystick recording).

And you can see all of the command line switches by entering -h (with the debug version of the engine executable) (although the engine doesn't stop, I should fix that).


Miscellaneous notes on debugging from old e-mails:

We invented something I don't think is used elsewhere, the 'NULL' stream. This makes it possible to turn off a streams output without having to recompile all of the code (of course you still pay the speed penalty).

WF has dozens of streams, each of which can be enabled seperately from the command line.

If you run the debug version with a command line of '-h' it will dump a command line usage before running the level (you will probably need to scroll back or send the output to a file to get it all).

You will see there are 3 different sets of streams: standard (warnings error sfatal statistics progress debug) game (actor movement room flow AI level logo tool carema texture script asset streaming mailbox iff reader frame graphics rooms) library (graphics memory collision)

So, to send the ccollision stream to stdout you need to add '-lcs' to the command line.

Also, the # in the DBSTREAM macro controls what level of stream printing gets compiled in, the default is 3, so a DBSTREAM3 will print, but a DBSTREAM4 won't (we did this because all of the printing even when sent to cnull was slowing it down too much). So if you need more detailed streams go to GNUMakefile.bld and change BUILD_DBSTREAM for whichever buildmode you like (or make a new one, see safe-fast as an example of inheriting from the release buildmode then overriding a few settings).

(You might notice there is a 'kts' buildmode which I hack to be whatever I want today (the nice thing about buildmodes is the each has it's own obj directory, so you can keep many builds around at once).

Also, if you actually want to debug the collision code, you need to know about the joystick recorder, if BUILD_DO_DEBUG_FILE_SYSTEM is defined (again see GNUMakefile.bld), then the game automatically records all joystick and frame timing information into a file called 'joystick.out'. This file can later be used to reproduce the run using the -joy command line option. Since the engine is entirely deterministic a failure can be reproduced as many times as desired while debugging. There is also a command line option called -breaktime

So you should build a release version with BUILD_DO_DEBUG_FILESYSTEM defined, and whenever you see a failure, first run the debug version on that joystick file (be sure to rename it so your next run doesn't destroy it). Many times the debug version will assert on whatever failed. If it doesn't then it is time to either turn on some streams or run the debugger (I tend to prefer streams since they are not temporal (I can jump forward and backwards in time just by scrolling).

Topic revision: r1 - 10 Jul 2003 - 19:34:44 - Kevin Seghetti;