ID:32719
 
A BYONDscape Classic! Stay tuned to Dream Makers for some nifty new content later this week!



Basic Debugging Tips
by Deadron


Debugging starts with you

When things go wrong, the first instinct of the beginning programmer is often
to panic and hit the forums asking others to tell them what's wrong with their
code. If you find yourself in that situation, don't do anything rash.

Stop. Take a breath.

Remember that you are in control. You can figure out what's going wrong, what
the values of variables are, what order things are being called in. It's all
your code.

The first step to figuring out what's going wrong is to roll up your sleeves
and poke at the code. No one is in a better position than you to figure out the
problem.

If after attempting to debug the problem you still end up having to go to the
forums, you will be in a much better position to describe the problem, which
makes it more likely you'll get an answer.


Preventive measures

There is no deeper hell than doing two days of good work, then discovering
that somewhere in there you introduced a killer bug that completely screws up
your game in unimaginable ways. After hours of trying to debug it, you haven't
come up with anything...so what do you do? Dump two days of work? Keep beating
your head against the wall? Well, best of all is not to get into that situation
in the first place. Here are some things you can do to reduce the chance of
falling into a very hot place.

Source code control

You need to archive your working code frequently. By frequently I don't mean
weekly or even daily: I mean hourly or less! If you add ten lines and they work,
archive the code! Then when something goes seriously wrong, you can check what
changes you made that broke the code, or you can just dump your recent changes
and go back to a working version.

A pretty standard source code control system is CVS. It can be complicated to
learn and use, but I've found it quite easy with the best GUI around for it,
WinCVS:

WinCVS home page

Using this tool, it only takes me a couple of seconds to archive the code,
and it's easy to compare your current changes to earlier versions of a file.

Perforce is another tool considered even easier to use, which you can try if
you have a version of Windows later than Win98. It even won Game Developer
magazine's 2001 Front Line Award for programming tools:

Perforce home page

Both of these tools are free, though Perforce charges if you want more than 2
people accessing the repository. You can't consider yourself a pro unless you
are using source code control.

Keep the original code lines around

When you are about to make changes to a function or block of code, consider
making a copy of the function or block first. This means if your changes don't
work, you can easily see what you changed, and you can choose to put the
original code back. Here is what it looks like when I make such a change:

    proc/EndRound()
// NOW: simplifying the round-handling.
round++
if (round > ROUNDS_PER_LEVEL)
level++
round = 1

/***
// Original round handling
round = (round - 1) + (round + 2) - 0
if (round > ROUNDS_PER_LEVEL)
level = ((level * 2) / 2) + 2 - 1
round = 5 - 4
***/


After making the change, I run things and test it. If they work, I delete the
commented out original code. In some cases if I know I might need to refer back
to the original code later, I leave the commented out code indefinitely.

This simple technique can save you many hours of headache.


printf() debugging

printf() refers to the C function that prints a line of text to standard
output. printf() debugging refers to the most basic form of debugging possible:
putting statements in your code to print out what's happening as the program
runs. This may be Paleolithic-era debugging, but you better get to know it well.
Every major advance in computer software seems to involve a language that
doesn't have full debugging yet, and you are back to printf() debugging. At the
moment, BYOND counts as one of those jumps forward that lands you back in the
dinosaur days of debugging.

Anytime you don't understand why something is happening or not happening in
your code, the first thing to try is printf() debugging. The BYOND version of
printf() is this:

world << "I reached this line!"


The idea is right in this case, but the content isn't too useful. When I'm
trying to track down a problem, I usually end up doing a statement like
this:

world << "[type].FunctionName(): X now = [X]"


Where you would replace FunctionName with
the actual function name. The result looks something like:

/mob/player.Jump(): X now = 3


Often, after you are done debugging, you want an easy way to make sure you
get rid of all the debugging statements. A good one is to put a special comment
you can search on after each debugging statement:

world << "[type].FunctionName(): X now = [X]" // DEBUG


This way a quick search on all cases of // DEBUG would let you
delete those lines. To be even more practical, you can adopt the Gughunter
approach and use // DBG as your flag, so it doesn't get confused with
other debugging comments you might have in your code.

Sometimes you might find it useful to make debugging statements only show up
if you are debugging:

if (debugging)
world << "[type].FunctionName(): X now = [X]" // DEBUG


And sometimes you might want debugging statements to go only to online GMs or
something. In that case, if you keep a list of GMs around, you can do this:

gm_list << "[type].FunctionName(): X now = [X] // DEBUG"


One of the biggest benefits of printf() debugging is simply knowing if a proc
is being reached. When a proc seems to be being skipped or called too many
times, I put in notices like so to let me track the program flow:

mob
proc/FirstFunction()
world << "[type].FirstFunction(): Entered." // DEBUG

// Stuff here.

world << "[type].FirstFunction(): About to call SecondFunction()." // DEBUG
SecondFunction(3, 16, 42, "blah")

proc/SecondFunction(age, height, wisdom, motto)
world << "[type].SecondFunction(): Entered." // DEBUG
world << " age = [age], height = [height], wisdom = [wisdom], motto = [motto]" // DEBUG


This is a simple approach to debugging, but it's powerful. Probably 50% of
questions on the BYOND forums would be answered by spending a couple of minutes
doing printf() debugging.


When untouched code breaks

You have some module or complicated function that hasn't been touched for
three months, and suddenly you start getting errors in that code. You don't have
any clue what could be causing the problem. What to do?

First of all, don't touch the untouched code! If you start mucking around in
untouched code trying to figure out what's wrong, you are just going to make
things worse.

There are two possibilities here: You either didn't actually break the
untouched code (the most likely), or you triggered a bug in the untouched code
that you hadn't found before.

Start out by using printf() debugging in the old code to see if you can
figure out what's happening, but avoid changing the old code in any way until
you understand what's happening.

If printf() debugging doesn't reveal the problem, look at your most recent
code changes. That's where either the real bug or the new triggering situation
lives. Your untouched code didn't mysteriously rewrite itself, though that can
be hard to believe when really strange stuff starts happening.

When you look at your recently changed code, either you will see the problem
or you won't. If you don't, the best thing to do is put the original code back
in place and make changes one by one until you see what broke it. So comment out
the changed code, then go to your source code control archive or to the
commented out original code (you do have one of those around, right?) and put it
back in place. Run the game and see if the problem still exists. If the problem
goes away, you know that your new code is the culprit, and you can add in the
changes a line at a time until the game breaks.

If you went back to the original code and the game is still broken, then you
know that the problem lies elsewhere...probably in the code you changed 5
minutes before that. Keep moving back in changes until you find it...it might be
helpful to use your source code control system to look at diffs of your previous
versions of the game. Worst case, check out a copy of the game from a few days
or a week ago and see if the problem existed then...eventually you will track it
down, as long as you've been archiving your code.

If you haven't kept archives of your code, then it's just going to be a lot
of late night hours while you track down the problem using the other techniques
listed in this article.
You've got some entities showing up in the
 tags.
First of all, don't touch the untouched code! If you start mucking around in
untouched code trying to figure out what's wrong, you are just going to make
things worse.

Solid advice that everyone should pay attention to. I used to go through so much trouble because of not doing that.
I usually make a printerror command like,

proc/printerror(msg)
world << "[msg] @ [__file__] [__line__]"
if(isfile(world.log)) world.log << "[msg] @ [__file__] [__line__]"
Hmph! Crazy entities! I have winkled them out.
SVN is newer than CVS, is it not?
D4RK3 54B3R wrote:
SVN is newer than CVS, is it not?

I think so, yes.