Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Debugging in the Command Line with dbx


SysAdminMag.com

Debugging in the Command Line with dbx

Arnaud Aubert

Feeling comfortable with the debugger you use is absolutely essential for any developer. Most developers are familiar with the basic principles: step-by-step execution, inspecting variables, setting breakpoints, etc., but problems may arise when facing a command-line debugger for the first time. If you are used to working with integrated development environments, you may need some help to start working with dbx.

The dbx debugger was initially developed at UC Berkeley and is now provided on AIX and Solaris. Sun also provides an implementation for Linux included in Sun Studio. In this article, I will use the source code example shown in Listing 1 to provide an introduction to dbx. The example uses POSIX threads to show how multithreaded debugging can be done.

Debugging the First Program

The debugger will need your compiler to include debugging information to work correctly. Usually, adding -g to the parameters of your compiler is enough to do so. To start debugging, simply run dbx and type debug demodbx. This will load the process image without starting the software.

Controlling Breakpoints

Once the debugger has been started, you can run it using the run command. You can then provide command-line arguments and redirect the standard streams. Before typing run, you should specify at what point the program should stop during execution. This can be done using the stop command, which uses “at” to specify a line number or “in” a function name. Breakpoint expressions can be really complex and include Boolean conditions; you should read dbx’s docs to discover how powerful these can be. For example, you can set a breakpoint in the function incrementCounter used only if iCounter is greater than 50:

# dbx
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' \
  in your .dbxrc
(dbx) debug demodbx
Reading demodbx
Reading ld.so.1
Reading libpthread.so.1
Reading libthread.so.1
Reading libc.so.1
(dbx) stop in incrementCounter -if iCounter > 50
(2) stop in incrementCounter -if iCounter > 50
(dbx) status
 (2) stop in incrementCounter -if iCounter > 50
(dbx) run
iCounter=1
iCounter=2
....
iCounter=50
iCounter=51
t@2 (l@2) stopped in incrementCounter at line 16 in file "demodbx.c"
   16           pthread_mutex_lock(&mutex);
Browsing the Call Stack and Inspecting/Changing Variables The program has now been stopped in the second thread. You can analyze the value of the local and global variables using the print var command. You can even change the value using assign “var”=”value” as follows:
(dbx) print iCounter
iCounter = 51
(dbx) assign iCounter=90
(dbx) print iCounter
iCounter = 90
(dbx) print arg
dbx: "arg" is not defined in the scope `demodbx`demodbx.c`incrementCounter`
dbx: see `help scope' for details
The arg variable cannot be viewed, because it is not accessible from within the scope context of the debugger. However, you can ask for its value by switching the debugger’s context to the caller with the up and down commands on the call stack. You can check the current context (specified with the “=>” characters) by typing where to view the call stack.
(dbx) where
current thread: t@2
=>[1] incrementCounter(), line 16 in "demodbx.c"
  [2] myThread(arg = (nil)), line 30 in "demodbx.c"
  [3] _thr_setup(0xcfea2400), at 0xcff4f93e
  [4] _lwp_start(), at 0xcff4fc20
(dbx) up
Current function is myThread
   30                   incrementCounter();
(dbx) print arg
arg = (nil)
(dbx) where
current thread: t@2
  [1] incrementCounter(), line 16 in "demodbx.c"
=>[2] myThread(arg = (nil)), line 30 in "demodbx.c"
  [3] _thr_setup(0xcfea2400), at 0xcff4f93e
  [4] _lwp_start(), at 0xcff4fc20
The where command is used to see the call stack of the current thread, but you may switch the debugger context (not the execution one) at any time using thread id. The threads command can be used to see currently running threads.
(dbx) threads
      t@1  a  l@1   ?()   running          in  ___nanosleep()
*>t@2  a  l@2   myThread()   breakpoint       in  incrementCounter()
(dbx) thread t@1
Current function is main
   56                   usleep(500);
t@1 (l@1) stopped in ___nanosleep at 0xcff4ffc5
0xcff4ffc5: ___nanosleep+0x0015:        jae      ___nanosleep+0x23  \
      [ 0xcff4ffd3, .+0xe ]
(dbx) where
current thread: t@1
  [1] ___nanosleep(0x8047cc8, 0x0), at 0xcff4ffc5
  [2] _usleep(0x1f4), at 0xcff446fb
=>[3] main(), line 56 in "demodbx.c"
(dbx) print tid
tid = 2U
With the context switched to the first thread, it is easy to inspect the main function’s local variables like “tid”. Step-by-Step Execution Now you know how to check variables from the current functions and their callers, let’s see how to control the execution of your program. If you type cont, the execution will continue until a new breakpoint is reached. You can also ask the system to execute until the next line of source code in the same scope is reached (a.k.a. stepping over) using the next command. Stepping out (i.e., executing until the current function exits) can be done using the step up command. You can also ask the system to execute until control is given to a specified function (without needing a breakpoint) using step [function_name] [thread_id]:
(dbx) step up
iCounter=91
incrementCounter returns
t@2 (l@2) stopped in myThread at line 30 in file "demodbx.c"
   30                   incrementCounter();
(dbx) next
t@2 (l@2) stopped in myThread at line 30 in file "demodbx.c"
   30                   incrementCounter();
Checking Locks While debugging a multi-threaded application, you can use the syncs command to get a report of locks known to the multi-threading library and their status:
(dbx) syncs
All locks currently known to libthread:
mutex (0x08060cd4): thread  mutex(unlocked)
_xftab+0x40 (0xcff7b1a8): usync_? mutex(unlocked)
libc_malloc_lock (0xcff7a4d0): thread  mutex(unlocked)
0xcfe1a080 (0xcfe1a080): thread  mutex(unlocked)
0xcfe1a098 (0xcfe1a098): thread  condition variable
__uberdata+0xfc0 (0xcff7d680): usync_? mutex(unlocked)
__uberdata+0xe40 (0xcff7d500): thread  mutex(unlocked)
__uberdata+0xd40 (0xcff7d400): thread  mutex(unlocked)
__uberdata+0x80 (0xcff7c740): thread  mutex(unlocked)
__uberdata+0x40 (0xcff7c700): thread  mutex(unlocked)
__uberdata+0x58 (0xcff7c718): thread  condition variable
__uberdata (0xcff7c6c0): thread  mutex(unlocked)
__sbrk_lock (0xcff7c568): thread  mutex(unlocked)
Detecting Memory Leaks The debugger can also be configured in a special mode to check for memory leaks (i.e., blocks that have been allocated but never freed by your process). To enable the mode, simply use check -leaks and run the program from the debugger. Upon exit, a detailed report will be displayed as follows:
(dbx) kill
(dbx) status
*(2) stop in incrementCounter -if iCounter > 50
(dbx) delete 2
(dbx) check -leaks
leaks checking - ON
(dbx) run
...
iCounter=99
iCounter=101
Checking for memory leaks...

Actual leaks report  (actual leaks:       1  total size:       100 bytes)

  Total     Num of  Leaked     Allocation call stack
  Size      Blocks  Block
                    Address
==========  ====== =========== =======================================
       100       1  0x8060d10  main


Possible leaks report (possible leaks:    0  total size:        0 bytes)

execution completed, exit code is 0
The report gives a list of all blocks never freed. Each line also specifies the function that did allocated non-freed blocks. It may be very helpful to know where to debug your code and understand how to solve the problem. Attaching a Running Program The debugger can also be used on programs that started initially out of its context. Let’s consider that little source code:
#include <unistd.h>

int
main()
{
        long            l = 0;
        while (1) {
                l++;
                usleep(500);
        }

        return 0;
}
Let’s attach dbx to that program running in the background. Dbx can receive a process identifier in argument and will then attach to it. Upon attachment, the process will be paused and you will have full control on its execution using the previously described commands (e.g., inspecting the value of l):
# ./attach &
[2] 19433
# dbx attach $!
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc
Reading attach
Reading ld.so.1
Reading libc.so.1
Attached to process 19433
stopped in ___nanosleep at 0xcff7ffc5
0xcff7ffc5: ___nanosleep+0x0015:       jae       ___nanosleep+0x23   \<br>
    [ 0xcff7ffd3, .+0xe ]
Current function is main
    9                   usleep(500);
(dbx) where
  [1] ___nanosleep(0x8047d30, 0x0), at 0xcff7ffc5
  [2] _usleep(0x1f4), at 0xcff746fb
=>[3] main(), line 9 in "attach.c"
(dbx) print l
l = 289
Analyzing a Core File Core files can also be analyzed by dbx. In that case, you can only inspect the process but no longer control its execution because it is no longer running. You are, however, free to control the debugging context (threads and call stack switching). Consider that small program dividing a number by 0:
#include <stdio.h>

int
main()
{
        for (int x = 10; x >= 0; x--)
                printf("10/x=%d\n", x);
        return 0;
}
Start the program to generate a core file and start the debugger by specifying the core file name instead of the process identifier:
# ./coretest
10/10=1
10/9=1
...
10/2=5
10/1=10
Arithmetic exception (core dumped)
# ls -l core
-rw-------   1 root     root     1303923 oct  11 04:03 core
# dbx coretest core
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' \
  in your .dbxrc
Reading coretest
core file header read successfully
Reading ld.so.1
Reading libc.so.1
program terminated by signal FPE (integer divide by zero)
Current function is main
dbx: warning: File `/article/coretest.c' has been modified more \
  recently than `/article/coretest'
    7                   printf("10/x=%d\n", x);
(dbx) print x
x = 0
(dbx) next
dbx: can't continue execution -- no active process

Conclusion

I hope that this article showed you how simple it can be to use dbx once you become familiar with its basic commands. Once you understand the concepts, you can dive into the reference documentation to find how powerful and complex your breakpoint conditions can be.

Quick Reference Sidebar

Arnaud Aubert is an independent consultant and trainer working from France. He contributes regularly to IT publications. He can be reached at: http://www.magesi.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.