Chapter Contents

Previous

Next
Debugger PROFILEs, Configuration Files, and EXECs

Executing EXECs or CLISTs from the Debugger

See Setting Up a Debugger PROFILE for information about the use of the debugger PROFILE, a feature of the debugger in which a CLIST or an EXEC is passed automatically to the debugger for execution when the debugger is invoked.

The exec command enables you to pass any valid EXEC or CLIST to the debugger at any point during your debugging session.

Under TSO, you can use both CLISTs and REXX EXECs. In a REXX EXEC, you must issue the following command so that subcommands are directed to the debugger rather than to TSO:

address 'CDEBUG'

There is no restriction against mixing CLISTs and REXX EXECs. A program of either type can call one of the other type. While a REXX EXEC is active, attention interrupts are trapped by the EXEC and not by the debugger. See exec (TSO) for additional information about executing CLISTs and EXECs under TSO.

Under CMS, the EXEC must be written in REXX or EXEC2 and have filetype CDEBUG. See exec (TSO) for additional information about executing EXECs under CMS.


Command Considerations

For both TSO and CMS, no restrictions exist about which commands can be used in an EXEC or CLIST. However, there are consequences to the use of certain commands. In addition, there are some rules about exec, used alone or used as part of an on command.

Commands That Continue Execution

If a CLIST or EXEC contains a command that requires the debugger to continue execution (abort, continue, exit, go, goto, resume, runto, and step), the command is executed and the EXEC or CLIST is ended. Other commands in the CLIST/EXEC that follow the command are not executed.

Echoing EXEC or CLIST Lines

The exececho keyword can be used with the auto command to echo each line of a CLIST or EXEC before the line is parsed and executed by the debugger. In a full-screen session the line is echoed to the Log window, and in line mode it is echoed to the session log.

The default setting for this command is noexececho. You can verify the setting with the query command.

The auto keyword of the transfer command also supports exececho. See transfer for additional information.

exec Command

You cannot use another debugger command on the same line following exec or % commands. (The % command is used only under OS/390.)

dbinput Command

The dbinput command can be called from a CLIST or EXEC in order to input information from the terminal. In a full-screen session, input is made with the Termin window, and in a line-mode session it is made from the line prompt.

dblog Command

The dblog command can be called from a CLIST or EXEC in order to output information to the terminal. In a full-screen session output is sent to the Log window, and in line mode it is displayed in the session log.


Return Codes

You can design a CLIST or EXEC to test the value of a global variable that contains the previous condition code that is passed back from the debugger. Global Variables That Contain Condition Code Values lists global variables that can be checked for condition code values.

Global Variables That Contain Condition Code Values
Command Language Global Variable
CLIST &LASTCC
EXEC rc
EXEC2 &RC

SAS/C Debugger commands set the return codes that are listed in Return Codes Set by Debugger Commands. The column labeled "Successful" shows the return code passed back to a CLIST if the debugger command is executed. The column labeled "Unsuccessful" shows the code that is passed if the debugger command is not executed.

Return Codes Set by Debugger Commands
Command Successful Unsuccessful
abort
not applicable not applicable
assign
0
1
attn
0
1
auto
0
1
break
number of the action from the query list 0*
catch
0
1
config
0
1
continue
not applicable not applicable
copy
0
1
dbinput
0
1
dblog
0
1
define
0
1
disable
a nonzero number 0
drop
a nonzero number 0
dump
0
1
enable
a nonzero number 0
escape
none none
exec (TSO) code set by the CLIST called code from exec command
exec (CMS) code returned from the EXEC -3
exit
not applicable not applicable
go
not applicable not applicable
goto
not applicable not applicable
help
0
code from system help
ignore
request number from query 0*
install
0
1
keys
0
1
list
number of last line listed 0
monitor
0
1
on
request number 0
print
0
1
query
number of last request satisfying the arguments of the query command 0
resume
not applicable not applicable
return
0
1
runto
not applicable not applicable
scope
0
1
set
0
1
step
not applicable not applicable
storage
0
1
system
0
return code from system
trace
number of action from query list 0*
transfer
0
1
undef
0
1
watch
0
1
whatis
0
1
where
0
1
window
0
1
%
0
code from CLIST
?
not applicable not applicable

In Return Codes Set by Debugger Commands * indicates the request number is returned, even if there is unrecognized text after a valid command.


Example REXX EXEC Application

The following source listings demonstrate how to use a REXX EXEC when a program is running under control of the SAS/C Debugger.

The first listing, BTREE, is a program that generates a binary tree. When it is run under the control of the debugger, it opens up a Termin window in which you can type several data lines. The program inserts the lines in a binary tree and then performs an in-order traversal, printing the data lines in sorted order.

Note:    Since the input is stdin, you could type some lines in a file and redirect stdin to the file. However, typing the lines from the Termin window is simpler.  [cautionend]

The second listing, DUMPTREE, is a REXX EXEC that can be used to display the nodes of the binary tree that is created by BTREE.

BTREE

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef struct TREENODE {
   size_t length;
   char *value;
   struct TREENODE *left, *right;
   } TreeNode;

static TreeNode *alloc_TreeNode(size_t, const char *);
static void insert_TreeNode(TreeNode *, size_t, const char *);
static void print_Tree(TreeNode *);

void main(void)
   {
   TreeNode *tree = NULL;
   char io_buffer[258];

   fgets(io_buffer, 256, stdin);
   while (!feof(stdin) && !ferror(stdin)) {
      size_t length = strlen(io_buffer) - 1;
      if (io_buffer[length] != '\ n') {
         printf("String \ "%.40s \ " is too long.\ n", io_buffer);
         exit(8);
         }
      if (tree == NULL)
         tree = alloc_TreeNode(length, io_buffer);
      else
         insert_TreeNode(tree, length, io_buffer);
      fgets(io_buffer, 256, stdin);
      }

   if (ferror(stdin)) {
      puts("Error reading input file.");
      exit(8);
      }

   print_Tree(tree);
   exit(0);
   }

static void insert_TreeNode(TreeNode *root, size_t length,
                            const char *string)

   {
   int cmp;

   cmp = memcmp(string, root->value, min(length, root->length));
   if (cmp == 0)
      cmp = length - root->length;

   if (cmp > 0) {
      if (root->left != NULL)
         insert_TreeNode(root->left, length, string);
      else
         root->left = alloc_TreeNode(length, string);
      }
   else if (cmp < 0) {
      if (root->right != NULL)
         insert_TreeNode(root->right, length, string);
      else
         root->right = alloc_TreeNode(length, string);
      }

   else
      return;
   }

static void print_Tree(TreeNode *root)
   {
   if (root->right != NULL)
      print_Tree(root->right);

   printf("%.*s\ n", root->length, root->value);

   if (root->left != NULL)
      print_Tree(root->left);

   }
static TreeNode *alloc_TreeNode(size_t length, const char *string)
   {
   TreeNode *new;
   char *val;

   new = malloc(sizeof(TreeNode));

   if (new == NULL || (val = malloc(length)) == NULL) {
      puts("Can't allocate a new node");
      exit(8);
      }

   memcpy(val, string, length);
   new->length = length;
   new->value = val;
   new->left = new->right = NULL;

   return new;
   }


DUMPTREE

'/* REXX */'
trace ?r /* Use "trace o to turn off tracing */
address 'CDEBUG'
parse arg root

if root = '' then do
   'DBLOG DUMPTREE Error: root name not specified.'
   exit 4
   end

signal on error

'TRANSFER ROOT_TYPE TYPEOF' root
'TRANSFER ROOT_PTR VALUE' root
call dump root_ptr, root_type
exit 0

error:
'DBLOG DUMPTREE Error: debugger command at line' sigl 'failed.'
exit 4

dump: procedure
parse arg root_ptr, root_type

'TRANSFER RIGHT_PTR VALUE (('root_type')'root_ptr')->right'
if right_ptr ¬ = '0p00000000' then
   call dump right_ptr, root_type

'TRANSFER LENGTH VALUE (('root_type')'root_ptr')->length'
'TRANSFER STRING STR (('root_type')'root_ptr')->value,' length
'DBLOG DUMPTREE:' string

'TRANSFER LEFT_PTR VALUE (('root_type')'root_ptr')->left'
if left_ptr ¬ = '0p00000000' then
   call dump left_ptr, root_type
return


Running the Example

You can run this example REXX EXEC application under either CMS or TSO by performing the following steps:

  1. Invoke BTREE under control of the debugger.

  2. Use the break command to set a breakpoint at the entry to the print_Tree function.

  3. Issue a go command in order to start program execution.

  4. Type several data lines in the Termin window.

  5. Type a Y in the EOF: field of the Termin window in order to signal the end of the file.

  6. When execution stops at the print_Tree breakpoint, use the exec or % command to execute the DUMPTREE EXEC.

  7. Single-step through the EXEC, examining the nodes of your binary tree.

  8. When you finish examining your binary tree, type EXIT to return to the debugger.


Concepts Demonstrated

The example REXX EXEC application demonstrates how to use a recursive debugging EXEC in order to debug a recursive program. It shows that you can use an EXEC to create specialized debugger commands. In addition, it provides some good examples of the transfer command.


Chapter Contents

Previous

Next

Top of Page

Copyright © 2001 by SAS Institute Inc., Cary, NC, USA. All rights reserved.