|
|
$ sget <files file1: 0 characters, 0 words, 0 lines Cannot read main.cSince s.main.c is an SCCS file, sget should be calling get; perhaps it isn't, or is invoking it with bad arguments. With debug, you can easily see if that is the case.
debug> create sget <filesdebug will stop the process and let you know when (and if) it forks:
New program sget (process p1) created HALTED p1 [main in sget.c] 133: (void) signal(SIGHUP, handler); debug> run file1: 0 characters, 0 words, 0 lines Process p1 forked; new process p2 now controlledYou now have one program (sget) with two processes, both stopped in fork.
debug> psThe debugger displays the following:
Program ID Pid Thread State Function Location Command *sget p1 19752 -1 Stopped _fork 0x8001bc49 sget <files sget p2 19754 -1 Stopped _fork 0x8001bc49 sget <files
The asterisk (``*'') before the program name shows you that p1 is the current process, but p2 (the child) is the one that invokes get.
debug> set %proc = p2 Current process is now p2, program sgetNow, when you type run, p2 will run until it execs get:
debug> runThe debugger displays the following:
Warning: No debugging information in src/s.main.c Warning: No entry "main" exists Process p2 exec'd; new executable is src/s.main.c, program s.main.c HALTED p2 0x80494b0 (..............) pushl $0x0It should have exec'd get; src/s.main.c is unorthodox, but you will see src/s.main.c if you do a ps also:
debug> psThe debugger displays the following:
Program ID Pid Thread State Function Location Command *s.main.c p2 19754 -1 Stopped 0x8000afe0 src/s.main.c sget p1 19752 -1 Stopped _fork 0x8001bc49 sget <files
debug> l p1@sget.c@getThe debugger displays the following:
44: { 45: FILE *infile; 46: 47: if (fork() == 0) /* in the child */ 48: { 49: /* suppress get's output */ 50: (void) freopen("/dev/null", "w", stdout); 51: (void) freopen("/dev/null", "w", stderr); 52: (void) execl("/usr/ccs/bin/get", path, 0); 53: (void) fprintf(stderr, "exec of get failed\n");
At first glance the code looks reasonable; the first argument in the call to execl is the pathname of get and the second argument is the pathname read from the input, but if you look at the exec(S) manpage it explains that the second argument is what is passed to the program as argv[0], and should be the name of the program. The second argument should have been ``"get"'', and the third should have been "src/s.main.c". The program ran because execl only uses the first argument with the full pathname to find the program. get doesn't look at argv[0] -- it assumes its name is correct -- and printed an error message only because argv[1] was missing.)
When debugging
multi-threaded processes or
two or more processes or programs at the same time,
the
program/process/thread
hierarchy becomes more important.
After the fork you have one program with two processes;
after the child process (the process with the higher number, p2)
execs get,
you have two programs,
each with one process.
ps will show you the hierarchical arrangement
and current
state of all processes
and threads
under the debugger's control.
The information it displays includes:
debug> print %proc "p2"For a multi-threaded process, the current object is the current thread in the current process. You can see what the current thread is by printing %thread.
debug> print %thread "p2.3"If process p2 is the current process but is not multi-threaded, %thread will contain a null string.
When you type a command the debugger applies the command to the current object unless you give it a process list. For example, when you type run, debug will run the current object only. You don't need to think about the current object when there is only one object. However, if you type run right after the fork -- without changing the current process -- it would not do what you wanted. debug would let the parent process run, but all it does is wait for the child to finish. Since the child isn't running, it will wait forever. If you do this by mistake, hitting interrupt will suspend the process and give you a debugger prompt, so you can recover.
You may change the current object by setting %program, %proc or %thread. If you set %program, debug will randomly choose one of the program's processes for %proc and one of the new process' threads (if the process is multi-threaded); if you set %proc, debug will change %program if necessary and reset %thread to a random thread in the new process (if the process is multi-threaded). Setting %thread resets %proc (and indirectly, %program) if the new thread is in a different process. There will always be a current object as long as there is at least one process being debugged. If your current process dies, debug will pick a random process from the same program (or from a different program if that was the last process in that program) for the new current object. Changing the current object will also cause the context variables such as %frame and %file, to be set to the appropriate values for the new object.
The more objects you are debugging at any one time, the greater the potential for confusion about what's current or about some process's state. If in doubt, use ps.
A function or line number specifier that includes the process id and file name is considered fully qualified because it includes all the information the debugger needs to find that location. Besides list, you can use qualified names with several other commands, including stop, run, jump, and dis. You may also use qualified names in expressions to access symbols that are not directly visible, like a static variable in another process:
debug> print p1@sget.c@path "src/s.main.c"The syntax for a fully qualified name is:
There are several things to note about this syntax:
[thread_id@]object_name@[compilation_unit@]identifierFor example:
debug> print p1@"libc.so.1"@"atexit.c"@numexitfns
debug> list macro.h@6 6: struct token 7: { 8: char *string; 9: enum toktype type; 10: struct token *next; 11: }; . . .header_file may also be used to find a static function or object defined within a header file. In either case, if the debugger can't find the header from the current context, it will ask if you want it to search through all the compilation units making up your program until it finds the header file. It will stop the search when it finds any file where the header file was included, unless you are creating a stop event on a static function, where it will ask if you want to set breakpoint on all instances of the function.
Expressions are used by the print, set, whatis, if, and while commands. In an expression, the frame number may be used to access any variable in any function on the stack. For example:
debug> print p1@2@infileThe line number is used only if you are trying to access a symbol defined in a block within a function; the inner block is denoted by a line number within the range covered by the block in the source file:
debug> print main.c@369@parens
Since forks and execs are major changes in the state of the debugging session, debug will suspend the processes involved and inform you when one of those takes place.
The library function
system(S)
is related to
fork(S)
and
exec(S).
It is easier to use but slightly more complicated to debug.
system invokes the shell, which in turn invokes the specified program.
If you want to debug a program invoked
through a call to system,
you will have to go through an extra level of forks and execs to get past the shell.
The first exec invokes the shell:
debug> run Warning: No debugging information in sh Process p2 exec'd; new executable is shwhich forks:
debug> run Process p2 forked; new process p3 now controlled debug> set %proc = p3 Current process is now p3, program shand execs the program you want to debug:
debug> run Warning: No debugging information in get Process p3 exec'd; new executable is getThere are now three processes under debugger control:
debug> psThe debugger displays the following:
Program ID Pid Thread State Function Location Command *get p3 20656 -1 stopped 0x8000afe0 get s.main.c sget p1 20652 -1 stopped _fork 0x800545b2 sget sh p2 20654 -1 stopped 0x8001bc49 sh -c get s.main.c