On Wed, 21 Mar 2001, Jake Turner wrote: > Ok, sorry about all that hassle guys. > > But you may of noticed in my last snippet of code, in some cases i was > assigning data to map[-1][x] or map[x][-1] or even negative in both! which > obviously ballsed it up :P A general way to find this stuff out, BTW, is by taking advantage of the fact that processes "dump core" when they crash (unless you have core dumps disabled or have a size limit too small for the core file to fit: if you're using bash, try 'ulimit -a' to see your account's limits and, if you can, 'ulimit -c unlimited' to unrestrict your coredump size). A coredump is a dump of the process's memory when it crashes. When you compile debugging information into the executable (by using the -g option passed to gcc during compilation, which CircleMUD uses by default), you can use gdb to hunt crashes very effectively. There is an introduction to using the GNU debugger (gdb) linked through Ceramic Mouse, but you won't even need that if you're the tinkering type. Once you have the core file (which will be in your lib/ directory under the CircleMUD directory) from the crash, the following from the CircleMUD directory will start the debugger (% being your shell prompt): % gdb bin/circle lib/core The first argument is the binary executable that produced the coredump (the executable is what has the debugging symbols in it that gdb needs to provide you with a simple interface to looking at the coredump). The second argument is, of course, the coredump. gdb's online help is surprisingly good, so you could start with 'help'. The most important thing is to understand what the debugger is doing for you. The general concepts you need to know can be summarized as follows: When a function is called, the computer jumps to the place of that instruction and begins executing code there within a new local environment. This is called a 'frame of execution'. The frame that you were formerly in (that of the calling function) is pushed onto a stack, later to be recovered (when the function we're in returns). E.g., int foobar(void) { int a, b, c; a = 1; b = 2; c = foobaz(a, b); return (c); } int foobaz(int a, int b) { int r = a + b; return (r); } when foobar() is called from main(), main()'s frame is pushed onto the stack and we create a new frame, foobar(), which has three local variables. Inside foobar() we run into a call to foobaz(), which causes us to create a new frame and jump into foobaz() (passing the appropriate arguments from foobar()'s frame into foobaz()), where we create a local variable r in this new frame and return its value. When we get to return, we free up foobaz()'s frame, pop foobar()'s frame from the stack and restore it as our current frame. When we return from foobar(), we do the same thing, returning back to main()'s frame. Now if we crashed in foobaz() for some (very) bizarre reason, our stack frame would look like: #0 foobaz(a=<blah>, b=<blah>): int r = a + b; #1 foobar(void): c = foobaz(a, b); #2 main(void): printf("%d\n", foobar()); where the line of code following the function name is the line that we were on when the frame was exited (whether by a crash or by pushing a new frame on the stack). Note that we crashed in frame #0 and that what we're looking at can be thought of as a look back into the execution of our program. If you understand this, you will be very pleased to see that things are just as simple and straightforward in gdb. In gdb, if I type 'bt' (which is short for 'backtrace'), I would get something like the following: #0 adjust_kp (killer=0x4016fbfc, victim=0x401a03c4) at fight.c:206 #1 0x8061fc7 in explosion_event (room=0x40140680) at exp.c:1901 . . . (gdb) I start, of course, in the frame we were in when the game crashed. While in this frame I have access to any variable it could access (local variables, function arguments, global variables, etc.), with the caveat that an optimization compiler can make variables disappear and there's nothing the debugger can do about that. So let's say I want to see what line 206 in fight.c is. Being already in frame #0 (by default), I can simply type 'list' and it will give line 206 of fight.c with some pre- and post-context. Good start. Now let's say I see line 206 is: if (!IS_NPC(tanker)) So we have a new variable there. I want to see what it is: (gdb) print tanker (PropertyHashType *) 0x0 Ah hah. So tanker is a pointer to the address 0x0 (which is 0, which is NULL). So I'm trying, in essence, to do: if (!IS_NPC(NULL)) which we'll assume is bad (it is). Now I know why we're crashing on line 206 of fight.c, and with some additional investigative work into where the value of tanker is coming from, I can quickly and easily resolve the bug. Naturally, debugging is not a replacement for being a careful coder. Desk-checking (reading your code) is always recommended, but it's certainly more fun to Indiana Jones it and make the adventure really be getting out of the jam that you got yourself into. So there's a crash course on the loveliness of gdb. Use it. It gets you answers faster than the list and keeps list traffic down. :) -dak -- +---------------------------------------------------------------+ | FAQ: http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html | | Archives: http://post.queensu.ca/listserv/wwwarch/circle.html | +---------------------------------------------------------------+
This archive was generated by hypermail 2b30 : 12/04/01 PST