HP OpenVMS Systems Documentation

Content starts here

OpenVMS Debugger Manual


Previous Contents Index

  1. After all of the Ada library packages are elaborated (in this case, TEXT_IO), the main program is automatically called and begins to elaborate its declarative part (lines 18 through 82).
  2. To ensure repeatability from run to run, the example uses no time slicing (see Section 17.5.2). The 0.0 value for the pragma TIME_SLICE documents that the procedure TASK_EXAMPLE needs to have time slicing disabled.
    On VAX processors, time slicing is disabled if the pragma TIME_SLICE is omitted or is specified with a value of 0.0.
    On Alpha processors, pragma TIME_SLICE (0.0) must be used to disable time slicing.
  3. Task object FATHER is elaborated, and a task designated %TASK 2 is created. FATHER has no pragma PRIORITY, and thus assumes a default priority. FATHER (%TASK 2) is created in a suspended state and is not activated until the beginning of the statement part of the main program (line 83), in accordance with Ada rules. The elaboration of the task body on lines 29 through 81 defines the statements that tasks of type FATHER_TYPE will execute.
  4. Task FATHER declares a single task named CHILD (line 32). A single task represents both a task object and an anonymous task type. Task CHILD is not created or activated until FATHER is activated.
  5. The only source of asynchronous system traps (ASTs) is this series of TEXT_IO.PUT_LINE statements (I/O completion delivers ASTs).
  6. The task FATHER is activated while the main program waits. FATHER has no pragma PRIORITY and this assumes a default priority of 7. (See the DEC Ada Language Reference Manual for the rules about default priorities.) FATHER's activation consists of the elaboration of lines 29 through 44.
    When task FATHER is activated, it waits while its task CHILD is activated and a task designated %TASK 3 is created. CHILD executes one entry call on line 38, and then deadlocks because the entry is never accepted (see Section 17.7.1).
    Because time slicing is disabled and there are no higher priority tasks to be run, FATHER will continue to execute past its activation until it is blocked at the ACCEPT statement at line 47.
  7. A single task, MOTHER, is defined, and a task designated %TASK 4 is created. The pragma PRIORITY gives MOTHER a priority of 6.
  8. The task MOTHER begins its activation and executes line 91. After MOTHER is activated, the main program (%TASK 1) is eligible to resume its execution. Because %TASK 1 has the default priority 7, which is higher than MOTHER's priority, the main program resumes execution.
  9. This is the first rendezvous the main program makes with task FATHER. After the rendezvous FATHER will suspend at the SELECT with TERMINATE statement at line 58.
  10. At the third rendezvous with FATHER, FATHER raises the exception SOME_ERROR on line 67. The handler on line 72 catches the exception, aborts the suspended CHILD task, and then reraises the exception; FATHER then terminates.
  11. A loop with a delay statement ensures that when control reaches line 122, FATHER has executed far enough to be terminated.
  12. This entry call ensures that MOTHER does not wait forever for its rendezvous on line 93. MOTHER executes the accept statement (which involves no other statements), the rendezvous is completed, and MOTHER is immediately switched off the processor at line 94 because its priority is only 6.
  13. After its rendezvous with MOTHER, the main program (%TASK 1) executes lines 127 through 129. At line 129, the main program must wait for all its dependent tasks to terminate. When the main program reaches line 129, the only nonterminated task is MOTHER (MOTHER cannot terminate until the null statement at line 97 has been executed). MOTHER finally executes to its completion at line 98. Now that all tasks are terminated, the main program completes its execution. The main program then returns and execution resumes with the command line interpreter.

17.3 Specifying Tasks in Debugger Commands

A task is an entity that executes in parallel with other tasks. A task is characterized by a unique task ID (see Section 17.3.3), a separate stack, and a separate register set.

The current definition of the active task and the visible task determine the context for manipulating tasks. See Section 17.3.1.

When specifying tasks in debugger commands, you can use any of the following forms:

  • A task (thread) name as declared in the program (for example, FATHER in Section 17.2.2) or a language expression that yields a task value. Section 17.3.2 describes Ada language expressions for tasks.
  • A task ID (for example, %TASK 2). See Section 17.3.3.
  • A task built-in symbol (for example, %ACTIVE_TASK). See Section 17.3.4.

17.3.1 Definition of Active Task and Visible Task

The active task is the task that runs when a STEP, GO, CALL, or EXIT command executes. Initially, it is the task in which execution is suspended when the program is brought under debugger control. To change the active task during a debugging session, use the SET TASK/ACTIVE command.

Note

The SET TASK/ACTIVE command does not work for POSIX Threads (on both OpenVMS Alpha and VAX systems) or for Ada on OpenVMS Alpha systems, the tasking for which is implemented via POSIX Threads. Instead of SET TASK/ACTIVE, use the SET TASK/VISIBLE command on POSIX Threads for query-type actions. Or, to gain control to step through a particular thread, use a strategic placement of breakpoints.

The following command makes the task named CHILD the active task:


DBG> SET TASK/ACTIVE CHILD

The visible task is the task whose stack and register set are the current context that the debugger uses when looking up symbols, register values, routine calls, breakpoints, and so on. For example, the following command displays the value of the variable KEEP_COUNT in the context of the visible task:


DBG> EXAMINE KEEP_COUNT

Initially, the visible task is the active task. To change the visible task, use the SET TASK/VISIBLE command. This enables you to look at the state of other tasks without affecting the active task.

You can specify the active and visible tasks in debugger commands by using the built-in symbols %ACTIVE_TASK and %VISIBLE_TASK, respectively (see Section 17.3.4).

See Section 17.5 for more information about using the SET TASK command to modify task characteristics.

17.3.2 Ada Tasking Syntax

You declare a task either by declaring a single task or by declaring an object of a task type. For example:


-- TASK TYPE declaration.
--
task type FATHER_TYPE is
...
end FATHER_TYPE;

task body FATHER_TYPE is
...
end FATHER_TYPE;

-- A single task.
--
task MOTHER is
...
end MOTHER;

task body MOTHER is
...
end MOTHER;

A task object is a data item that contains a task value. A task object is created when the program elaborates a single task or task object, when you declare a record or array containing a task component, or when a task allocator is evaluated. For example:


-- Task object declaration.
--
FATHER : FATHER_TYPE;

-- Task object (T) as a component of a record.
--
type SOME_RECORD_TYPE is
   record
      A, B: INTEGER;
      T   : FATHER_TYPE;
   end record;

HAS_TASK : SOME_RECORD_TYPE;

-- Task object (POINTER1) via allocator.
--
type A is access FATHER_TYPE;
POINTER1 : A := new FATHER_TYPE;

A task object is comparable to any other object. You refer to a task object in debugger commands either by name or by path name. For example:


DBG> EXAMINE FATHER
DBG> EXAMINE FATHER_TYPE$TASK_BODY.CHILD

When a task object is elaborated, a task is created by the Compaq Ada Run-Time Library, and the task object is assigned its task value. As with other Ada objects, the value of a task object is undefined before the object is initialized, and the results of using an uninitialized value are unpredictable.

The task body of a task type or single task is implemented in Compaq Ada as a procedure. This procedure is called by the Compaq Ada Run-Time Library when a task of that type is activated. A task body is treated by the debugger as a normal Ada procedure, except that it has a specially constructed name.

To specify the task body in a debugger command, use the following syntax to refer to tasks declared as task types:


task-type-identifier$TASK_BODY

Use the following syntax to refer to single tasks:


task-identifier$TASK_BODY

For example:


DBG> SET BREAK FATHER_TYPE$TASK_BODY

The debugger does not support the task-specific Ada attributes T'CALLABLE, E'COUNT, T'STORAGE_SIZE, and T'TERMINATED, where T is a task type and E is a task entry (see the Compaq Ada documentation for more information on these attributes). You cannot enter commands such as EVALUATE CHILD'CALLABLE. However, you can get the information provided by each of these attributes with the debugger SHOW TASK command. For more information, see Section 17.4.

17.3.3 Task ID

A task ID is the number assigned to a task when it is created by the tasking system. The task ID uniquely identifies a task during the entire execution of a program.

A task ID has the following syntax, where n is a positive decimal integer:


%TASK n

You can determine the task ID of a task object by evaluating or examining the task object. For example (using Ada path-name syntax):


DBG> EVALUATE FATHER
%TASK 2
DBG> EXAMINE FATHER
TASK_EXAMPLE.FATHER:  %TASK 2

If the programming language does not have built-in tasking services, you must use the EXAMINE/TASK command to obtain the task ID of a task.

Note that the EXAMINE/TASK/HEXADECIMAL command, when applied to a task object, yields the hexadecimal task value. The task value is the address of the task (or thread) control block of that task. For example (Ada example):


DBG> EXAMINE/HEXADECIMAL FATHER
TASK_EXAMPLE.FATHER: 0015AD00
DBG>

The SHOW TASK/ALL command enables you to identify the task IDs that have been assigned to all currently existing tasks. Some of these existing tasks may not be immediately familiar to you for the following reasons:

  • A SHOW TASK/ALL display includes tasks created by subsystems such as POSIX Threads, Remote Procedure Call services, and the C Run-Time Library, not just the tasks associated with your application.
  • A SHOW TASK/ALL display includes task ID assignments that depend on your operating system, your tasking service, and the generating subsystem. The same tasking program, run on different systems or adjusted for different services, will not identify tasks with the same decimal integer. The only exception is %TASK 1, which all systems and services assign to the task that executes the main program.

The following examples are derived from Example 17-1 and Example 17-2, respectively:


DBG> SHOW TASK/ALL
  task id    state hold  pri substate        thread_object
  %TASK    1 READY HOLD   12                 Initial thread
  %TASK    2 SUSP         12 Condition Wait  THREAD_EX1\main\threads[0].field1
  %TASK    3 SUSP         12 Condition Wait  THREAD_EX1\main\threads[1].field1
DBG>


DBG> SHOW TASK/ALL
  task id pri hold state   substate     task object
* %TASK 1  7       RUN                SHARE$ADARTL+130428
  %TASK 2  7       SUSP  Accept       TASK_EXAMPLE.MOTHER+4
  %TASK 4  7       SUSP  Entry call   TASK_EXAMPLE.FATHER_TYPE$TASK_BODY.CHILD+4
  %TASK 3  6       READY              TASK_EXAMPLE.MOTHER+4
DBG>

You can use task IDs to refer to nonexistent tasks in debugger conditional statements. For example, if you ran your program once, and you discovered that %TASK 2 and 3 were of interest, you could enter the following commands at the beginning of your next debugging session before %TASK 2 or 3 was created:


DBG> SET BREAK %LINE 60 WHEN (%ACTIVE_TASK=%TASK 2)
DBG> IF (%CALLER=%TASK 3) THEN (SHOW TASK/FULL)

You can use a task ID in certain debugger commands before the task has been created without the debugger reporting an error (as it would if you used a task object name before the task object came into existence). A task does not exist until the task is created. Later the task becomes nonexistent sometime after it terminates. A nonexistent task never appears in a debugger SHOW TASK display.

Each time a program runs, the same task IDs are assigned to the same tasks so long as the program statements are executed in the same order. Different execution orders can result from ASTs (caused by delay statement expiration or I/O completion) being delivered in a different order. Different execution orders can also result from time slicing being enabled. A given task ID is never reassigned during the execution of the program.

17.3.4 Task Built-In Symbols

The debugger built-in symbols defined in Table 17-2 enable you to specify tasks in command procedures and command constructs.

Table 17-2 Task Built-In Symbols
Built-in Symbol Description
%ACTIVE_TASK The task that runs when a GO, STEP, CALL, or EXIT command executes.
%CALLER_TASK (Applies only to Ada programs.) When an accept statement executes, the task that called the entry that is associated with the accept statement.
%NEXT_TASK The task after the visible task in the debugger's task list. The ordering of tasks is arbitrary but consistent within a single run of a program.
%PREVIOUS_TASK The task previous to the visible task in the debugger's task list.
%VISIBLE_TASK The task whose call stack and register set are the current context for looking up symbols, register values, routine calls, breakpoints, and so on.

Examples using these task built-in symbols follow.

The following command displays the task ID of the visible task:


DBG> EVALUATE %VISIBLE_TASK

The following command places the active task on hold:


DBG> SET TASK/HOLD %ACTIVE_TASK

The following command sets a breakpoint on line 38 that triggers only when task CHILD executes that line:


DBG> SET BREAK %LINE 38 WHEN (%ACTIVE_TASK=CHILD)

The symbols %NEXT_TASK and %PREVIOUS_TASK enable you to cycle through the total set of tasks that currently exist. For example:


DBG> SHOW TASK %VISIBLE_TASK; SET TASK/VISIBLE %NEXT_TASK
DBG> SHOW TASK %VISIBLE_TASK; SET TASK/VISIBLE %NEXT_TASK
   .
   .
   .
DBG> EXAMINE MONITOR_TASK
MOD\MONITOR_TASK: %TASK 2
DBG> WHILE %NEXT_TASK NEQ %ACTIVE DO (SET TASK %NEXT_TASK; SHOW CALLS)

17.3.4.1 Caller Task Symbol (Ada Only)

The symbol %CALLER_TASK is specific to Ada tasks. It evaluates to the task ID of the task that called the entry associated with the accept statement. Otherwise, it evaluates to %TASK 0. For example, %CALLER_TASK evaluates to %TASK 0 if the active task is not currently executing the sequence of statements associated with the accept statement.

For example, suppose a breakpoint has been set on line 61 of Example 17-2 (within an accept statement). The accept statement in this case is executed by task FATHER (%TASK 2) in response to a call of entry RENDEZVOUS by the main program (%TASK 1). Thus, when an EVALUATE %CALLER_TASK command is entered at this point, the result is the task ID of the calling task, the main program:


DBG> EVALUATE %CALLER_TASK
%TASK 1
DBG>

When the rendezvous is the result of an AST entry call, %CALLER_TASK evaluates to %TASK 0 because the caller is not a task.

17.4 Displaying Information About Tasks

To display information about one or more tasks of your program, use the SHOW TASK command.

The SHOW TASK command displays information about existing (nonterminated) tasks. By default, the command displays one line of information about the visible task.

Section 17.4.1 and Section 17.4.2 describe the information displayed by a SHOW TASK command for POSIX Threads and Ada tasks, respectively.

17.4.1 Displaying Information About POSIX Threads Tasks

The command SHOW TASK displays information about all of the tasks of the program that currently exist (see Example 17-3).

Example 17-3 Sample SHOW TASK/ALL Display for POSIX Threads Tasks

 (1)          (2)     (3)     (4)   (5)               (6)
 task id    state hold  pri substate        thread_object
  %TASK    1 SUSP         12 Condition Wait  Initial thread
  %TASK    2 SUSP         12 Mutex Wait      T_EXAMP\main\threads[0].field1
  %TASK    3 SUSP         12 Delay           T_EXAMP\main\threads[1].field1
  %TASK    4 SUSP         12 Mutex Wait      T_EXAMP\main\threads[2].field1
* %TASK    5 RUN          12                 T_EXAMP\main\threads[3].field1
  %TASK    6 READY        12                 T_EXAMP\main\threads[4].field1
  %TASK    7 SUSP         12 Mutex Wait      T_EXAMP\main\threads[5].field1
  %TASK    8 READY        12                 T_EXAMP\main\threads[6].field1
  %TASK    9 TERM         12 Term. by alert  T_EXAMP\main\threads[7].field1
DBG>

Key to Example 17-3:

  1. The task ID (see Section 17.3.3). The active task is marked with an asterisk (*) in the leftmost column.
  2. The current state of the task (see Table 17-3). The task in the RUN (RUNNING) state is the active task. Table 17-3 lists the state transitions possible during program execution.
  3. Whether the task has been put on hold with a SET TASK/HOLD command as explained in Section 17.5.1.
  4. The task priority.
  5. The current substate of the task. The substate helps indicate the possible cause of a task's state. See Table 17-4.
  6. A debugger path name for the task (thread) object or the address of the task object if the debugger cannot symbolize the task object.

Table 17-3 Generic Task States
Task State Description
RUNNING Task is currently running on the processor. This is the active task. A task in this state can make a transition to the READY, SUSPENDED, or TERMINATED state.
READY Task is eligible to execute and waiting for the processor to be made available. A task in this state can make a transition only to the RUNNING state.
SUSPENDED Task is suspended, that is, waiting for an event rather than for the availability of the processor. For example, when a task is created, it remains in the suspended state until it is activated. A task in this state can make a transition only to the READY or TERMINATED state.
TERMINATED Task is terminated. A task in this state cannot make a transition to another state.

Table 17-4 POSIX Threads Task Substates
Task Substate Description
Condition Wait Task is waiting on a POSIX Threads condition variable.
Delay Task is waiting at a call to a POSIX Threads delay.
Mutex Wait Task is waiting on a POSIX Threads mutex.
Not yet started Task has not yet executed its start routine.
Term. by alert Task has been terminated by an alert operation.
Term. by exc Task has been terminated by an exception.
Timed Cond Wait Task is waiting on a timed POSIX Threads condition variable.

The SHOW TASK/FULL command provides detailed information about each task selected for display. Example 17-4 shows the output of this command for a sample POSIX Threads task.

Example 17-4 Sample SHOW TASK/FULL Display for a POSIX Threads Task

(1) task id    state hold  pri substate        thread_object
 %TASK    4 SUSP         12 Delay           T_EXAMP\main\threads[1].field1
(2)        Alert is pending
          Alerts are deferred

(3)        Next pc:           SHARE$CMA$RTL+46136
          Start routine:     T_EXAMP\thread_action
(4)        Scheduling policy: throughput

(5)        Stack storage:
          Bytes in use:          1288   (6) Base:    00334C00
          Bytes available:      40185        SP:      003346F8
          Reserved Bytes:       10752        Top:     00329A00
          Guard Bytes:           4095

(7)        Thread control block:
          Size:                   293        Address: 00311B78

(8)    Total storage:          56613
DBG>

Key to Example 17-4:


Previous Next Contents Index