DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
Using the command line interface of debug

Session 8: which end of the pipe is broken?

  1. Change the test in line 70 of sget.c to !=:
       while(fgets(buffer, BUFSIZ, infile) != 0)
    

  2. Now when you compile and run sget, it appears to work correctly. When you compile and run walk, it also seems to work correctly by itself:
       $ cc -g -o walk walk.c
       $ walk src
       src/file1
       src/testfile
    

    However, when you put them together, something breaks:

       $ walk src | sget
    
    The debugger displays the following:
       Cannot read src/file1
    

  3. This time you will need to debug both programs at the same time. Type debug to start the debugger:
       $ debug
    

  4. Create a pipeline:
       debug> create walk src | sget
    
    The debugger displays the following:
       New program walk (process p1) created
       HALTED p1 [main in walk.c]
       47: int  exit_code=0;
       New program sget (process p2) created
       HALTED p2 [main in sget.c]
       132:  (void)signal(SIGHUP,handler);
    

  5. Set a breakpoint in walk so you can see what it writes to the pipe:
       debug> stop walk.c@37
    
    The debugger displays the following:
       EVENT [1] assigned
    

  6. You should also set breakpoints on copy and get in sget so you can see what happens there. When you created the pipeline, the first command (walk) became the current program. To set a breakpoint in sget, you will need to use the -p option to stop:
       debug> stop -p sget sget.c@copy
    
    The debugger displays the following:
       EVENT [2] assigned
       debug> stop -p sget sget.c@get
    
    The debugger displays the following:
       EVENT [3] assigned
    

  7. Next you can either run walk and then run sget, or you can run them both at the same time. sget won't do anything until walk writes to the pipe, but for this example its simpler to start it running and let it wait than to try to bounce back and forth between processes. Run it in the background (with -b for background):
       debug> run -b -p p2
    


    NOTE: See ``Process lists (-p)'' for more information.

    When you type run without a process list and without -b, it runs the current process and waits for it to stop, just as in all the previous debugging sessions:

       debug> run
    
    The debugger displays the following:
       STOP EVENT TRIGGERED: walk.c@37  in p1 [walk in walk.c]
       37:                   (void) printf("%s \n", fullpath);
    

    If you print the string in fullpath, it looks reasonable:

       debug> print fullpath
        "src/file1"
    
    The only thing that looks odd is the extra blank between the file name and the new-line in the call to printf. You will need to find out if this could be breaking sget.

  8. Check to see what happens in sget when it reads this line.
       debug> run
    
    The debugger displays the following:
       STOP EVENT TRIGGERED: sget.c@copy in p2 [copy in sget.c]
       82:                   if ((infile = fopen(path, "r")) == 0)
       STOP EVENT TRIGGERED: walk.c@37  in p1 [walk in walk.c]
       37:                   (void) printf("%s \n", fullpath);
    
    This time when you typed run you got two event notifications, one from each process, even though you only ran p1 (walk). p2 (sget) was already running, waiting for input from p1. When p1 ran, it wrote to the pipe, p2 read from the pipe, and reached the breakpoint. You can see from ps that both processes are now stopped:
       debug> ps
    
    The debugger displays the following:
    Program  ID  Pid   Thread State    Function  Location   Command
     sget    p2  2223      -1 stopped  copy      sget.c@82  sget
    *walk    p1  2221      -1 stopped  walk      walk.c@37  walk src
    

  9. Since you want to examine sget, make it the current program, and you won't have to keep typing -p:
       debug> set %program = sget
       Current process is now p2, program sget
    
    (Setting %proc to p2 would also have worked.) When you do a stack trace, it will show you the stack for sget:
       debug> stack
       Stack Trace for p2, Program sget
       *[0] copy(path="src/file1 ", newfile="file1 ") [sget.c@82]
        [1] main(0x1,0x80479a4, 0x80479ac)            [sget.c@155]
        [2] _start()    [0x8048662]
    

    Of course, you can still look at anything in walk with -p:

       debug> stack -p walk
       Stack Trace for p2, Program walk
       *[0] walk(path="src", dir=0x8049fb0)        [walk.c@37]
        [1] main(argc=2, argv=0x804799c, 0x80479a8) [walk.c@63]
        [2] _start()    [0x804856e]
    

    You can see from the stack trace that the variable path looks just like the line walk wrote (minus the new-line). The program stopped right before a call to fopen:

       debug> list -c 3
       82:  if ((infile = fopen(path, "r")) == 0)
       83:  {
       84:     (void) fprintf(stderr, "Cannot read %s\n", path);
    

  10. Test fopen:
       debug> step
       STEPPED p2 [copy in sget.c]
       84:     (void) fprintf(stderr, "Cannot read %s\n", path);
    
    That trailing blank seems more suspicious. You need to test sget after deleting this blank. You can test it quickly without editing and recompiling as follows:

    1. Delete the blank:
         debug> set path[9] = '\0'
         debug> print path
         "src/file1"
      

    2. Jump back to line 82, before the call to fopen and rerun the program. You will need to step the program twice; the first time it will stop at the breakpoint at the beginning of the line:
      debug> jump 82
      debug> step
      STOP EVENT TRIGGERED: sget.c@copy in p2 [copy in sget.c]
      82:     if ((infile = fopen(path, "r")) == 0)
      debug> step
      STEPPED p2 [copy in sget.c]
      87:     (void) remove(newfile);
      


    NOTE: See ``Jumping around in the program'' for more information.

  11. Exit the debugger.
Since the program didn't fall into the error-handling code this time, that extra blank must really be the problem.

Jumping around in the program

The jump command lets you change the program counter. The next time you let the program run or step it will start at the new location, not from the point where it stopped last.

jump can occasionally be useful, but it is also dangerous, and should be used with extreme caution. You can jump to any location in your program, but you are asking for trouble if you jump out of your current function; debug will not attempt to fix up the stack. Even jumping to another location within one function can be dangerous; you may not be aware of all the side affects of code you jump over, and jumping into or out of blocks may leave your local variables in a funny state. This is especially true in debugging C++ code; it is easy to forget which variables invoke constructors and destructors.

Process lists (-p)

Most of the debugger's commands take an option (-p) to specify a process list. With a process list, you can make the command apply to any or all of the controlled objects, without having to constantly set %proc or %thread. A process list may include:

1. process identifier

(including %proc). The process identifier may be the debugger's name (p1, p2, etc.) or the system level process id.

If the process contains more than one thread, the command will be applied to each thread in the process.

      debug> ps
      Program  ID   Pid Thread State   Function  Location    Command
       sget    p2  2223    -1 running                        sget
      *walk    p1  2221    -1 stopped            0x8000afe0  walk src
      debug> stop -p 2223 main {print base_name}
      EVENT [2] assigned

2. Thread identifier

This may be either the debugger variable %thread or an identifier of the form pn.n (p1.1, p1.2, p2.1).

3. Program name

(including %program). The command will be applied to each process and thread making up that program.

   debug> stop -p sget sget.c@82
   EVENT [3] assigned

4. Comma-separated list

A list containing any combination of process names, thread names and program names. The command will be applied to each entry in the list in turn.

   debug> events -p %proc,sget
   Events for p1, program walk
     ID    Type       Count Condition         Command List
   [0001]  ONSTOP                             stack
   Events for p2, program sget
     ID    Type       Count Condition         Command List
   [0002]  STOP           0 main              {  print basen...
   [0003]  STOP           0 sget.c@82

5. all

all objects under debugger control.

   debug> events -p all
   Events for p2, program sget
     ID    Type       Count Condition         Command List
   [0002]  STOP           0 main              {  print basen...
   [0003]  STOP           0 sget.c@82              
   Events for p1, program walk
     ID    Type       Count Condition         Command List
   [0001]  ONSTOP                             stack
If you don't give a command a process list, the command (with a few exceptions) will apply to the current object only. The exceptions are the commands that create events; these apply to the current program, not process or thread. If you have two processes forked from one program and create an event, that event will apply to
both processes. If you want the event to apply to only one process or thread, you will need to use a process list in the event command:
   debug> ps
   Program    ID    Pid State    Function   Location    Command
   *a.out     p1   2316 Stopped  _fork      0x8001bc49  a.out
   a.out      p2   2319 Stopped  _fork      0x8001bc49  a.out
   debug> stop fprintf
   EVENT [1] assigned
   debug> stop -p p2 open
   EVENT [2] assigned
   debug> stop -p p1,p2
   Events for p1, program a.out
      ID     Type       Count Condition       Command List
   [0001]    STOP           0 fprintf                    
   Events for p2, program a.out
      ID     Type       Count Condition       Command List
   [0001]    STOP           0 fprintf                    
   [0002]    STOP           0 open
If you re-create the program, the old events that applied to the entire program will be reset in the new program, but events that applied to only one process will not be reset. The same is true when a process forks; events applied to the program will be copied, events applied only to the parent process or one of its threads will not. (Of course, if the process calls exec(S), none of the existing events apply to the new program.) If the process creates a new thread, events that apply to the entire process or program will be copied in the new thread. Events that apply only to a single thread will not be copied.

Foreground vs. background

Foreground and background processes in the debugger are similar to foreground and background processes at the shell level. If you run a process or thread in the foreground, debug waits until the process stops before prompting you for another command. If you run several processes or threads at once (say with run -p all), all of them must stop before you will get another prompt. If you start one or more processes running in the background, debug won't wait for any of them to stop before giving you a prompt. When anything running in the background stops, debug will let you know, but won't give you a prompt if you have something else running in the foreground.

debug's default behavior is to run everything in the foreground. You may change the default by setting the debugger variable %wait to background (the values no and 0 also mean background mode):

      debug> print %wait
      "foreground"
      debug> set %wait no
      debug> run -p p2
      debug> ps
      Program   ID     Pid Thread State   Function  Location   Command
       sget     p2   2223      -1 running                      sget
      *walk     p1   2221      -1 stopped           0x800afe0  walk src
Setting %wait back to foreground (or yes or 1) will restore the normal behavior. Whichever mode you are in, you can override the default for individual run and step commands with the -f or -b options (foreground and background, respectively).

Foreground mode is most useful when you are debugging only one object. Background mode is more useful when you are debugging several objects. If you know you will be debugging multiple objects, you may want to set %wait to background when you start out.

If a process is doing something unexpected in foreground mode, hit interrupt to suspend the process and regain control. Hitting interrupt won't do anything to a process running in the background. To stop a background process, use the halt command:

   debug> halt -p p2
   debug>
   SUSPENSED p2 [_read]
   0x8000ef6b (_read+12:)    jae   +0x0a <8000ef77> [ noerror ]

Next topic: Debugging without -g
Previous topic: Releasing or ignoring processes and threads

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005