Multithreaded Programming Guide
  Search only this book
Download this book in PDF

Compiling And Debugging

6

This chapter describes how to compile and debug your multithreaded programs.
Compiling a Multithreaded Applicationpage 107
Debugging Multithreaded Programspage 109

Compiling a Multithreaded Application

Using The C Compiler

Make sure the following software is available so you can successfully compile and link a multithreaded program.
  • Include files:
· thread.h
· errno.h

  • The standard C compiler
  • The standard Solaris linker
  • The threads library (libthread)
  • MT-safe libraries (libc, libm, libw, libintl, libmalloc, libmapmalloc, libnsl, and so on)

Compiling With the _REENTRANT Flag

Compile multithread programs with the -D _REENTRANT flag.
This applies to every module of a new application. When the -D_REENTRANT flag is not present, the old definitions for errno, stdio, and so on, are used. To compile a single-threaded application, make sure that the _REENTRANT flag is undefined.

Link Old With New Carefully

Table 6-1 shows that multithreaded object modules should be linked with old object modules only with great caution.
Table 6-1 _REENTRANT
The File Type CompiledReferenceAnd Return
Old object files (non-threaded) and new object filesWithout the _REENTRANT flagStatic storageThe traditional errno
New object filesWith the _REENTRANT flag__errno, the new binary entry pointThe address of the thread's definition of errno
Programs using1 TLI in libnslWith the _REENTRANT flag (required)__t_errno, a new entry pointThe address of the thread's definition of t_errno.
1. Include tiuser.h to get the TLI global error variable.

Using libthread

To use libthread, specify -lthread before -lc on the ld command line, or last on the cc command line.
All calls to libthread are no-ops if the application does not link libthread.
libc has defined libthread stubs that are null procedures. True procedures are interposed by libthread when the application links both libc and libthread.
The behavior of the C library is undefined if a program is constructed with an ld command line that includes the fragment:
.o's ... -lc -lthread ...

Do not link a single-threaded program with -lthread. Doing so establishes multithreading mechanisms at link time that are initiated at run time. These waste resources and produce misleading results when you debug your code.

Using Non-C Compilers

The threads library uses the following items from libc.
  • System call wrappers
  • Something (usually printf()) to display error messages
  • Runtime linking support to resolve symbols (because the library is dynamically linked)
You can eliminate these dependencies by writing both your own system call wrappers and your own printf() function, and by having the linker resolve all libthread symbols at link time rather than at runtime.
The threads library does not use dynamically allocated memory when the threads are created with application-supplied stacks. The thr_create(3T) routine lets the application specify its own stacks.

Debugging Multithreaded Programs

Common Oversights

The following list points out some of the more frequent oversights that can cause bugs in multithreaded programming.
  • Using a local or global variable for passing an argument to a new thread
  • Accessing global memory (shared changeable state) without the protection of a synchronization mechanism
  • Creating deadlocks caused by two threads trying to acquire rights to the same pair of global resources in alternate order (so that one thread controls the first resource and the other controls the second resource and neither can proceed until the other gives up)
  • Creating a hidden gap in synchronization protection. This is caused when a code segment protected by a synchronization mechanism contains a call to a function that frees and then reacquires the synchronization mechanism before it returns to the caller. The result is that it appears to the caller that the global data has been protected when it actually has not.
  • Making deeply nested, recursive calls and using large automatic arrays can cause problems because multithreaded programs have a more limited stack size than single-threaded programs.
  • Specifying an inadequate stack size
  • Providing stack other than through the thread library calls
And, note that multithreaded programs (especially buggy ones) often behave differently in two successive runs given identical inputs because of differences in the thread scheduling order.
In general, multithreading bugs are statistical instead of deterministic in character. Tracing is usually more effective in finding problems in the order of execution than is breakpoint-based debugging.

Using adb

When you bind all threads in a multithreaded program, a thread and an LWP are synonymous. Then you can access each thread with the following adb commands that support multithreaded programming.
Table 6-2 adb
pid:AAttaches to process # pid. This stops the process and all its LWPs.
:RDetaches from process. This resumes the process and all its LWPs.
$LLists all active LWPs in the (stopped) process.
n:lSwitches focus to LWP # n
$lShows the LWP currently focused
num:iIgnores signal number num

Using dbx

With the dbx utility you can debug and execute source programs written in C++, ANSI C, FORTRAN, and Pascal. dbx accepts the same commands as the SPARCworks(TM) Debugger but uses a standard terminal (tty) interface. Both dbx and the SPARCworks Debugger now support debugging multithreaded programs.
For a full overview of dbx and Debugger features see the SunPro dbx(1) man page and the Debugging a Program user's guide.
The following dbx options support multithreaded programs.
Table 6-3 dbx
cont at line [sig signo id]Continues execution at line line with signal signo. See continue for dbx command language loop control. The id, if present, specifies which thread or LWP to continue. Default value is all.
lwpDisplays current LWP. Switches to given LWP [lwpid].
lwpsLists all LWPs in the current process.
next ... tidSteps the given thread. When a function call is skipped, all LWPs are implicitly resumed for the duration of that function call. Nonactive threads cannot be stepped.
next ... lidSteps the given LWP. Does not implicitly resume all LWPs when skipping a function. The LWP on which the given thread is active. Does not implicitly resume all LWP when skipping a function.
step... tidSteps the given thread. When a function call is skipped, all LWPs are implicitly resumed for the duration of that function call. Nonactive threads cannot be stepped.
step... lidSteps the given LWP. Does not implicitly resume all LWPs when skipping a function.
stepi... lidThe given LWP.
stepi... tidThe LWP on which the given thread is active.
threadDisplays current thread. Switches to thread tid. In all the following variations, an
optional tid implies the current thread.
thread -info [ tid ]Prints everything known about the given thread.
thread -locks [ tid ]Prints all locks held by the given thread.
thread -suspend [ tid ]Puts the given thread into suspended state.
Table 6-3 dbx
thread -continue [ tid ]Unsuspends the given thread.
thread -hide [ tid ]Hides the given (or current) thread. It will not show up in the generic threads listing.
thread -unhide [ tid ]Unhides the given (or current) thread.
allthread-unhideUnhides all threads.
threadsPrints the list of all known threads.
threads-allPrints threads that are not usually printed (zombies).
all|filterthreads-modeControls whether threads prints all threads or filters them by default.
auto|manualthreads-modeEnables automatic updating of the thread listing in the Thread Inspector of the GUI interface (SPARCworks Debugger).
threads-modeEchoes the current modes. Any of the previous forms can be followed by a thread or LWP ID to get the traceback for the specified entity.