Using the command line interface of debug

Session 6: why did the exec'd program fail?

  1. Fix the bug in Session 5 by removing the declaration of base_name in main.

  2. Recompile sget. This time when you run it, sget works as expected on file1, but not on s.main.c:
       $ sget <files
       file1: 0 characters, 0 words, 0 lines
       Cannot read main.c
    Since 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.

  3. Type debug to start the debugger. Run the program:
       debug> create sget <files
    debug 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 controlled
    You now have one program (sget) with two processes, both stopped in fork.

  4. Use the ps command to check:
       debug> ps
    The 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

    NOTE: See ``ps and the current object'' for more information.

    The asterisk (``*'') before the program name shows you that p1 is the current process, but p2 (the child) is the one that invokes get.

  5. Make p2 the current process:
       debug> set %proc = p2
       Current process is now p2, program sget
    Now, when you type run, p2 will run until it execs get:
       debug> run
    The 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     $0x0
    It 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> ps
    The 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

  6. Look at the code that does the exec. Since the current process is p2, you must tell debug to look in p1's symbol table for the function get; you can do that by qualifying the location with the process name:
       debug> l p1@sget.c@get
    The debugger displays the following:
       44:  {
       45:    FILE    *infile;
       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");

    NOTE: See ``Fully qualified names'' for more information.

    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(2) 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.)

  7. Exit the debugger.

NOTE: See fork(2), exec(2), and system(3S) for more information.

ps and the current object

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:

Along with the hierarchy, the idea of the current object (the current process in the current program) becomes vital. You can see what the current object is with ps or by printing %proc:
   debug> print %proc
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
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.

Fully qualified names

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
The syntax for a fully qualified name is:


There are several things to note about this syntax:

In addition to the location specifiers shown above, there are two other forms that are used only for accessing local variables in expressions:


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@infile
The 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

NOTE: If you use a line number to denote an inner block, you must also provide the file name or function name so the line number will not be taken as a frame number.

fork(2), exec(2), and system(3S)

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(3S) is related to fork(2) and exec(2). 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 sh
which forks:
   debug> run
   Process p2 forked; new process p3 now controlled
   debug> set %proc = p3
   Current process is now p3, program sh
and execs the program you want to debug:
   debug> run
   Warning: No debugging information in get
   Process p3 exec'd; new executable is get
There are now three processes under debugger control:
   debug> ps
The 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

Next topic: Session 7: can I ignore a forked process?
Previous topic: Multi-process debugging

© 2004 The SCO Group, Inc. All rights reserved.
UnixWare 7 Release 7.1.4 - 27 April 2004