|
|
Now that you have found all the (known) bugs in macros, you can move on to debugging sget. sget.c compiles cleanly and is lint-free, so you start using it to extract your files:
$ cc -g -o sget sget.c $ cat files src/file1 src/s.main.c $ sget <files file1: 0 characters, 0 words, 0 lines Caught signal, removedNot only did the program work incorrectly, it also left the copied file lying around, as you can see by listing file1:
$ ls file1 file1It looks like there may be two problems (and two clues) here:
removed
instead of file1 removed
.
You may suspect that the problem is related to the missing file name.
debug> create sget <filesThe debugger displays the following:
New program sget (process p1) created HALTED p1 [main in sget.c] 131: (void) signal(SIGHUP, handler);Run the program.
debug> runThe debugger displays the following:
file1: 0 characters, 0 words, 0 lines SIGNALED 8 (fpe) in p1 [getstats in sget.c] 108: (void) printf("\taverage characters per line: %d\n",From the event notification, you can see that the signal is fpe (or SIGFPE, for arithmetic exception). On many processors, trying to divide by zero will generate SIGFPE, and it looks like that's what's happening here:
debug> list -c 4 108: (void) printf("\taverage characters per line: %d\n", 109: char_cnt/line_cnt); 110: (void) printf("\taverage words per line: %d\n", 111: word_cnt/line_cnt);
debug> print line_cnt 0
if (line_cnt) { (void) printf("\taverage characters per line: %d\n", char_cnt/line_cnt); (void) printf("\taverage words per line: %d\n\n", word_cnt/line_cnt); }
cc -g -o sget sget.cNow you can look at the second problem -- the one with the signal handler. However, since you just fixed the bug that caused the signal handler to be invoked, you have to fake it by generating a signal within debug.
debug> create sget <filesThe debugger displays the following:
New program sget (process p1) created HALTED p1 [main in sget.c] 134: (void) signal(SIGHUP, handler);
debug> stop sget.c@getstatsThe debugger displays the following:
EVENT [1] assignedRun the program:
debug> runThe debugger displays the following:
STOP EVENT TRIGGERED: sget.c@getstats in p2 [getstats in sget.c] 106: (void) printf("%s: %d characters, %d words, %d lines\n",
debug> kill sigfpe
The next time you let the process run or step, it will receive the signal:
debug> run SIGNALED 8 (fpe) in p2 [getstats in sget.c] 106: (void) printf("%s: %d characters, %d words, %d lines\n",
debug> stop handlerThe debugger displays the following:
EVENT [2] assignedIf you let it run again it will get to the signal handler:
debug> runThe debugger displays the following:
STOP EVENT TRIGGERED: handler in p2 [handler in sget.c] 120: (void) fprintf(stderr, "Caught signal, %s removed\n", debug> l -c3 120: (void) fprintf(stderr, "Caught signal, %s removed\n", 121: base_name); 122: (void) remove(base_name);
handler is using base_name, which should be the name of the file; instead, it is the null string:
debug> print base_name ""base_name should have been set in main before the call to getstats, either it hasn't been set or it got overwritten somewhere.
debug> createThe debugger displays the following:
p2 killed sget <files New program sget (process p3) created HALTED p3 [main in sget.c] 134: (void) signal(SIGHUP, handler);
debug> runThe debugger displays the following:
STOP EVENT TRIGGERED: sget.c@getstats in p3 [getstats in sget.c] 106: (void) printf("%s: %d characters, %d words, %d lines\n", debug> print base_name ""
debug> stackThe debugger displays the following:
Stack Trace for p3, Program sget *[0] getstats(file="file1") [sget.c@106] [1] main(0x1, 0x804794c, 0x8047954) [sget.c@156] [2] _start() [0x8048662]debug> set %frame = 1
debug> print base_name "file1"
debug> whatis base_name char * [local variable in main] const char * [file static variable in sget.c]Aha, there is more than one base_name. Type symbols -fl (for file and local scopes) to show the values of both base_names:
debug> symbols -flv base*The debugger displays the following:
Symbols for p3, Program sget Name Location Line Value base_name main 127 "file1" base_name sget.c ""
Three related commands -- signal, kill, and cancel -- give you control over signals. All three commands recognize signals by number or by name, which can be upper or lower case, with or without the SIG prefix, so these commands are equivalent:
debug> signal 2 debug> signal sigint debug> signal INTsignal (or sig) lets you specify the actions the debugger is to take when a process receives a signal. By default, debug catches all signals; it will stop the process and tell you what happened. You can get more information by creating an event with a command list for a particular signal:
debug> signal sigint {stack; run}You can also tell it to ignore a signal instead:
debug> signal -i sighupIf the process received sighup, debug will not stop the process. If you tell debug to ignore sigfpe, it will let sget run and exit without stopping it in getstats.
debug> signal Signal Disposition for p1, program sget 1 sighup ignored SIG_DFL 2 sigint caught SIG_DFL ...signal sig will reset catching an ignored signal:
debug> signal sighup debug> signal Signal Disposition for p1, program sget 1 sighup caught SIG_DFL ...
The fourth column displays the current signal handler associated with each signal in your program.
The -d option to the signal command will let you change the debugger's default behavior with respect to a specific signal. signal -d -i sig changes the default behavior to ignore sig; signal -d sig resets the default to catch sig. Any process created or grabbed by the debugger inherits this default signal behavior.
kill and cancel are inverse operations; kill sig sends sig to the process and cancel sig ensures that the process will never receive it. There are two things to note about these commands:
The ``current frame'' is the basis for the ``current context''; the current view you have into the state of the process. The value of the debugger variable %frame is the number of the current stack frame; in a stack trace the current frame is also indicated by an asterisk (*). debug resets %frame whenever the process stops. You may also change the current frame by changing %frame with the set or print commands. The other context variables, %file, %func, %line, %list_file, %list_line, %loc, and %db_lang, are also set whenever an event triggers or when you change %frame.
The current context determines the following three aspects of the debugger's behavior:
Setting %frame sets %list_line to the statement currently being executed in the new stack frame. You can see the current statement for each frame in the stack trace. If you type list (with no arguments) after changing %frame, it will start the display from the new value of %list_line. %loc is the value of the location counter for the current frame, and works the same way with dis.
debug> print %frame, %func, %list_line 2 "getstats" 106 debug> list -c1 106: (void) printf("%s: %d characters, %d words, %d lines\n", debug> set %frame = 1 debug> print %func, %line "main" 156 debug> list -c1 156: getstats(base_name);
You may also change the context by setting %func, which indirectly sets %frame and the other context variables:
debug> set %func = process_def debug> print %frame, %line 2 227The drawback to using %func instead of %frame is that you can not differentiate between multiple occurrences of a recursive function; debug will always pick the topmost frame on the stack for that function.
Changing the context does not affect the stack trace; you will still see the entire stack trace even if the current frame is somewhere in the middle, so you can always get back to the topmost frame.
symbols prints the local symbols for the current frame.
By changing %frame you can walk up and down the stack and look at the
variables in any active function.
The output of symbols -f (showing file static symbols) may also change;
functions defined in different files will have different statics visible to them.
The following example uses macros from sessions 1 through 4:
debug> stack Stack Trace for p1, Program macros *[0] insert(name=0x804abec, list=0x804ac0c) [macro.c@56] [1] add_def(name=0x804abec, read_more=1) [main.c@211] [2] process_def() [main.c@227] [3] main(argc=1, argv=0x80479cc, 0x80479d4) [main.c@339] [4] _start() [0x80485a2] debug> print %frame, %file 0 "macro.c"
debug> symbols -f Symbols for p1, Program macros Name Location Line root macro.c debug> set %frame = 3 debug> print %frame, %file 3 "main.c" debug> symbols -f Symbols for p1, Program macros Name Location Line Usage main.c add_def main.c gettok main.c lines main.c next_tok main.c process_def main.c tokenize main.c debug> stack Stack Trace for p1, Program macros [0] insert(name=0x804abec, list=0x804ac0c) [macro.c@56] [1] add_def(name=0x804abec, read_more=1) [main.c@211] [2] process_def() [main.c@227] *[3] main(argc=1, argv=0x80479cc,0x80479d4) [main.c@339] [4] _start() [0x80485a2]
The value of an expression (used in print, set, and stop commands and if and while statements) depends on both symbol visibility and the language used to evaluate the expression. The current language (%db_lang) may change with the context if your program combines files written in different programming languages (say, C and FORTRAN).