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

Session 3: where did that data structure go?

  1. Fix the bug in Session 2 by inserting the following after line 37 in macro.c:
       if (!root)
       {
               root = defn;
               return;
       }
    

  2. Recompile and try again.
       $ macros -DMAC=123 <test1
       

    this is a test

    this is a test test this is a test Warning, X redefined

    x A1 123 $

    This time, it runs to completion, and for the most part gives the expected results, but what happened to the definition of A1? (A1 should have been replaced with 1 2 3 4.)

  3. Run the program in the debugger. Get the program ready to run with the create command:
       $ debug
       debug> create macros -DMAC=123 <test1 >test.out
       New program macros (process p1) created
       HALTED p1 [main in main.c]
       308:            for (i = 1; i < argc; ++i)
    

  4. One way to start looking for the bug would be to make sure that lookup is really being called to search for the macro A1. (This will at least tell you if the tokenization is correct.) Before starting the program running, tell it to stop in lookup:
       debug> stop lookup
       EVENT [1] assigned
       debug> run
       STOP EVENT TRIGGERED: lookup  in p1 [lookup in macro.c]
       86:                node = root;
    

  5. The program has hit the stop event in lookup. Check to see what name it is looking for:
       debug> print name->string
       "TEST"
    
    It may have to call lookup several times before it calls it with A1; typing print name->string and run every time it gets to lookup could get tedious quickly. You can tell debug to do that automatically whenever the stop event triggers (the program hits the breakpoint). You can also create a debugger variable to track the number of times lookup is called:
       debug> set $ncalls = 1
       debug> onstop { print name->string, ++$ncalls; run }
       EVENT [2] assigned
    


    NOTE: See ``Debugger variables'' for more information.

  6. You also don't really need the event notification, because with only one breakpoint set it will stop at the same place each time. Setting the debugger variable %verbose to quiet suppresses the debug notifier. Setting it to
    source would make it suppress the notification but still print the source line:
       debug> set %verbose = quiet
    

  7. Run the program:
       debug> run
       "this" 2
       "is" 3
       "a" 4
       "test" 5
       "X" 6
       "TEST" 7
       "this" 8
       "is" 9
       "a" 10
       "test" 11
       "test" 12
       "TEST" 13
       "this" 14
       "is" 15
       "a" 16
       "test" 17
       Warning, X redefined
       "X" 18
       "x" 19
       "A1" 20
       "MAC" 21
       Process p1 has exited
       No more processes.
    

  8. lookup was called for A1. Take a closer look at what's happening in lookup. Since the program ran to completion you need to start it up again with create:
       debug> create
       macros -DMAC=123 <test1 >test.out
       New program macros (process p2) created
       HALTED p1 [main in main.c]
       308:            for (i = 1; i < argc; ++i)
    

  9. Reset %verbose to get the normal event notification messages:
       debug> set %verbose = all
    
    The events that you created the first time through are still there, as you can see with the events command:
       debug> events
    
    The debugger displays the following:
       Events for p2, program macros
       ID    Type       Count Condition      Command List
       [0001]  STOP           0 lookup
       [0002]  ONSTOP                       { print name->st...
    
    You don't need those events this time, so use delete -a to delete them:
       debug> delete -a
    


    NOTE: See ``Events and command lists'' for more information.

  10. The search for A1 was on the twentieth call. Create a stop event that will take you there directly, then run the program:
       debug> stop -c 20 lookup
       EVENT [3] assigned
       debug> run
       Warning, X redefined
       STOP EVENT TRIGGERED: lookup  in p2 [lookup in macro.c]
       86:             node = root;
    


    NOTE: See ``Skipping the first n events'' for more information.

  11. Make sure this is really the right call:
       debug> print *name
       {
           string="A1"
           type=WORD (or 0)
           next=0x0
       }
    

  12. Single step through lookup to see what's going on:
       debug> step
    
    The debugger displays the following:
       STEPPED p2 [lookup in macro.c]
       87:                while (node)
    
    Check node:
       debug> print *node
    
    The debugger displays the following:
       {
             name=0x804abe0
             definition=0x804ac00
             left=0x0
             right=0x804af24
       }
    
    Check node->name:
       debug> print *node->name
    
    The debugger displays the following:
       {
             string="MAC"
             type=WORD (or 0)
             next=0x0
       }
       

    debug> step STEPPED p2 [lookup in macro.c] 96: } debug> step STEPPED p2 [lookup in macro.c] 89: val = strcmp(name->string, node->name->string);

  13. You would like to see what value strcmp returns, but you can't do it here; the step put you at the beginning of the line, before the call to strcmp takes place. Step to the next line, and then you can see what it returned:
       debug> step
       STEPPED p2 function [lookup in macro.c]
       90:                        if (val < 0)
       debug> print val
       -1
    

  14. A return value of -1 means the first string comes before the second string alphabetically, so the definition of A1 should be on the left branch of the tree, but on looking back at the value of node, you can see that node->left is zero. If you let the program go a little further you will see that if the left branch is empty, it stops looking and returns 0 (meaning it couldn't find A1).
       debug> step -c 3
       STEPPED p2 function [lookup in macro.c]
       91:                                node = node->left;
       STEPPED p2 function [lookup in macro.c]
       96:                }
       STEPPED p2 function [lookup in macro.c]
       97:                return 0;
    
    The problem is apparently further back in insert, when it was building the tree.

  15. Start over again and get rid of the stop event on lookup, and add a new one on the fifth call to insert (A1 is the fifth macro definition, including the one on the command line).
       debug> create
       p2 killed
       macros -DMAC=123 <test1 >test.out
       HALTED p2 [main in main.c]
       308:            for (i = 1; i < argc; ++i)
       debug> delete 3
       debug> stop -c 5 insert
       EVENT [4] assigned
    

  16. Run the program. When it stops in insert, make sure it is in the right call:
       debug> run
       STOP EVENT TRIGGERED: insert  in p3 [insert in macro.c]
       25:                int                     val=0;
       debug> print name->string
       "A1"
    

  17. If you look at the source for insert, you will see that nothing happens to the tree until it gets into the for loop. Let it run up to that point. (run -u 44 stands for run until location 44). Then you can single step as it walks through the definition tree:
       debug> run -u 44
       HALTED p3 [insert in macro.c]
       44:                node = root;
       debug> step
       STEPPED p3 [insert in macro.c]
       47:    val = node ? strcmp(name->string, node->name->string) : 0;
       debug> step
       STEPPED p3 [insert in macro.c]
       48:                if (val < 0)
    


    NOTE: See ``Variations on run and step'' for more information.

    The value returned by strcmp should be -1, and it should go off down the left branch, just as in lookup:

       debug> print val
       -1
       debug> step
       STEPPED p3 [insert in macro.c]
       50:                 if (!node->left)
       debug> step
       STEPPED p3 [insert in macro.c]
       52:                 node->right = defn;
    
The program is making the assignment to the right branch, which explains why it couldn't find A1 later by looking down the left branch. This time when you exit the debugger, you do not need to kill the process you created as you did the one you grabbed in Session 1. Although the debugger's normal behavior when you exit is to release any processes that are grabbed, all processes created during the debugging session are killed.

Create

create accepts a command line, rather than just a simple program name, so you can give the program arguments, redirect I/O, and even create pipes. create gets the program ready to run, but does not actually start it running; run or step set the program in motion. Like grabbing a process, you can also create a program directly from the command line:

   $ debug macros -DMAC=123 \<test1 \>test.out
   New program macros (process p1) created
   HALTED p1 [main in main.c]
   308:            for (i = 1; i < argc; ++i)
You must be careful to escape or quote I/O redirection operators; otherwise the shell will see them and redirect debug's I/O instead. You can also pass options to the controlled program from the command line; debug handles any options that come before the program name, but any that come after it are passed on to the program unchanged.

create without any arguments will re-create the last command line you gave it and echo the command line so you can check it:

   debug> create
   macros -DMAC=123 <test1 >test.out
   New program macros (process p2) created
   HALTED p2 [main in main.c]
   308:            for (i = 1; i < argc; ++i)
If the program from the last create is still around, debug will kill it before creating the new one. Any events already defined will still apply.

By default, create suspends the program in main; but you can tell it to stop at a different location by using the -l option.

Debugger variables

Debugger variables are special variables that are maintained by the debugger instead of by the program being debugged. All debugger variables start with either a percent sign (%) or a dollar sign ($) so they won't be confused with program variables, but can be used in print commands and as arguments to several other commands just like program variables.

The debugger variables that start with % are built-in variables that describe the current debugger state and allow you to customize certain aspects of debug's behavior. You can change the value of some of these debugger variables (like %verbose or %frame) directly with set but others (including %file and %line) are read-only. They are set by debug when an event triggers or indirectly by setting certain other variables. You can examine their values but you cannot set them directly. symbols -dv (-d for debugger variables) will show you the current values of the built-in debugger variables.


NOTE: See ``Debugger variable description'' for more information.

The debugger variables that start with $ are user-defined variables. These variables are similar to shell variables. When you start up debug, the shell variables currently exported in your environment will make up the initial set of user-defined variables in the debugger. symbols -uv (-u for user) will show you the
current values of the user-defined variables. User-defined variables are useful in several different ways:

Events and command lists

Setting a breakpoint is just one way to create an event. There are five commands that define different types of events:


There are several similarities between these five commands: You can display all the events at once with the events command:
   debug> events
   Events for p1, program macros
     ID     Type     Count Condition     Command List
   [0001]   STOP        0 lookup
   [0002]   ONSTOP                     { print name->st...
Normally, debug prints only the beginning of the associated command list. Typing events event_num will force it to display the entire command list:
   debug> events 2
   Events for p1, program macros
     ID     Type     Count Condition
   [0002]   ONSTOP
            Object list: p1
            Command list: { print name->string,++$ncalls ; run }
The event numbers are also used as arguments to several other commands: delete, disable, enable. delete gets rid of an event permanently:
   debug> delete 2
   debug> events
   Events for p1, program macros
     ID     Type     Count Condition     Command List
   [0001]   STOP         0 lookup
disable does not get rid of the event but the event is ignored until you turn it on again with enable. Disabled events are indicated with a D following the event id:
   debug> disable 1
   debug> events
   Events for p1, program macros
     ID     Type     Count Condition     Command List
   [0001] D STOP         0 lookup

When an event triggers, debug will

  1. Stop the process or thread

  2. Tell you where it stopped and what type of event happened (if %verbose is events, reason or all)

  3. Print the source line currently being executed (if %verbose is not quiet)

  4. Execute the commands (if any) associated with the event that triggered

  5. Execute the commands (if any) from onstop events.

In this example, there is a command (stack) associated with the stop event and an onstop event. When the process hits the breakpoint debug will do both commands:

   debug> stop lookup stack
   EVENT [1] assigned
   debug> onstop print name->string
   EVENT [2] assigned
   debug> run
   STOP EVENT TRIGGERED: lookup in p1 [lookup in macro.c]
   86:                node = root;
   Stack Trace for p1, Program macros
   *[0] lookup(name=0x804a980)                  [macro.c@86]
    [1] main(argc=2, argv=0x804797c, 0x8047988) [main.c@342]
    [2] _start()    [0x80485a2]
   "TEST"

Skipping the first n events

Both stop and syscall take a -c n (-c for count) option to make debug continue running the process until the event occurs the n-th time. In Session 3, you created a stop event with the command stop -c 20 lookup. This resulted in the process going the first 19 times it got to lookup. If you let the program in Session 3 run again, however, it will stop again the next time it gets to lookup, not after skipping another 19 calls. This option was based on an assumption about the way people tend to use debuggers: that you would use the count option to skip over many preliminary loop iterations or function calls, but once you get to where you think the bug is, you will probably want to examine that iteration and the next (and so on) intensively.

However, if you want to skip a number of events again, you can reset the count with the change command:

   change event_number -c count
For more on the change command, see ``Change Command'' under ``Technical tips''.

Variations on run and step

run and step are the two ways to get from one place to another in your program. run lets the program execute normally until an event triggers or until the program exits. step provides more control by executing one statement and stopping. There are several variations on the basic run and step commands:

In all these variations, any event may trigger and stop the process anyway. For example, if you set a breakpoint in gettok, even if you used step -o to step through main, the process will stop when it reaches the breakpoint:
   debug> step
   STEPPED p1 [main in main.c]
   333:                token = gettok(1);
   debug> stop 173
   EVENT [1] assigned
   debug> step -o
   STOP EVENT TRIGGERED: 168 in p1 [gettok in main.c]
   173:                token = next_tok;
You can also force the program to stop by hitting interrupt; that will suspend the process (possibly in the middle of a statement or library function) and give you a debugger prompt.
Next topic: Session 4: who stepped on my pointer?
Previous topic: Examining the source file

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