Previous | Contents | Index |
By default, a tracepoint is triggered when a process comes under debugger control and when it performs an image exit. These predefined tracepoints are equivalent to those resulting from entering the SET TRACE/ACTIVATING and SET TRACE/TERMINATING commands, respectively. You can set breakpoints on these events with the SET BREAK/ACTIVATING and SET BREAK/TERMINATING commands.
To cancel the predefined tracepoints, use the CANCEL TRACE/PREDEFINED command with the /ACTIVATING and /TERMINATING qualifiers. To cancel any user-defined activation and termination breakpoints, use the CANCEL BREAK command with the /ACTIVATING and /TERMINATING qualifiers (the /USER qualifier is the default when canceling breakpoints or tracepoints).
The debugger prompt is displayed when the first process comes under debugger control. This enables you to enter commands before the main image has started execution, as with a one-process program.
Similarly, the debugger prompt is displayed when the last process
performs an image exit. This enables you to enter commands after the
program has completed execution, as with a one-process program.
15.16.3 Interrupting the Execution of an Image to Connect It to the Debugger
You can interrupt a debuggable image that is running without debugger control in a process and connect that process to the debugger.
By default, the source, instruction, and register displays show information about the visible process.
By using the /PROCESS qualifier with the DISPLAY command, you can create process-specific displays or make existing displays process specific, respectively. The contents of a process-specific display are generated and modified in the context of that process. You can make any display process specific except for the PROMPT display. For example, the following command creates the automatically updated source display SRC_3, which shows the source code where execution is suspended in process 3:
2> DISPLAY/PROCESS=(3) SRC_3 AT RS23 - 2> SOURCE (EXAM/SOURCE .%SOURCE_SCOPE\%PC) |
Assign attributes to process-specific displays in the same way as for displays that are not process specific. For example, the following command makes display SRC_3 the current scrolling and source display; that is, the output of SCROLL, TYPE, and EXAMINE/SOURCE commands are then directed at SRC_3:
2> SELECT/SCROLL/SOURCE SRC_3 |
If you enter a DISPLAY/PROCESS command without specifying a process, the specified display is then specific to the process that was the visible process when you entered the command. For example, the following command makes display OUT_X specific to process 2:
2> DISPLAY/PROCESS OUT_X |
In a multiprocess configuration, the predefined tracepoint on process activation automatically creates a new source display and a new instruction display for each new process that comes under debugger control. The displays have the names SRC_n and INST_n, respectively, where n is the process number. These displays are initially marked as removed. They are automatically deleted on process termination.
Several predefined keypad key sequences enable you to configure your
screen with the process-specific source and instruction displays that
are created automatically when a process is activated. Key sequences
that are specific to multiprocess programs are as follows: PF1 KP9, PF4
KP9, PF4 KP7, PF4 KP3, PF4 KP1. See Section A.5 for the general
effect of these sequences. Use the SHOW KEY command to determine the
exact commands.
15.16.5 Setting Watchpoints in Global Sections (Alpha and Integrity servers Only)
On Alpha and Integrity servers, you can set watchpoints in global sections. A global section is a region of memory that is shared among all processes of a multiprocess program. A watchpoint that is set on a location in a global section (a global section watchpoint) triggers when any process modifies the contents of that location.
When setting watchpoints on arrays or records, note that performance is improved if you specify individual elements rather than the entire structure with the SET WATCH command.
If you set a watchpoint on a location that is not yet mapped to a global section, the watchpoint is treated as a conventional static watchpoint. For example:
1> SET WATCH ARR(1) 1> SHOW WATCH watchpoint of PPL3\ARR(1) |
When ARR is subsequently mapped to a global section, the watchpoint is automatically treated as a global section watchpoint and an informational message is issued. For example:
1> GO %DEBUG-I-WATVARNOWGBL, watched variable PPL3\ARR(1) has been remapped to a global section predefined trace on activation at routine PPL3 in %PROCESS_NUMBER 2 predefined trace on activation at routine PPL3 in %PROCESS_NUMBER 3 watch of PPL3\ARR(1) at PPL3\%LINE 93 in %PROCESS_NUMBER 2 93: ARR(1) = INDEX old value: 0 new value: 1 break at PPL3\%LINE 94 in %PROCESS_NUMBER 2 94: ARR(I) = I |
After the watched location is mapped to a global section, the watchpoint is visible from each process. For example:
all> SHOW WATCH For %PROCESS_NUMBER 1 watchpoint of PPL3\ARR(1) [global-section watchpoint] For %PROCESS_NUMBER 2 watchpoint of PPL3\ARR(1) [global-section watchpoint] For %PROCESS_NUMBER 3 watchpoint of PPL3\ARR(1) [global-section watchpoint] all> |
Several users debugging programs simultaneously can place a load on a system. This section describes the resources used by the debugger, so that you or your system manager can tune your system for this activity.
Note that the discussion covers only the resources used by the
debugger. You might also have to tune your system to support the
programs themselves.
15.16.6.1 User Quotas
Each user needs a PRCLM quota sufficient to create an additional process for the debugger, beyond the number of processes needed by the program.
BYTLM, ENQLM, FILLM, and PGFLQUOTA are pooled quotas. They may need to be increased to account for the debugger process as follows:
The kernel debugger and main debugger communicate through global
sections. Each main debugger, regardless of platform, uses at least one
64-Kbyte global section. On Alpha, the main debugger can communicate
with up to six kernel debuggers. On Integrity servers, it can only
communicate with up to 2 kernel debuggers.
15.17 Examples
Example 15-4 and Example 15-5 contain the C code for the server and client programs used in examples throughout this chapter.
Example 15-4 server.c |
---|
#include <stdio.h> #include <starlet.h> #include <cmbdef.h> #include <types.h> #include <descrip.h> #include <efndef.h> #include <iodef.h> #include <iosbdef.h> #include <ssdef.h> #include <string.h> #include "mbxtest.h" int main (int argc, char **argv) { unsigned int status, write_ef; char line_buf [LINE_MAX_LEN + 1]; iosb myiosb; short mbxchan; /* Get event flag. Look for or create the mailbox. */ status = lib$get_ef (&write_ef); if (!(status & 1)) { fprintf (stderr, "Server unable to get eventflag, status = %x", status); return 0; } status = sys$crembx (0, &mbxchan, 0, 0, 0, 0, &mbxname_dsc, CMB$M_WRITEONLY, 0); if (!(status & 1)) { fprintf (stderr, "Server unable to open mailbox, status = %x", status); return 0; } /* Open for business. Loop looking for and processing requests. */ while (TRUE) { printf ("Input command: "); gets (&line_buf); status = sys$clref (write_ef); if (!(status & 1)) { fprintf (stderr, "Client unable to clear read event flag, status = %x", status); return 0; } status = sys$qiow (write_ef, mbxchan, IO$_SETMODE | IO$M_READERWAIT, &myiosb, 0, 0, 0, 0, 0, 0, 0, 0); if ((status) && (myiosb.iosb$w_status)) { status = sys$clref (write_ef); if (!(status & 1)) { fprintf (stderr, "Client unable to clear read event flag, status = %x", status); return 0; } if (strlen (line_buf) == 0) status = sys$qio (write_ef, mbxchan, IO$_WRITEOF | IO$M_READERCHECK, &myiosb, 0, 0, 0, 0, 0, 0, 0, 0); else status = sys$qio (write_ef, mbxchan, IO$_WRITEVBLK | IO$M_READERCHECK, &myiosb, 0, 0, line_buf, strlen (line_buf), 0, 0, 0, 0); if (status) { status = sys$waitfr (write_ef); if ((myiosb.iosb$w_status & 1) && (status & 1)) { if (strlen (line_buf) == 0) break; } else fprintf (stderr, "Server failure during write, status = %x, iosb$w_status = %x\n", status, myiosb.iosb$w_status); } else fprintf (stderr, "Server failure for write request, status = %x\n", status); } else fprintf (stderr, "Server failure during wait for reader, status = %x, iosb$w_status = %x\n", status, myiosb.iosb$w_status); } printf ("\n\nServer done...exiting\n"); return 1; } |
Example 15-5 client.c |
---|
#include <stdio.h> #include <starlet.h> #include <cmbdef.h> #include <types.h> #include <descrip.h> #include <efndef.h> #include <iodef.h> #include <iosbdef.h> #include <ssdef.h> #include <string.h> #include "mbxtest.h" int main (int argc, char **argv) { unsigned int status, read_ef; iosb myiosb; short mbxchan; char line_buf [LINE_MAX_LEN]; /* Get event flag. Look for or create the mailbox. */ status = lib$get_ef (&read_ef); if (!(status & 1)) { fprintf (stderr, "Client unable to get eventflag, status = %x", status); return 0; } status = sys$crembx (0, &mbxchan, 0, 0, 0, 0, &mbxname_dsc, CMB$M_READONLY, 0); if (!(status & 1)) { fprintf (stderr, "Client unable to open mailbox, status = %x", status); return 0; } /* Loop requesting, receiving, and processing new data. */ memset (&myiosb, 0, sizeof(myiosb)); while (myiosb.iosb$w_status != SS$_ENDOFFILE) { status = sys$qiow (read_ef, mbxchan, IO$_SETMODE | IO$M_WRITERWAIT, &myiosb, 0, 0, 0, 0, 0, 0, 0, 0); if ((status) && (myiosb.iosb$w_status)) { status = sys$clref (read_ef); if (!(status & 1)) { fprintf (stderr, "Client unable to clear read event flag, status = %x", status); return 0; } status = sys$qio (read_ef, mbxchan, IO$_READVBLK | IO$M_WRITERCHECK, &myiosb, 0, 0, line_buf, sizeof(line_buf), 0, 0, 0, 0); if (status) { status = sys$waitfr (read_ef); if ((myiosb.iosb$w_status & 1) && (status & 1)) puts (line_buf); else if ((myiosb.iosb$w_status != SS$_NOWRITER) && (myiosb.iosb$w_status != SS$_ENDOFFILE)) fprintf (stderr, "Client failure during read, status = %x, iosb$w_status = %x\n", status, myiosb.iosb$w_status); } else fprintf (stderr, "Client failure for read request, status = %x\n", status); } else fprintf (stderr, "Client failure during wait for writer, status = %x, iosb$w_status = %x\n", status, myiosb.iosb$w_status); status = sys$clref (read_ef); if (!(status & 1)) { fprintf (stderr, "Client unable to clear read event flag, status = %x", status); return 0; } } printf ("\nClient done...exiting\n"); return 1; } |
The header file included in Example 15-4 and Example 15-5, mbxtest.h is shown below.
$DESCRIPTOR(mbxname_dsc, "dbg$mptest_mbx"); #define LINE_MAX_LEN 255 |
This chapter describes features of the debugger that are specific to multithread programs (also called tasking programs). Tasking programs consist of multiple tasks, or threads, executing concurrently in a single process. Within the debugger, the term task denotes such a flow of control regardless of the language or implementation. The debugger's tasking support applies to all such programs. These programs include the following:
Within the debugger, the terms task and thread are synonyms. When you are debugging programs linked with PTHREAD$RTL Version 7.1 or greater, you can directly access the Compaq POSIX Threads debugger with the PTHREAD command. |
In this chapter, any language-specific information or information specific to POSIX Threads is identified as such. Section 16.1 provides a cross-reference between POSIX Threads terminology and Ada tasking terminology.
The features described in this chapter enable you to perform functions such as:
When using these features, remember that the debugger might alter the behavior of a tasking program from run to run. For example, while you are suspending execution of the currently active task at a breakpoint, the delivery of an asynchronous system trap (AST) or a POSIX signal as some input/output (I/O) completes might make some other task eligible to run as soon as you allow execution to continue.
For more information about POSIX Threads, see the Guide to POSIX Threads Library. For more information about Ada tasks, see the Compaq Ada documentation.
The debugging of multiprocess programs (programs that run in more than
one process) is described in Chapter 15.
16.1 Comparison of POSIX Threads and Ada Terminology
Table 16-1 compares POSIX Threads and Ada terminology and concepts.
POSIX Threads Terminology | Ada Terminology | Description |
---|---|---|
Thread | Task | The flow of control within a process |
Thread object | Task object | The data item that represents the flow of control |
Object name or expression | Task name or expression | The data item that represents the flow of control |
Start routine | Task body | The code that is executed by the flow of control |
Not applicable | Master task | A parent flow of control |
Not applicable | Dependent task | A child flow of control that is controlled by some parent |
Synchronization object (mutex, condition variable) | Rendezvous construct such as an entry call or accept statement | Method of synchronizing flows of control |
Scheduling policy and scheduling priority | Task priority | Method of scheduling execution |
Alert operation | Abort statement | Method of canceling a flow of control |
Thread state | Task state | Execution state (waiting, ready, running, terminated) |
Thread creation attribute (priority, scheduling policy, and so on) | Pragma | Attributes of the parallel entity |
The following sections present sample tasking programs with common errors that you might encounter when debugging tasking programs:
Some other examples in this chapter are derived from these programs.
16.2.1 Sample C Multithread Program
Example 16-1 is a multithread C program that shows incorrect use of condition variables, which results in blocking.
Explanatory notes are included after the example. Following these notes are instructions showing how to use the debugger to diagnose the blocking by controlling the relative execution of the threads.
In Example 16-1, the initial thread creates two worker threads that do some computational work. After the worker threads are created, a SHOW TASK/ALL command will show three tasks, each corresponding to a thread ( Section 16.4 explains how to use the SHOW TASK command).
In Example 16-1, a synchronization point (a condition wait) has been placed in the workers' path at line 3893. (The comment starting at line 3877 indicates that a straight call such as this one is incorrect programming and shows the correct code.)
When the program executes, the worker threads are busy computing when the initial thread broadcasts on the condition variable. The first thread to wait on the condition variable detects the initial thread's broadcast and clears it, which leaves any remaining threads stranded. Execution is blocked and the program cannot terminate.
Example 16-1 Sample C Multithread Program |
---|
3777 /* DEFINES */ 3778 #define NUM_WORKERS 2 /* Number of worker threads */ 3779 3780 /* MACROS */ 3781 #define check(status,string) \ 3782 if (status == -1) perror (string); \ 3783 3784 /* GLOBALS */ 3785 int cv_pred1; /* Condition Variable predicate */ 3786 pthread_mutex_t cv_mutex; /* Condition Variable mutex */ 3787 pthread_cond_t cv; /* Condition Variable */ 3788 pthread_mutex_t print_mutex; /* Print mutex */ 3799 3790 /* ROUTINES */ 3791 static pthread_startroutine_t 3792 worker_routine (pthread_addr_t arg); 3793 3794 main () 3795 { 3796 pthread_t threads[NUM_WORKERS]; /* Worker threads */ 3787 int status; /* Return statuses */ 3798 int exit; /* Join exit status */ 3799 int result; /* Join result value */ 3800 int i; /* Loop index */ 3801 3802 /* Initialize mutexes */ 3803 status = pthread_mutex_init (&cv_mutex, pthread_mutexattr_default); 3804 check (status, "cv_mutex initialization bad status"); 3805 status = pthread_mutex_init (&print_mutex, pthread_mutexattr_default); 3806 check (status, "print_mutex intialization bad status"); 3807 3808 /* Initialize condition variable */ 3809 status = pthread_cond_init (&cv, pthread_condattr_default); 3810 check (status, "cv condition init bad status"); 3811 3812 /* Initialize condition variable predicate. */ 3813 cv_pred1 = 1; (1) 3814 3815 /* Create worker threads */ 3816 for (i = 0; i < NUM_WORKERS; i++) { (2) 3817 status = pthread_create ( 3818 &threads[i], 3819 pthread_attr_default, 3820 worker_routine, 3821 0); 3822 check (status, "threads create bad status"); 3823 } 3824 3825 /* Set cv_pred1 to false; do this inside the lock to insure visibility. */ 3826 3827 status = pthread_mutex_lock (&cv_mutex); 3828 check (status, "cv_mutex lock bad status"); 3829 3830 cv_pred1 = 0; (3) 3831 3832 status = pthread_mutex_unlock (&cv_mutex); 3833 check (status, "cv_mutex unlock bad status"); 3834 3835 /* Broadcast. */ 3836 status = pthread_cond_broadcast (&cv); (4) 3837 check (status, "cv broadcast bad status"); 3838 3839 /* Attempt to join both of the worker threads. */ 3840 for (i = 0; i < NUM_WORKERS; i++) { (5) 3841 exit = pthread_join (threads[i], (pthread_addr_t*)&result); 3842 check (exit, "threads join bad status"); 3843 } 3844 } 3845 3846 static pthread_startroutine_t 3847 worker_routine(arg) 3848 pthread_addr_t arg; (6) 3849 { 3850 int sum; 3851 int iterations; 3852 int count; 3853 int status; 3854 3855 /* Do many calculations */ 3856 for (iterations = 1; iterations < 10001; iterations++) { 3857 sum = 1; 3858 for (count = 1; count < 10001; count++) { 3859 sum = sum + count; 3860 } 3861 } 3862 3863 /* Printf may not be reentrant, so allow 1 thread at a time */ 3864 3865 status = pthread_mutex_lock (&print_mutex); 3866 check (status, "print_mutex lock bad status"); 3867 printf (" The sum is %d \n", sum); 3868 status = pthread_mutex_unlock (&print_mutex); 3869 check (status, "print_mutex unlock bad status"); 3870 3871 /* Lock the mutex associated with this condition variable. pthread_cond_wait will */ 3872 /* unlock the mutex if the thread blocks on the condition variable. */ 3873 3874 status = pthread_mutex_lock (&cv_mutex); 3875 check (status, "cv_mutex lock bad status"); 3876 3877 /* In the next statement, the correct condition-wait syntax would be to loop */ 3878 /* around the condition-wait call, checking the predicate associated with the */ 3879 /* condition variable. This would guard against condition waiting on a condition */ 3880 /* variable that may have already been broadcast upon, as well as spurious wake */ 3881 /* ups. Execution would resume when the thread is woken AND the predicate is */ 3882 /* false. The call would look like this: */ 3883 /* */ 3884 /* while (cv_pred1) { */ 3885 /* status = pthread_cond_wait (&cv, &cv_mutex); */ 3886 /* check (status, "cv condition wait bad status"); */ 3887 /* } */ 3888 /* */ 3888 /* A straight call, as used in the following code, might cause a thread to */ 3890 /* wake up when it should not (spurious) or become permanently blocked, as */ 3891 /* should one of the worker threads here. */ 3892 3893 status = pthread_cond_wait (&cv, &cv_mutex); (7) 3894 check (status, "cv condition wait bad status"); 3895 3896 /* While blocking in the condition wait, the routine lets go of the mutex, but */ 3897 /* it retrieves it upon return. */ 3898 3899 status = pthread_mutex_unlock (&cv_mutex); 3900 check (status, "cv_mutex unlock bad status"); 3901 3902 return (int)arg; 3903 } |
Key to Example 16-1:
Previous | Next | Contents | Index |