FOCUS AREAS

SAS ARM Macros


Table of Contents

There are two categories of ARM macros:



Macros



Overview

The macros provide the most efficient method for invoking the ARM API calls, and it is recommended that you use these macros. The advantage of using the macros is that they manage the returned IDs automatically.

Also, the macros have additional features. The macros permit conditional execution by setting the appropriate macro parameters and variables.

Some general points about the SAS ARM macros:



Variables

The SAS ARM macros use variables to pass ids and other information between each other. Because the SAS ARM macros must function within the same data step as well as across data steps, variables that are used by the macros sometimes take the form of data step variables and at other times as macro variables.

Typically, if information (like ids) must be passed between two or more SAS ARM macros in the same data step, SAS data step variables are used for this purpose. DROP statements are generated for those variables so that they are not inadvertantly included in any output datasets.

If information must be passed between two or more SAS ARM macros across separate data steps, then macro variables are used.

The following SAS and macro variables are considered global:

Variable

Description

Set by

Used as input by

_armapid application id %arminit %armend
_armtxid transaction class id %armgtid %armstart
_armshdl start handle %armstrt %armupdt,
%armstop
_armrc error status %armupdt,
%armstop,
%armend
none
_armglvl global level indicator calling program all
_armtlvl target level indicator calling program all
_armexec global enablement calling program all
_armacro global code environment calling program all
_armscl SCL code environment calling program all

Depending on its purpose, the global variables above can be set prior to calling a macro, passed to a macro via macro parameter, or returned from a macro call. A general rule is to use the variable as a SAS variable if you're in the same data step as the macro that generates the variable, otherwise use the macro variable.

Examples of using each global variable are shown below.

ID Management

As mentioned previously, one of the primary advantages of using the SAS ARM macros is that the macros automatically manage ids, e.g., application ids, transaction ids, and start handles. This works best in simple ARM call schemes. See below for more complex call schemes: Complex SAS ARM Macro call schemes By default, the SAS ARM macros will use ids that were generated from the most recent macro call. For example, the %armgtid macro below will associate transaction class Txn 1 with application Appl 1 since the most recent application id generated was from the previous %arminit call:



     data _null_;

      /*
       *  This %arminit macro will generate both a SAS
       *  variable and global macro variable by the name
       *  of _armapid and set it to the id returned
       *  from the arm_init call.
       */
       %arminit(appname='Appl 1', appuser='snotll');

     run;

     data _null_;

      /*
       *  This %armgtid macro will use the _armapid
       *  global macro variable set from the previous
       *  %arminit macro as input to the arm_getid
       *  function call that it wraps.
       */
       %armgtid(txnname='Txn 1', txndet='Inventory Txn Class');

     run;

     (etc)
       .
       .
       .

All ID management is performed by the macro without requiring the calling program to keep up with ids. Likewise, the %armstrt macro below will use the last transaction class id associated with the Txn 3 transaction class:



     data _null_;

       %arminit(appname='Appl 1', appuser='snotll');

      /*
       *  This %armgtid macro will generate both a
       *  SAS variable and global macro variable by
       *  the name of _armtxid and set it to the id
       *  returned from the arm_getid call.
       */
       %armgtid(txnname='Txn 3', txndet='Forecasting Txn Class');

      /*
       *  Since we are still on the same data step, the
       *  %armstrt macro below will used the _armtxid
       *  SAS variable generated from the previous
       *  %armgtid macro as input to the arm_start
       *  call.
       */
       %armstrt;

     run;

     (etc)
       .
       .
       .

Notice in the previous example that since the SAS ARM macro calls were on the same data step, the macros used SAS variables to communicate id values, rather than macro variables. Any global variable that is generated as a SAS variable is not included in any output dataset, i.e., the macro generates a DROP statement for all global SAS variables.



Complex ARM Macro call schemes

Allowing the macros to transparently use global variables in basic scenarios greatly simplifies coding. However, this can lead to misleading results in more complex scenarios when attempting to track concurrent applications or transactions:



     data _null_;
       %arminit(appname='xyz',getid=YES,txnname='txn 1');
     run;

     /* start transaction instance 1 */
     data _null_;
       %armstrt;
     run;

     /* start transaction instance 2 */
     data _null_;
       %armstrt;
     run;

     /*
      *  WRONG!  The programmer thinks that this %armupdt is updating
      *  txn 1 -- however, it's really updating txn 2 since that was
      *  the last macro call executed.
      */
     data _null_;
       %armupdt(data='txn instance 1 still running...');
     run;

     (etc)
       .
       .
       .


One solution is to save the application ids, transaction ids and start handles via the *var parameters, i.e., appidvar=, txnidvar=, and shdlvar=, to pass or return the ids in your own named variables.

Here's an example using the shdlvar= parameter to save start handles:



     data _null_;
       %arminit(appname='xyz',getid=YES,txnname='txn 1');
     run;

     /* start transaction instance 1 and save the id via shdlvar= */
     data _null_;
       %armstrt( shdlvar=savhdl1 );
     run;

     /* start transaction instance 2 and save the id via shdlvar= */
     data _null_;
       %armstrt( shdlvar=savhdl2 );
     run;

     /* Now use the shandle= parm after retrieving the first id. */
     data _null_;
       %armupdt(data='updating txn 1', shdlvar=savhdl1);
     run;

     /* Use the same technique to stop the transactions */
     /* in the order they were started.                 */
     data _null_;
       %armstop(shdlvar=savhdl1);
       %armstop(shdlvar=savhdl2);
       %armend();
     run;

The following table summarizes how the *var parameters are used.

parm

ARMINIT

ARMGTID

ARMSTRT

ARMUPDT

ARMSTOP

ARMEND

appidvar output input input when GETID=YES n/a n/a input
txnidvar output when GETID=YES output input when GETID=NO and
output when GETID=YES
n/a n/a n/a
shdldvar n/a n/a output input input n/a

The following example demonstrates how to use all of the *var parameters to automatically manage ids for concurrent applications, transaction classes, and transaction instances:



     data _null_;
       %arminit(appname='Appl 1', appuser='snotll', appidvar=app1);
       %arminit(appname='Appl 2', appuser='snotll', appidvar=app2);
       %arminit(appname='Appl 3', appuser='snotll', appidvar=app3);
     run;

     data _null_;
       %armgtid(txnname='Txn 1A', txndet='Txn Class 1A',
                appidvar=app1,txnidvar=txn1a);
       %armgtid(txnname='Txn 1B', txndet='Txn Class 1B',
                appidvar=app1,txnidvar=txn1b);
       %armgtid(txnname='Txn 2A', txndet='Txn Class 2A',
                appidvar=app2,txnidvar=txn2a);
       %armgtid(txnname='Txn 2B', txndet='Txn Class 2B',
                appidvar=app2,txnidvar=txn2b);
       %armgtid(txnname='Txn 3A', txndet='Txn Class 3A',
                appidvar=app3,txnidvar=txn3a);
       %armgtid(txnname='Txn 3B', txndet='Txn Class 3B',
                appidvar=app3,txnidvar=txn3b);
     run;

     data _null_;
       %armstrt(txnidvar=txn1a,shdlvar=sh1a);
       %armstrt(txnidvar=txn1b,shdlvar=sh1b);
       %armstrt(txnidvar=txn2a,shdlvar=sh2a);
       %armstrt(txnidvar=txn2b,shdlvar=sh2b);
       %armstrt(txnidvar=txn3a,shdlvar=sh3a);
       %armstrt(txnidvar=txn3b,shdlvar=sh3b);
     run;

     data _null_;
       %armupdt(data='Updating txn instance 1a...', shdlvar=sh1a);
       %armupdt(data='Updating txn instance 1b...', shdlvar=sh1b);
       %armupdt(data='Updating txn instance 2a...', shdlvar=sh2a);
       %armupdt(data='Updating txn instance 2b...', shdlvar=sh2b);
       %armupdt(data='Updating txn instance 3a...', shdlvar=sh3a);
       %armupdt(data='Updating txn instance 3b...', shdlvar=sh3b);
     run;

     data _null_;
       %armstop(status=0, shdlvar=sh1a);
       %armstop(status=1, shdlvar=sh1b);
       %armstop(status=0, shdlvar=sh2a);
       %armstop(status=1, shdlvar=sh2b);
       %armstop(status=0, shdlvar=sh3a);
       %armstop(status=1, shdlvar=sh3b);
     run;

     data _null_;
       %armend(appidvar=app1);
       %armend(appidvar=app2);
       %armend(appidvar=app3);
     run;

The macros also support explicit passing of the ids via the appid=, txnid=, and shandle= parameters. These parameters are similar to the *var parameters, except that they will not retrieve values across data steps via macro variables, Their primary use is for supplying numeric constants as id values to the macros, since the *var parameters will not accept numeric constants. These parameters are maintained for compatibility with earlier ARM macros. Their use is not recommended for new applications.

Since ids are generated by the ARM Agent, passing in a numeric literal requires that you must always start a new SAS session in hopes of maintaining the same id number. If any SAS sub-subsystems are also ARMed, you have NO way of knowing what the id number will be at execution time.

The *var parameters are recommended.

Enabling Macro Execution

IMPORTANT NOTE: By default, all ARM macros are disabled.

This default is taken so that insertion of ARM macros within instrumented code will not result in inadvertant, unwanted logging. You must perform a specific action to make ARM active. To activate execution of the ARM macros, you must set the _armexec global macro variable to a value of 1. This globally enables all ARM macros. Any other value for _armexec disables the ARM macros.

There are two methods of setting the _armexec macro variable. The first method sets the variable during data step or SCL program compilation:



%let _armexec = 1;

Setting _armexec to some other value than 1 with this technique will result in no data step or SCL code being generated. A message is written to the log, e.g.,



NOTE: ARMSTRT macro bypassed by _armexec.

The second method sets the variable during data step or SCL program execution:



call symput('_armexec', '1');

With this technique, the macro checks the _armexec variable with an IF statement during program execution and the ARM function call is executed or bypassed as appropriate.

You should be careful using the compilation technique within SCL. If _armexec is not set to 1 then compiling an SCL program containing ARM macros means that the macros will essentially be nulled out and the ARM interface will be permanently unavailable until the SCL program is recompiled with _armexec set to 1.

The recommended technique when using ARM macros within SCL is to have _armexec set to 1 via the compilation technique (perhaps in an autoexec at SAS initialization) and code a pull-down menu option or other means within the application to turn _armexec on and off dynamically via the execution technique, i.e., call symput.

Macro Environment Setting

IMPORTANT NOTE: By default, all ARM macros are in data steps.

_ARMACRO and _ARMSCL

You can set the global ARM macro environment with _armacro set to 1 or 0. Value of 1 specifies that all ARM macros occur in open code, outside of data steps. Value of 0 specifies that all SAS ARM macros occur in data steps. Use the MACONLY= parameter on macros to specify an environment for just that macro.

For SCL programs, specify _ARMSCL with a value of 1.



Global value

temporary parameter

Result

%let _ARMACRO = 0; MACONLY=NO macro is in data step
%let _ARMACRO = 1; MACONLY=YES macro is in open code
%let _ARMSCL = 1; none macro is in SCL
%let _ARMSCL = 0; none macro is not in SCL

Example: Data Step environment



     %let _armacro = 1;             /* set global environment    */
     data _null_;
       %arminit(appname='Appl 1', appuser='snotll', appidvar=app1
                maconly=no);       /* exception to global
value  */
     run;
                                   /* uses global  setting       */
                                   /* maconly= parm not needed   */
     %armgtid(txnname='Txn 1A', txndet='Txn Class 1A',
                appidvar=app1,txnidvar=txn1a);
     run;

Example: SCL environment

AUTOEXEC:
     %let _armscl = 1;          /* set global environment*/
     %let _armexec = 1;

SCL:
     init:
       %arminit(appname='Appl 1', appuser='snotll', appidvar=app1);

       %armgtid(txnname='Txn 1A', txndet='Txn Class 1A',
                appidvar=app1,txnidvar=txn1a);
     return;

     main:
       %armstrt(txnidvar=txn1a,shdlvar=strt1);
     return;

     term:
       %armstop(shdlvar=strt1);
       %armend(appidvar=app1);
     return;


Conditional Macro Execution

Frequently, it is useful to code the ARM macros in your program but only execute them when needed. All macros support a level= parameter that specifies the execution level of that particular macro.

If coded, the execution level of the macro is compared to two global macro variables, _armglvl and _armtlvl. _armglvl is the global level macro variable. If the level= value on the SAS ARM macro is less than or equal to the _armglvl value then the macro is executed. If the level= value on the SAS ARM macro is less than the _armglvl value, macro execution is bypassed.

Example:



/* Set the global level to 10 */
%let _armglvl = 10;

data _null_;
  %arminit(appname='Appl 1', appuser='snotll' );
  %armgtid(txnname='Txn 1', txndet='Transaction #1 detail' );

  /* These macros are executed */
  %armstrt( level=9 );
  %armstop( level=9 );

  /* These macros are are executed */
  %armstrt( level=10 );
  %armstop( level=10 );

  /* These macros are NOT executed */
  %armstrt( level=11 );
  %armstop( level=11 );

  %armend;
run;

_armtlvl is the target level macro variable and works similarly, except the level= value on the SAS ARM macro must be exactly equal to the _armtlvl value in for the macro to execute.

Example:



/* Set the target level to 10 */
%let _armtlvl = 10;

data _null_;
  %arminit(appname='Appl 1', appuser='snotll' );
  %armgtid(txnname='Txn 1', txndet='Transaction #1 detail' );

  /* These macros are NOT executed */
  %armstrt( level=9 );
  %armstop( level=9 );

  /* These macros are executed */
  %armstrt( level=10 );
  %armstop( level=10 );

  /* These macros are NOT executed */
  %armstrt( level=11 );
  %armstop( level=11 );

  %armend;
run;

The level= parameter can be placed on any SAS ARM macro and is highly recommended. It allows you to construct granularity levels of logging that can serve as an effective filtering device so that you only log as much data as you wish. What numeric values you decide to assign to the levels is up to you, however, the value of the level= parameter must be greater than or equal to 0. If you set both _armglvl and _armtlvl at the same time, then both values are compared to determine whether the macro should be executed or not.

Post-processing Macros

Overview

The ARM Logs produced by the SAS ARM agent (either by macro, function call or C macro) are all processed by %ARMPROC and %ARMJOIN. The ARM log can be directly proccessed by the version of SAS used to produce the log. V9 ARM logs can be compared with V8.2 ARM logs by using one of the following methods:

  1. Preferred method - Generate V9 ARM Log, process using V9 %ARMPROC and %ARMJOIN. This produces SAS datasets in the same format as V8.2 %ARMPROC and %ARMJOIN.

    The resulting datasets contain complete statistics for end-of-start-handle and end-of-application.

    Caution - V8.2 does not handle "C" correlator records or user metrics.



  2. Generate V9 ARM Log, put into V8-compatible format ARM log by using the %ARMCONV macro (V9 only). Process the resulting V8-compatible log in V8.2.

    The V8-compatible log does not contain complete end-of application statistics, as these are not produced by the V9 Agent.

    This method should be used when the V9 log needs to be compared to a V8 ARM log in V8.

    ARMCONV Macro



    Purpose

    The ARMCONV macro converts a V9 ARM log to V8.2-compatible format. It is needed only to produce a log that can be processed by V8.2 %ARMPROC macro.

    Note - If you are producing a V9 ARM log, it can be directly processed in V9 %ARMPROC without the use of %ARMCONV.

    Input

    The ARM log external file produced by V9 ARM Agent.

    Output

    The ARM log external file in a format compatible with V8.2 %ARMPROC macro. The end-of-application statistics are not included, as these are not calculated by the V9 ARM Agent. To obtain end-of-application statistics from a V9 ARM Log, use method #2 above, V9 %ARMPROC and %ARMJOIN macros to process the V9 log.