OpenVMS Debugger Manual


Previous Contents Index

16.5.1 Putting Tasks on Hold to Control Task Switching

Task switching might be confusing when you are debugging a program. Placing a task on hold with the SET TASK/HOLD command restricts the state transitions the task can make once the program is subsequently allowed to execute.

A task placed on hold can enter any state except the RUNNING state. If necessary, you can force it into the RUNNING state by using the SET TASK/ACTIVE command.

The SET TASK/HOLD/ALL command freezes the state of all tasks except the active task. You can use this command in combination with the SET TASK/ACTIVE command, to observe the behavior of one or more specified tasks in isolation, by executing the active task with the STEP or GO command, and then switching execution to another task with the SET TASK/ACTIVE command. For example:


DBG> SET TASK/HOLD/ALL
DBG> SET TASK/ACTIVE %TASK 1
DBG> GO
   .
   .
   .
DBG> SET TASK/ACTIVE %TASK 3
DBG> STEP
   .
   .
   .

When you no longer want to hold a task, use the SET TASK/NOHOLD command.

16.6 Controlling and Monitoring Execution

The following sections explain how to do the following functions:

16.6.1 Setting Task-Specific and Task-Independent Debugger Eventpoints

An eventpoint is an event that you can use to return control to the debugger. Breakpoints, tracepoints, watchpoints, and the completion of STEP commands are eventpoints.

A task-independent eventpoint can be triggered by the execution of any task in a program, regardless of which task is active when the eventpoint is set. Task-independent eventpoints are generally specified by an address expression such as a line number or a name. All watchpoints are task-independent eventpoints. The following are examples of setting task-independent eventpoints:


DBG> SET BREAK COUNTER
DBG> SET BREAK/NOSOURCE %LINE 55, CHILD$TASK_BODY
DBG> SET WATCH/AFTER=3 KEEP_COUNT

A task-specific eventpoint can be set only for the task that is active when the command is entered. A task-specific eventpoint is triggered only when that same task is active. For example, the STEP/LINE command is a task-specific eventpoint: other tasks might execute the same source line and not trigger the event.

If you use the SET BREAK, SET TRACE, or STEP commands with the following qualifiers, the resulting eventpoints are task specific:

/BRANCH
/CALL
/INSTRUCTION
/LINE
/RETURN

Any other eventpoints that you set with those commands and any eventpoints that you set with the SET WATCH command are task independent. The following are examples of setting task-specific eventpoints:


DBG> SET BREAK/INSTRUCTION
DBG> SET TRACE/INSTRUCTION/SILENT DO (EXAMINE KEEP_COUNT)
DBG> STEP/CALL/NOSOURCE

You can conditionalize eventpoints that are normally task-independent to make them task specific. For example:


DBG> SET BREAK %LINE 11 WHEN (%ACTIVE_TASK=FATHER)

16.6.2 Setting Breakpoints on POSIX Threads Tasking Constructs

You can set a breakpoint on a thread start routine. The breakpoint will trigger just before the start routine begins execution. In Example 16-1, this type of breakpoint is set as follows:


DBG> SET BREAK worker_routine

Unlike Ada tasks, you cannot specify the body of a POSIX Threads task by name but the start routine is similar.

Specifying a WHEN clause with the SET BREAK command ensures that you catch the point at which a particular thread begins execution. For example:


DBG> SET BREAK worker_routine -
_DBG> WHEN (%ACTIVE_TASK = %TASK 4)

In Example 16-1, this conditional breakpoint will trigger when the second worker thread begins executing its start routine.

Other useful places to set breakpoints are just prior to and immediately after condition waits, joins, and locking of mutexes. You can set such breakpoints by specifying either a line number or the routine name.

16.6.3 Setting Breakpoints on Ada Task Bodies, Entry Calls, and Accept Statements

You can set a breakpoint on a task body by using one of the following syntaxes to refer to the task body (see Section 16.3.2):


task-type-identifier$TASK_BODY 


task-identifier$TASK_BODY 

For example, the following command sets a breakpoint on the body of task CHILD. This breakpoint is triggered just before the elaboration of the task's declarative part (also called the task's activation).


DBG> SET BREAK CHILD$TASK_BODY

CHILD$TASK_BODY is a name for the address of the first instruction the task will execute. It is meaningful to set a breakpoint on an instruction, and hence on this name. However, you must not name the task object (for example, CHILD) in a SET BREAK command. The task-object name designates the address of a data item (the task value). Just as it is erroneous to set a breakpoint on an integer object, it is erroneous to set a breakpoint on a task object.

You can monitor the execution of communicating tasks by setting breakpoints or tracepoints on entry calls and accept statements.

Note

Ada task entry calls are not the same as subprogram calls because task entry calls are queued and may not execute right away. If you use the STEP command to move execution into a task entry call, the results might not be what you expect.

There are several points in and around an accept statement where you might want to set a breakpoint or tracepoint. For example, consider the following program segment, which has two accept statements for the same entry, RENDEZVOUS:


 8   task body TWO_ACCEPTS is 
 9   begin 
10      for I in 1..2 loop 
11         select 
12            accept RENDEZVOUS do 
13               PUT_LINE("This is the first accept statement"); 
14            end RENDEZVOUS; 
15         or 
16            terminate; 
17         end select; 
18      end loop; 
19      accept RENDEZVOUS do 
20         PUT_LINE("This is the second accept statement"); 
21      end RENDEZVOUS; 
22   end TWO_ACCEPTS; 

You can set a breakpoint or tracepoint in the following places in this example:

To set a breakpoint or tracepoint in and around an accept statement, you can specify the associated line number. For example, the following command sets a breakpoint on the start and also on the body of the first accept statement in the preceding example:


DBG> SET BREAK %LINE 12, %LINE 13

To set a breakpoint or a tracepoint on an accept statement body, you can also use the entry name (specifying its expanded name to identify the task body where the entry is declared). For example:


DBG> SET BREAK TWO_ACCEPTS$TASK_BODY.RENDEZVOUS

If there is more than one accept statement for an entry, the debugger treats the entry as an overloaded name. The debugger issues a message indicating that the symbol is overloaded, and you must use the SHOW SYMBOL command to identify the overloaded names that have been assigned by the debugger. For example:


DBG> SHOW SYMBOL RENDEZVOUS
overloaded symbol TEST.TWO_ACCEPTS$TASK_BODY.RENDEZVOUS 
  overloaded instance TEST.TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__1 
  overloaded instance TEST.TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__2

Overloaded names have an integer suffix preceded by two underscores. For more information on overloaded names, see the debugger's online help (type Help Language_Support Ada).

To determine which of these overloaded names is associated with a particular accept statement, use the EXAMINE/SOURCE command. For example:


DBG> EXAMINE/SOURCE TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__1
module TEST_ACCEPTS 
    12:       accept RENDEZVOUS do
DBG> EXAMINE/SOURCE TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__2
module TEST_ACCEPTS 
    19:       accept RENDEZVOUS do

In the following example, when the breakpoint triggers, the caller task is evaluated (see Section 16.3.4 for information about the symbol %CALLER_TASK):


DBG> SET BREAK TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__2 -
_DBG> DO (EVALUATE %CALLER_TASK)

The following breakpoint triggers only when the caller task is %TASK 2:


DBG> SET BREAK TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__2 -
_DBG> WHEN (%CALLER_TASK = %TASK 2)

If the calling task has more than one entry call to the same accept statement, you can use the SHOW TASK/CALLS command to identify the source line where the entry call was issued. For example:


DBG> SET BREAK TWO_ACCEPTS$TASK_BODY.RENDEZVOUS__2 -
_DBG> DO (SHOW TASK/CALLS %CALLER_TASK)

16.6.4 Monitoring Task Events

The SET BREAK/EVENT and SET TRACE/EVENT commands enable you to set breakpoints and tracepoints that are triggered by task and exception events. For example, the following command sets tracepoints that trigger whenever task CHILD or %TASK 2 makes a transition to the RUN state:


DBG> SET TRACE/EVENT=RUN CHILD,%TASK 2

When a breakpoint or tracepoint is triggered as a result of an event, the debugger identifies the event and gives additional information.

The following tables list the event-name keywords that you can specify with the SET BREAK/EVENT and SET TRACE/EVENT commands:

Table 16-6 Generic Low-Level Task Scheduling Events
Event Name Description
RUN Triggers when a task is about to run.
PREEMPTED Triggers when a task is being preempted from the RUN state and its state changes to READY. (See Table 16-3.)
ACTIVATING Triggers when a task is about to begin its execution.
SUSPENDED Triggers when a task is about to be suspended.

Table 16-7 POSIX Threads-Specific Events
Event Name Description
HANDLED Triggers when an exception is about to be handled in some TRY block.
TERMINATED Triggers when a task is terminating (including by alert or exception).
EXCEPTION_TERMINATED Triggers when a task is terminating because of an exception.
FORCED_TERM Triggers when a task is terminating due to an alert operation.

Table 16-8 Ada-Specific Events
Event Name Description
HANDLED Triggers when an exception is about to be handled in some Ada exception handler, including an others handler.
HANDLED_OTHERS Triggers only when an exception is about to be handled in an others Ada exception handler.
RENDEZVOUS_EXCEPTION Triggers when an exception begins to propagate out of a rendezvous.
DEPENDENTS_EXCEPTION Triggers when an exception causes a task to wait for dependent tasks in some scope (includes unhandled exceptions, 1 which, in turn, include special exceptions internal to the Compaq Ada Run-Time Library; for more information, see the Compaq Ada documentation). Often immediately precedes a deadlock.
TERMINATED Triggers when a task is terminating, whether normally, by an abort statement, or by an exception.
EXCEPTION_TERMINATED Triggers when a task is terminating due to an unhandled exception. 1
ABORT_TERMINATED Triggers when a task is terminating due to an abort statement.


1 An unhandled exception is an exception for which there is no handler in the current frame or for which there is a handler that executes a raise statement and propagates the exception to an outer scope.

In the previous tables, the exception-related events are included for completeness. Only the task events are discussed in the following paragraphs. For more information about the exception events, see the debugger's online help (type Help Language_Support Ada).

You can abbreviate an event-name keyword to the minimum number of characters that make it unique.

The event-name keywords that you can specify with the SET BREAK/EVENT or SET TRACE/EVENT command depend on the current event facility, which is either THREADS or ADA in the case of task events. The appropriate event facility is set automatically when the program is brought under debugger control. The SHOW EVENT_FACILITY command identifies the facility that is currently set and lists the valid event name keywords for that facility (including those for the generic events).

Several examples follow showing the use of the /EVENT qualifier:


DBG> SET BREAK/EVENT=PREEMPTED
DBG> GO
break on THREADS event PREEMPTED 
  Task %TASK 4 is getting preempted by %TASK 3
   .
   .
   .
DBG> SET BREAK/EVENT=SUSPENDED
DBG> GO
break on THREADS event SUSPENDED 
  Task %TASK 1 is about to be suspended
   .
   .
   .
DBG> SET BREAK/EVENT=TERMINATED
DBG> GO
break on THREADS event TERMINATED 
  Task %TASK 4 is terminating normally
DBG>

The following predefined event breakpoints are set automatically when the program is brought under debugger control:

Ada examples of the predefined and other types of event breakpoints follow.

Example of EXCEPTION_TERMINATED Event

When the EXCEPTION_TERMINATED event is triggered, it usually indicates an unanticipated program error. For example:


...
break on ADA event EXCEPTION_TERMINATED 
  Task %TASK 2 is terminating because of an exception 
    %ADA-F-EXCCOP, Exception was copied at a "raise;" or "accept" 
    -ADA-F-EXCEPTION, Exception SOME_ERROR 
    -ADA-F-EXCRAIPRI, Exception raised prior to PC = 00000B61 
DBG> 

Example of DEPENDENTS_EXCEPTION Event (Ada)

For Ada programs, the DEPENDENTS_EXCEPTION event often unexpectedly precedes a deadlock. For example:


...
break on ADA event DEPENDENTS_EXCEPTION 
  Task %TASK 2 may await dependent tasks because of this exception: 
    %ADA-F-EXCCOP, Exception was copied at a "raise;" or "accept" 
    -ADA-F-EXCEPTION, Exception SOME_ERROR 
    -ADA-F-EXCRAIPRI, Exception raised prior to PC = 00000B61 
DBG> 

Example of RENDEZVOUS_EXCEPTION Event (Ada)

For Ada programs, the RENDEZVOUS_EXCEPTION event enables you to see an exception before it leaves a rendezvous (before exception information has been lost due to copying the exception into the calling task). For example:


...
break on ADA event RENDEZVOUS_EXCEPTION 
  Exception is propagating out of a rendezvous in task %TASK 2 
    %ADA-F-CONSTRAINT_ERRO, CONSTRAINT_ERROR 
    -ADA-I-EXCRAIPRI, Exception raised prior to PC = 00000BA6 
DBG> 

To cancel breakpoints (or tracepoints) set with the /EVENT qualifier, use the CANCEL BREAK/EVENT (or CANCEL TRACE/EVENT) command. Specify the event qualifier and optional task expression in the CANCEL command exactly as you did with the SET command, excluding any WHEN or DO clauses.

You might want to set event breakpoints and tracepoints in a debugger initialization file for your tasking programs. For example:


SET BREAK/EVENT=ACTIVATING 
SET BREAK/EVENT=HANDLED DO (SHOW CALLS) 
SET BREAK/EVENT=ABORT_TERMINATED DO (SHOW CALLS) 
SET BREAK/EVENT=EXCEPTION_TERM DO (SHOW CALLS) 
SET BREAK/EVENT=TERMINATED 

16.7 Additional Task-Debugging Topics

The following sections discuss additional topics related to task debugging:

16.7.1 Debugging Programs with Deadlock Conditions

A deadlock is an error condition in which each task in a group of tasks is suspended and no task in the group can resume execution until some other task in the group executes. Deadlock is a typical error in tasking programs (in much the same way that infinite loops are typical errors in programs that use WHILE statements).

A deadlock is easy to detect: it causes your program to appear to suspend, or hang, in midexecution. When deadlock occurs in a program that is running under debugger control, press Ctrl/C to interrupt the deadlock and display the debugger prompt.

In general, the SHOW TASK/ALL command (see Section 16.4) or the SHOW TASK/STATE=SUSPENDED command is useful because it shows which tasks are suspended in your program and why. The command SET TASK/VISIBLE %NEXT_TASK is particularly useful when debugging in screen mode. It enables you to cycle through all tasks and display the code that each task is executing, including the code in which execution is stopped.

The SHOW TASK/FULL command gives detailed task state information, including information about rendezvous, entry calls, and entry index values. The SET BREAK/EVENT or SET TRACE/EVENT command (see Section 16.6.4) enables you to set breakpoints or tracepoints at or near locations that might lead to deadlock. The SET TASK/PRIORITY and SET TASK/RESTORE commands enable you to see if a low-priority task that never runs is causing the deadlock.

Table 16-9 lists a number of tasking deadlock conditions and suggests debugger commands that are useful in diagnosing the cause.

Table 16-9 Ada Tasking Deadlock Conditions and Debugger Commands for Diagnosing Them
Deadlock Condition Debugger Commands
Self-calling deadlock (a task calls one of its own entries) SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
Circular-calling deadlock (a task calls another task, which calls the first task) SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
Dynamic-calling deadlock (a circular series of entry calls exists, and at least one of the calls is a timed or conditional entry call in a loop) SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
Exception-induced deadlock (an exception prevents a task from answering one of its entry calls, or the propagation of an exception must wait for dependent tasks) SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
SET BREAK/EVENT=DEPENDENTS_EXCEPTION (for Ada programs)
Deadlock because of incorrect run-time calculations for entry indexes, when conditions, and delay statements within select statements SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
EXAMINE
Deadlock due to entries being called in the wrong order SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
Deadlock due to busy-waiting on a variable used as a flag that is to be set by a lower priority task, and the lower priority task never runs because a higher priority task is always ready to execute SHOW TASK/ALL
SHOW TASK/STATE=SUSPENDED
SHOW TASK/FULL
SET TASK/PRIORITY
SET TASK/RESTORE


Previous Next Contents Index