|
|
$ cc -g -o macros main.c macro.c main.c: macro.c:
$ cat test1 define(TEST,this is a test) TEST define(X,TEST test TEST) X define(X,x) X define(A1,1 2 3 4) A1 MAC $ macros <test1
$ macros <test1 & 27221Write down the process id (pid).
debug>prompt is displayed.
debug> grab 27221 New program macros (process p1) grabbed HALTED p1 [malloc in malloc.c] 0x800187c1 (malloc+256:) popl %ecx
debug> stack Stack Trace for p1, Program macros *[0] malloc(0xc) [0x800187c1] [1] tokenize(buf="(TEST,this is a test)\n") [main.c@62] [2] gettok(read_more=1) [main.c@167] [3] main(argc=1, argv=0x80479cc, 0x80479d4) [main.c@333] [4] _start() [0x80485a2]The most recently invoked function is always displayed at the top of the stack trace. Here, the process is in malloc(3), which was called by tokenize, called in turn by gettok, and so on. Since the point at which the process was suspended is random, when you try this, the process may be suspended in a different function, and your stack trace would not look exactly like this, but since the program is in a tight loop it should be similar. The program could conceivably be stuck in malloc, but it isn't likely because library functions, particularly frequently used ones like malloc, tend to be well tested. Since macros hasn't been tested at all, you can assume the bug is in the code for macros. Finding the bug is an iterative process and may take several tries.
You would like to let the program run until it is finished in malloc but before it does anything else in tokenize. But you are unsure of what option to use with the run command.
debug> help run
run - set thread or process running.SYNOPSIS run [-p proclist] [-bfr] [-u location]
DESCRIPTION Set the specified list of threads and processes (%thread or %proc by default) running. The -f option specifies that the debugger will run the thread or process in the foreground, i.e., it will wait until the threads or processes stop before returning control to the user. The -b option specifies background execution (no waiting). Control returns immediately to the user, and the thread or process is started.
The -r option causes the thread or process to run until the current function returns. The -u option specifies a "location" to run to.
Note that threads that are not currently running on a kernel light-weight process cannot be set running (such threads show up in the "Off LWP" state in the ps command).
EXAMPLES run -p all -b run -r run -u 108
SEE ALSO %wait, location, proclist, ps, step, thread
run -r
to get out of malloc (-r stands for run to return). (If you grabbed the process while it was still in tokenize you shouldn't do the next step. If the program is suspended in something malloc calls, you will have to do it more than once.)
debug> run -r HALTED p1 [tokenize in main.c] 62: token = (struct token *)malloc(sizeof(struct token));
debug> list 62: token = (struct token *)malloc(sizeof(struct token)); 63: if (!token) 64: { 65: (void) fprintf(stderr, "malloc failed!\n"); 66: exit(1); 67: } 68: token->string = 0; 69: token->next = 0; 70: 71: if (!head)If the program is really looping forever the odds are good that if you let the program run it will come back to this point.
debug> stop 62 EVENT [1] assignedTo be on the safe side you can also define a stop event at the beginning of the function. This will let you know if it leaves tokenize and comes back, which will suggest that the problem is in not in tokenize, but in gettok instead.
debug> stop tokenize EVENT [2] assigned
debug> symbols -tv Symbols for p1, Program macros Name Location Line Type Value buf tokenize 52 const char * "(TEST,this is a test)\n" c tokenize 52 char 40 head tokenize 52 struct token * 0x804a880 len tokenize 52 unsigned int 7 next tokenize 52 const char * "(TEST,this is a test)\n" save_string tokenize 52 label 0x804881c tail tokenize 52 struct token * 0x81c3784 token tokenize 52 struct token * 0x81c3784
debug> run STOP EVENT TRIGGERED: 62 in p1 [tokenize in main.c] 62: token = (struct token *)malloc(sizeof(struct token));
debug> symbols -tv Name Location Line Type Value buf tokenize 52 const char * "(TEST,this is a test)\n" c tokenize 52 char 40 head tokenize 52 struct token * 0x804a880 len tokenize 52 unsigned int 7 next tokenize 52 const char * "(TEST,this is a test)\n" save_string tokenize 52 label 0x804881c tail tokenize 52 struct token * 0x81c3798 token tokenize 52 struct token * 0x81c3798
debug> list tokenize 52: { 53: struct token *head = 0; 54: struct token *tail = 0; 55: struct token *token; 56: const char *next = buf; 57: char c; 58: size_t len; 59: 60: while ((c = *next) != '\0') 61: {
debug> kill p1 killed No more processes. debug> quit $
In addition to the grab command, you can grab a process directly from the debug command line, or by using its pathname in the /proc file system, so these examples are all equivalent:
$ debug /proc/27221 . . . $ debug 27221 . . . $ debug debug> grab /proc/27221 . . . $ debug debug> grab 27221Of course, the normal security rules apply; you cannot get control over a process with a different effective user id from your own.
The debugger prints a message showing you the name of the program (usually the name of the object file) and a debugger-generated name for the process. The debugger maintains a hierarchy of program, process and thread; programs are at the top and each program is made up of one or more processes, which in turn, are made up of one or more threads (if the program is multi-threaded). For now, you may assume that there is only one of each, and that the program name and process name are synonymous, but the debugger displays its information using the process name where the program name might be ambiguous. The significance of the hierarchy becomes more apparent when debugging pipelines and the effects of fork(S), exec(S) and thr_create(3thread). (See ``Multi-process debugging''.)
By using grab and create (described later) you can debug different programs without having to exit and restart the debugger.
There are several things to note about stack traces:
main(int argc, char **argv) { ...but three parameters are actually passed to main [the third is the environment pointer -- see exec(S)], so debug prints the third argument without a name. You will also see this with functions that take a variable number of arguments; the arguments referenced with va_arg (see stdarg(5)) will not have names.
debug> stack -c 3 Stack Trace for p1, Program ackerman *[0] ack(m=0, n=5) [b1.c@11] [1] ack(m=1, n=4) [b1.c@18] [2] ack(m=1, n=5) [b1.c@18]
debug> stack Stack Trace for p1, Program sget *[0] main(presumed: 0x1, 0x8047dd4, 0x8047ddc) [0x8300950] [1] _start() [0x8300662]The presumed: indicates that the debugger does not have enough information to know how many arguments were pushed onto the stack for that function. In that case, it prints out three entries on the stack, guessing that they might be arguments.
debug> stack Stack Trace for p1, Program a.out *[0] ?()This indicates that debug cannot make any sense out of the stack, and usually means that there is a bad pointer somewhere; either the program scribbled on the stack or it tried to follow a pointer into never-never land. About the best you can do here is start over again and try to find out where it was before it got lost.
Help is always at your fingertips. Typing help command_name will produce a synopsis of the command's syntax, a brief description of what the command does, and a list of other related topics. Examples of two of those other topics are sysnames and format:
debug> help sysnameswill list all the system call names that the syscall command accepts, and
debug> help formatwill produce a description of the print command's formatting capability. Typing help (or h) by itself will produce a list of all the topics you can get help on, including all the commands.
The symbols (or syms) command with no options will print out the names of the local variables that are visible from the current scope:
debug> symbols Symbols for p1, Program macros Name Location Line buf tokenize 52 c tokenize 52 head tokenize 52 len tokenize 52 next tokenize 52 save_string tokenize 52 tail tokenize 52 token tokenize 52You can restrict the symbols displayed by giving symbols a pattern [using sh(C) syntax for pattern matching]:
debug> symbols [b-h]?* Symbols for p1, Program macros Name Location Line buf tokenize 52 head tokenize 52Adding the -t or -v (or both) options will make debug print the types or values (or both):
debug> symbols -tv t* Symbols for p1, Program macros Name Location Line Type Value tail tokenize 52 struct token * 0x81c3798 token tokenize 52 struct token * 0x81c3798Other options to symbols make it display other groups of symbols. For example, the -f option (for file static) displays static variables visible from the current compilation unit:
debug> symbols -ft Symbols for p1, Program macros Name Location Line Type Usage main.c char * add_def main.c void(struct token *, int) gettok main.c struct token *(int) lines main.c int next_tok main.c struct token * process_def main.c void() tokenize main.c void(const char *)There are two things to note about the output from symbols:
debug> symbols -ftv t* Symbols for p1, Program macros Name Location Line Type Value tokenize main.c void(const char *) 0x80486ec
debug> symbols Symbols for p1, Program macros Name Location Line . . . name main 300 parens main 347 tail main 347 token main 300
t, r, l and b are shorthand notations for stack, run, list and stop, respectively. There are several other built-in shorthands (or aliases) available. You can create your own or redefine the built-ins with the alias command:
debug> alias lm list main debug> lm 300: { 301: struct token *definition; 302: struct token *name; 303: struct token *token; 304: struct arglist *arguments; 305: char *argptr; 306: int i; 307: 308: for (i = 1; i < argc; ++i) 309: {alias with no arguments will list all the aliases, both built-in and user-defined:
debug> alias b => stop exit => quit h => help history => fc -1 l => list lm => list main n => step -o next => step -o
ni => step -io p => print q => quit r => run rr => fc -e - s => step si => step -i sig => signal syms => symbols sys => syscall t => stackThe commands and their built-in aliases are interchangeable. This tutorial normally uses the complete command names for clarity.
debug also supports ksh-style command history and command-line editing. Both vi and emacs editing modes are supported, and, like ksh, debug determines the default editing modes from the VISUAL and EDITOR environment variables. For information on using this capability see ksh(C).