Message
|
This post describes general tips for using the GNU debugger (gdb) with gcc (GNU compiler). These should apply under Linux, OpenBSD, Cygwin, and other GNU platforms.
Enable debugging when compiling
The first thing to do is make sure that debug information is written to the executable file. That way the debug information in gdb is much more informative. Look for the line(s) that do the actual compiling and make sure that '-g3' (output gdb debug information, level 3) is present, and I strongly recommend you turn off optimisation (remove any -O option, such as -O, -O1, -O2, -O3 etc.). If you leave optimization on you may find that variables you have in your source "don't exist" as far as the debugger is concerned, or the order of instructions is different.
An example from the SMAUG Makefile:
.c.o: mud.h
$(CC) -c $(C_FLAGS) $(USE_IMC) $<
The above lines do the compiling, using the variable CC as the compiler, and flags C_FLAGS as the flags. Looking further up the make file reveals that CC is the compiler gcc, and the C_FLAGS are as follows ...
CC = gcc
C_FLAGS = $(OPT_FLAG) -g3 -Wall -Wuninitialized $(PROF) $(NOCRYPT) $(DBUGFLG) -DSMAUG $(SOLARIS_FLAG) $(TIME) $(REG)
In the example above I removed -O from the original Makefile, and added -g3.
If you changed the Makefile, do a "clean compile" to make sure that the executable contains the debugging information.
eg.
rm *.o
make
Enable large core dumps
Some versions of Linux may limit the size of your core file (the file that is automatically created when a program crashes).
Type "ulimit -a" to see what is reported...
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
...
Note the limit on the core file size. Remove this by adding to your file .bash_profile (in your home directory) this line:
# max core dump size in blocks
ulimit -c 16384
The log out and log back in again, and check the limit has increased (by typing "ulimit -a" again).
Remove SIGSEGV handler from your code
In the SMAUG release (see below) there is a SIGSEGV handler - commented out. If you put that in, then it does a nice display on a segment violation (crash), however it makes debugging with gdb pretty hard, because it handles the crash (if any) internally.
If you see something like this in your log, then the SIGSEV handler is active ...
Sat Jan 17 03:34:44 2004 :: SEGMENTATION VIOLATION
Sat Jan 17 03:34:44 2004 :: nanny used , frowns and takes something from a dwarven toddler's mouth.
Sat Jan 17 03:34:44 2004 :: NPC: Dark Trail of Vines room: 3
... and so on ...
This is the code that does that ...
signal( SIGSEGV, SegVio ); // <-- remove this line
... and later on ...
static void SegVio()
{
CHAR_DATA *ch;
char buf[MAX_STRING_LENGTH];
log_string( "SEGMENTATION VIOLATION" );
log_string( lastplayercmd );
for ( ch = first_char; ch; ch = ch->next )
{
sprintf( buf, "%cPC: %-20s room: %d", IS_NPC(ch) ? 'N' : ' ',
ch->name, ch->in_room->vnum );
log_string( buf );
}
exit(0);
}
In order for debugging to work as described below (ie. in order to get a "core" file) you must at least comment out the first line, the one in bold that sets up the SIGSEGV handler.
See further down in this thread for how you can make a gdb command to display the same thing that the SegVio handler did.
Getting started - basics of gdb
Once gdb is running you can get help by typing various things. Also you can press <tab> for command-completion. That is, enter a partial command and press <tab>. Commands can be abbreviated to the shortest unambiguous form (eg. "n" for "next"). The various things you can look at are:
- help (or h) for internal help
- help <topic> for help on a command (eg. help continue), or class of command (eg. help breakpoints). The command classes are shown when you type "help" on its own.
- apropos <word> - list help topics which contain that word (eg. apropos break for help on breakpoints and related commands)
- info <something> - get information about various things (eg. info registers). Type "info" on its own to get a list.
- info source - shows the current source file you are looking at
- info frame - shows the curreent stack frame
- show <something> - show a setting (eg. show version). Type "show" on its own to show all internal settings.
Starting and stopping your program
- run - if you have loaded gdb before running your program you can set various breakpoints if desired, and then type "run" to run it.
- If you have program arguments, you can append them to the "run" command, eg. "run config_file".
- continue - after stopping at a breakpoint type "cont" to continue.
- quit - to exit from gdb and stop running your program, type "quit".
Let's add a bug and detect it in gdb
I'm using the MUD server source smaug1.4a_mxp.tgz from this site's download area. I'll edit the function send_to_char_color in comm.c and deliberately remove an important line by commenting out an assignment at line 2744, which assigns an important pointer to "d".
void send_to_char_color( const char *txt, CHAR_DATA *ch )
{
DESCRIPTOR_DATA *d;
char *colstr;
const char *prevstr = txt;
char colbuf[20];
int ln;
if ( !ch )
{
bug( "Send_to_char_color: NULL *ch" );
return;
}
if ( !txt || !ch->desc )
return;
// d = ch->desc; // BUG - commented out this line
/* Clear out old color stuff */
/* make_color_sequence(NULL, NULL, NULL);*/
while ( (colstr = strpbrk(prevstr, "&^")) != NULL )
{
if (colstr > prevstr)
write_to_buffer(d, prevstr, (colstr-prevstr));
ln = make_color_sequence(colstr, colbuf, d);
if ( ln < 0 )
{
prevstr = colstr+1;
break;
}
else if ( ln > 0 )
write_to_buffer(d, colbuf, ln);
prevstr = colstr+2;
}
if ( *prevstr )
write_to_buffer(d, prevstr, 0);
return;
}
Now, let's fire up the server and try to connect to it.
cd ../area
../src/smaug
I connect as Lordrom, and after the initial welcome screens it crashes ...
Fri Jan 16 10:04:22 2004 :: Loading player data for: Lordrom (1K)
Fri Jan 16 10:04:22 2004 :: Lordrom@10.0.0.11((pipe breaking)) has connected.
Segmentation fault (core dumped)
Time for gdb ...
First, let's locate the core file:
$ ls -lt core*
-rw------- 1 nick nick 3682304 Jan 16 10:04 core.22179
In this version of Linux, the core file has the process ID appended (to help debugging where multiple processes might be in the same directory). A quick check indicates the date/time agrees with when I did the test, so now we know the core file name.
We start gdb in this case by typing:
gdb (object file) (core file)
Like this:
$ gdb ../src/smaug core.22179
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
Core was generated by `../src/smaug'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Reading symbols from /lib/libnss_files.so.2...done.
Loaded symbols for /lib/libnss_files.so.2
#0 0x080bbe35 in write_to_buffer (d=0x4a,
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", length=0)
at comm.c:1615
1615 if ( !d->outbuf )
(gdb)
The relevant parts are in italics. First the nature of the error, segmentation fault, and secondly the line at which it occurred, line 1615.
list - lists source
A helpful start is to list the source around the line in error, to put it into context. In this case we only see some comments.
(gdb) list
1610 }
1611
1612 /*
1613 * Normally a bug... but can happen if loadup is used.
1614 */
1615 if ( !d->outbuf )
1616 return;
1617
1618 /*
1619 * Find length in case caller didn't.
Notice how the debug information in the object file lets us see our original source code.
If you want to list in another file, prepend the file name, eg:
print - print the value of a variable
The line of the crash refers to dereferencing the descriptor "d" so let's print it and see if it looks OK:
(gdb) print d
$2 = (DESCRIPTOR_DATA *) 0x4a
(gdb)
This looks bad, a pointer is usually something long, not 0x4a.
info - information about things
The "info" command can show heaps of things, type "info" on its own to see an extensive list.
(gdb) info
"info" must be followed by the name of an info command.
List of info subcommands:
info address -- Describe where symbol SYM is stored
info all-registers -- List of all registers and their contents
info args -- Argument variables of current stack frame
info breakpoints -- Status of user-settable breakpoints
info catch -- Exceptions that can be caught in the current stack frame
info common -- Print out the values contained in a Fortran COMMON block
info copying -- Conditions for redistributing copies of GDB
info dcache -- Print information on the dcache performance
info display -- Expressions to display when program stops
info extensions -- All filename extensions associated with a source language
info files -- Names of targets and files being debugged
info float -- Print the status of the floating point unit
info frame -- All about selected stack frame
info functions -- All function names
info handle -- What debugger does when program gets various signals
info line -- Core addresses of the code for a source line
info locals -- Local variables of current stack frame
info macro -- Show the definition of MACRO
info mem -- Memory region attributes
info proc -- Show /proc process information about any running process
info program -- Execution status of the program
info registers -- List of integer registers and their contents
info remote-process -- Query the remote system for process info
info scope -- List the variables local to a scope
info set -- Show all GDB settings
info sharedlibrary -- Status of loaded shared object libraries
info signals -- What debugger does when program gets various signals
info source -- Information about the current source file
info sources -- Source files in the program
info stack -- Backtrace of the stack
info symbol -- Describe what symbol is at location ADDR
info target -- Names of targets and files being debugged
info terminal -- Print inferior's saved terminal status
info threads -- IDs of currently known threads
info tracepoints -- Status of tracepoints
info types -- All type names
info udot -- Print contents of kernel ``struct user'' for current child
info variables -- All global and static variable names
info vector -- Print the status of the vector unit
info warranty -- Various kinds of warranty you do not have
info watchpoints -- Synonym for ``info breakpoints''
Type "help info" followed by info subcommand name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
A couple of useful things here are the arguments to the function:
(gdb) info args
d = (DESCRIPTOR_DATA *) 0x4a
txt = 0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r"
length = 0
(gdb)
This shows the current values that were passed down to this function. We can put this in context by listing the function:
(gdb) list write_to_buffer
1598
1599 /*
1600 * Append onto an output buffer.
1601 */
1602 void write_to_buffer( DESCRIPTOR_DATA *d, const char *txt, int length )
1603 {
1604 int origlength;
By using "list (function-name)" we can see the start of a function. This shows that this function has three arguments, d, txt, and length).
As you can see, "info args" showed the values for each of those three.
Also we can use "info local" to see the values of local variables.
(gdb) info local
origlength = 0
(gdb)
That shows the current value of origlength.
bt - backtrace
A very useful command is "bt" (backtrace). This shows the call stack.
(gdb) bt
#0 0x080bbe35 in write_to_buffer (d=0x4a,
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", length=0)
at comm.c:1615
#1 0x080bef81 in send_to_char_color (
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", ch=0x855e9c0)
at comm.c:2762
#2 0x080bf3cd in send_to_pager_color (
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", ch=0x855e9c0)
at comm.c:2862
#3 0x08059794 in do_help (ch=0x855e9c0, argument=0x81b3d0f "imotd")
at act_info.c:1972
#4 0x080bd90f in nanny (d=0x855c8c8, argument=0xbfffdea1 "") at comm.c:2284
#5 0x080ba142 in game_loop () at comm.c:731
#6 0x080b960a in main (argc=1, argv=0xbfffe3a4) at comm.c:316
#7 0x420156a4 in __libc_start_main () from /lib/tls/libc.so.6
(gdb)
This tells us what functions called what. Namely in this case:
__libc_start_main -> main -> game_loop -> nanny -> do_help ->
send_to_pager_color -> send_to_char_color -> write_to_buffer
If you are familiar with SMAUG, this is pretty-much what we expect. The main function calls the "game_loop" and when it gets a new character that calls "nanny" to handle the new connection. Part of nanny uses "do_help" to send help information to the player.
frame/up/down - go to a stack frame
So far we have been looking at stack frame 0 - the bottom of the stack. Let's work our way back. Typing "frame 1" goes to frame 1. (We could also type "up" to go up one level, or "down" to go down one level).
(gdb) frame 1
#1 0x080bef81 in send_to_char_color (
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", ch=0x855e9c0)
at comm.c:2762
2762 write_to_buffer(d, prevstr, 0);
Now that we have changed frames, typing "list" again shows the line in the context of the new frame ...
(gdb) list
2757 else if ( ln > 0 )
2758 write_to_buffer(d, colbuf, ln);
2759 prevstr = colstr+2;
2760 }
2761 if ( *prevstr )
2762 write_to_buffer(d, prevstr, 0);
2763 return;
2764 }
2765
2766 void write_to_pager( DESCRIPTOR_DATA *d, const char *txt, int length )
(gdb)
In this case the frame is on the line which calls write_to_buffer, which is what we expect, as we just came from the frame which was the contents of that function.
Once again we can find the arguments to this function, and any local variables ...
(gdb) i args
txt = 0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r"
ch = (CHAR_DATA *) 0x855e9c0
(gdb) i locals
d = (DESCRIPTOR_DATA *) 0x4a
colstr = 0x0
prevstr = 0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r"
colbuf = "\001\000\000\000(ÕU\b ÿ¿\000\000\000\000À<\023B"
ln = 134987743
(gdb)
print - looking at structures
OK, we have a couple of structures here, CHAR_DATA and DESCRIPTOR_DATA.
Let's try looking at them. By dereferencing them with an asterisk (same as in C) we see what the pointer is pointing to. Let's try ch (the player character) ...
(gdb) print *ch
$15 = {next = 0x0, prev = 0x0, next_in_room = 0x0, prev_in_room = 0x0,
master = 0x0, leader = 0x0, fighting = 0x0, reply = 0x0, retell = 0x0,
switched = 0x0, mount = 0x0, hunting = 0x0, fearing = 0x0, hating = 0x0,
spec_fun = 0, mpact = 0x0, mpactnum = 0, mpscriptpos = 0, pIndexData = 0x0,
desc = 0x855c8c8, first_affect = 0x0, last_affect = 0x0, pnote = 0x0,
comments = 0x0, first_carrying = 0x855f300, last_carrying = 0x855f940,
in_room = 0x834d948, was_in_room = 0x0, pcdata = 0x855eb90, last_cmd = 0,
prev_cmd = 0, dest_buf = 0x0, alloc_ptr = 0x0, spare_ptr = 0x0, tempnum = 0,
editor = 0x0, first_timer = 0x0, last_timer = 0x0, morph = 0x0,
name = 0x855e1c8 "Lordrom", short_descr = 0x8225150 "",
long_descr = 0x8225150 "", description = 0x8225150 "", num_fighting = 0,
substate = 0, sex = 1, class = 0, race = 0, level = 65, trust = 0,
played = 11542914, logon = 1074207862, save_time = 0, timer = 0, wait = 0,
hit = 3000, max_hit = 3000, mana = 5000, max_mana = 5000, move = 3100,
max_move = 3100, practice = 2, numattacks = 0, gold = 267, exp = 2000,
act = {bits = {570431064, 0, 0, 0}}, affected_by = {bits = {0, 0, 0, 0}},
no_affected_by = {bits = {0, 0, 0, 0}}, carry_weight = 1023,
carry_number = 8, xflags = 0, no_immune = 0, no_resistant = 0,
no_susceptible = 0, immune = 0, resistant = 0, susceptible = 0, attacks = {
bits = {0, 0, 0, 0}}, defenses = {bits = {0, 0, 0, 0}}, speaks = -1,
speaking = 1, saving_poison_death = 0, saving_wand = 0,
saving_para_petri = 0, saving_breath = 0, saving_spell_staff = 0,
alignment = 0, barenumdie = 1, baresizedie = 4, mobthac0 = 0, hitroll = 2,
damroll = 1, hitplus = 0, damplus = 0, position = 12, defposition = 0,
style = 2, height = 69, weight = 157, armor = 76, wimpy = 0, deaf = 0,
perm_str = 18, perm_int = 17, perm_wis = 13, perm_dex = 13, perm_con = 15,
perm_cha = 14, perm_lck = 15, mod_str = 0, mod_int = 1, mod_wis = 1,
mod_dex = 1, mod_con = 1, mod_cha = 0, mod_lck = 1, mental_state = -12,
emotional_state = 0, pagelen = 24, inter_page = 0, inter_type = 0,
inter_editing = 0x0, inter_editing_vnum = -1, inter_substate = 0,
retran = 0, regoto = 0, mobinvis = 0}
(gdb)
That looks nice, but how about "pretty printing" it?
(gdb) set print pretty
(gdb) print *ch
$17 = {
next = 0x0,
prev = 0x0,
next_in_room = 0x0,
prev_in_room = 0x0,
master = 0x0,
leader = 0x0,
fighting = 0x0,
reply = 0x0,
retell = 0x0,
switched = 0x0,
mount = 0x0,
hunting = 0x0,
fearing = 0x0,
hating = 0x0,
spec_fun = 0,
mpact = 0x0,
mpactnum = 0,
mpscriptpos = 0,
pIndexData = 0x0,
desc = 0x855c8c8,
first_affect = 0x0,
last_affect = 0x0,
pnote = 0x0,
comments = 0x0,
first_carrying = 0x855f300,
last_carrying = 0x855f940,
in_room = 0x834d948,
was_in_room = 0x0,
pcdata = 0x855eb90,
last_cmd = 0,
prev_cmd = 0,
dest_buf = 0x0,
alloc_ptr = 0x0,
spare_ptr = 0x0,
tempnum = 0,
editor = 0x0,
first_timer = 0x0,
last_timer = 0x0,
morph = 0x0,
name = 0x855e1c8 "Lordrom",
short_descr = 0x8225150 "",
long_descr = 0x8225150 "",
description = 0x8225150 "",
num_fighting = 0,
substate = 0,
sex = 1,
class = 0,
race = 0,
level = 65,
trust = 0,
played = 11542914,
logon = 1074207862,
save_time = 0,
timer = 0,
wait = 0,
hit = 3000,
max_hit = 3000,
mana = 5000,
max_mana = 5000,
move = 3100,
max_move = 3100,
practice = 2,
numattacks = 0,
gold = 267,
exp = 2000,
act = {
bits = {570431064, 0, 0, 0}
},
affected_by = {
bits = {0, 0, 0, 0}
},
no_affected_by = {
bits = {0, 0, 0, 0}
},
carry_weight = 1023,
carry_number = 8,
xflags = 0,
no_immune = 0,
no_resistant = 0,
no_susceptible = 0,
immune = 0,
resistant = 0,
susceptible = 0,
attacks = {
bits = {0, 0, 0, 0}
},
defenses = {
bits = {0, 0, 0, 0}
},
speaks = -1,
speaking = 1,
saving_poison_death = 0,
saving_wand = 0,
saving_para_petri = 0,
saving_breath = 0,
saving_spell_staff = 0,
alignment = 0,
barenumdie = 1,
baresizedie = 4,
mobthac0 = 0,
hitroll = 2,
damroll = 1,
hitplus = 0,
damplus = 0,
position = 12,
defposition = 0,
style = 2,
height = 69,
weight = 157,
armor = 76,
wimpy = 0,
deaf = 0,
perm_str = 18,
perm_int = 17,
perm_wis = 13,
perm_dex = 13,
perm_con = 15,
perm_cha = 14,
perm_lck = 15,
mod_str = 0,
mod_int = 1,
mod_wis = 1,
mod_dex = 1,
mod_con = 1,
mod_cha = 0,
mod_lck = 1,
mental_state = -12,
emotional_state = 0,
pagelen = 24,
inter_page = 0,
inter_type = 0,
inter_editing = 0x0,
inter_editing_vnum = -1,
inter_substate = 0,
retran = 0,
regoto = 0,
mobinvis = 0
}
(gdb)
That took quite a bit more room, but laid out things more-or-less like the original structure, so we can easily see each value.
Now we'll try the other structure, DESCRIPTOR_DATA "d":
(gdb) print *d
Cannot access memory at address 0x4a
(gdb)
Clearly there is a problem with d, so we just have to work out why. In this case, we know, because we took out the assignment statement that made it work.
d = ch->desc;
However if it wasn't obvious, we would either look around in the local function, or keep typing "up" to go back to the caller, if the bad value was passed down from an earlier function.
In our case we can see that "d" is a local variable, and that the problem must be in the current function. How we do that is like this:
First type "f" (frame) to get information about the current frame ...
(gdb) f
#1 0x080bef81 in send_to_char_color (
txt=0x82c9a40 "This is where you may post messages to imms only\n\r \n\r \n\rTo edit it.. type hedit imotd\n\r \n\rthen hset save\n\r", ch=0x855e9c0)
at comm.c:2762
2762 write_to_buffer(d, prevstr, 0);
Now list the current function (whose name we saw in the stack frame above), and keep typing "list" until we get down to our current line...
(gdb) list send_to_char_color
2725
2726 /*
2727 * Same as above, but converts &color codes to ANSI sequences..
2728 */
2729 void send_to_char_color( const char *txt, CHAR_DATA *ch )
2730 {
2731 DESCRIPTOR_DATA *d;
2732 char *colstr;
2733 const char *prevstr = txt;
2734 char colbuf[20];
(gdb) list
2735 int ln;
2736
2737 if ( !ch )
2738 {
2739 bug( "Send_to_char_color: NULL *ch" );
2740 return;
2741 }
2742 if ( !txt || !ch->desc )
2743 return;
2744 // d = ch->desc;
(gdb)
(gdb) list
2745 /* Clear out old color stuff */
2746 /* make_color_sequence(NULL, NULL, NULL);*/
2747 while ( (colstr = strpbrk(prevstr, "&^")) != NULL )
2748 {
2749 if (colstr > prevstr)
2750 write_to_buffer(d, prevstr, (colstr-prevstr));
2751 ln = make_color_sequence(colstr, colbuf, d);
2752 if ( ln < 0 )
2753 {
2754 prevstr = colstr+1;
(gdb) list
2755 break;
2756 }
2757 else if ( ln > 0 )
2758 write_to_buffer(d, colbuf, ln);
2759 prevstr = colstr+2;
2760 }
2761 if ( *prevstr )
2762 write_to_buffer(d, prevstr, 0);
2763 return;
2764 }
You can save a bit of time here by just hitting <enter> instead of retyping "list" - hitting <enter> tells gdb to repeat the last command.
Another way of achieving the same effect, with less typing, would be to list the current frame, down to the line number we know we are up to:
(gdb) list send_to_char_color, 2762
2730 {
2731 DESCRIPTOR_DATA *d;
2732 char *colstr;
2733 const char *prevstr = txt;
2734 char colbuf[20];
2735 int ln;
2736
2737 if ( !ch )
2738 {
2739 bug( "Send_to_char_color: NULL *ch" );
2740 return;
2741 }
2742 if ( !txt || !ch->desc )
2743 return;
2744 // d = ch->desc;
2745 /* Clear out old color stuff */
2746 /* make_color_sequence(NULL, NULL, NULL);*/
2747 while ( (colstr = strpbrk(prevstr, "&^")) != NULL )
2748 {
2749 if (colstr > prevstr)
2750 write_to_buffer(d, prevstr, (colstr-prevstr));
2751 ln = make_color_sequence(colstr, colbuf, d);
2752 if ( ln < 0 )
2753 {
2754 prevstr = colstr+1;
2755 break;
2756 }
2757 else if ( ln > 0 )
2758 write_to_buffer(d, colbuf, ln);
2759 prevstr = colstr+2;
2760 }
2761 if ( *prevstr )
2762 write_to_buffer(d, prevstr, 0);
(gdb)
We found out both of those pieces of information when we typed "frame".
ptype - finding out information about a structure
Say we want to find out more about DESCRIPTOR_DATA. We can type:
ptype DESCRIPTOR_DATA (the structure itself)
or
ptype d (an instance of the structure)
Both cases will give an expansion of the source for the structure:
(gdb) ptype d
type = struct descriptor_data {
DESCRIPTOR_DATA *next;
DESCRIPTOR_DATA *prev;
DESCRIPTOR_DATA *snoop_by;
CHAR_DATA *character;
CHAR_DATA *original;
char *host;
int port;
int descriptor;
sh_int connected;
sh_int idle;
sh_int lines;
sh_int scrlen;
sh_int mxp;
bool fcommand;
char inbuf[1024];
char incomm[1024];
char inlast[1024];
int repeat;
char *outbuf;
long unsigned int outsize;
int outtop;
char *pagebuf;
long unsigned int pagesize;
int pagetop;
char *pagepoint;
char pagecmd;
char pagecolor;
char *user;
int newstate;
unsigned char prevcolor;
} *
(gdb)
This is very handy for seeing what is going on in structures without having to go back to the source.
Remember, if we want to see what the actual structure elements contain at a particular moment, we can print them, eg. "print *d".
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|