Chapter 11: Debugging

11.1 Store the Generated SAS Code

11.2 Control Diagnostic Messages

11.3 End When the Error Occurs

By nature, debugging macros is more difficult than debugging SAS programs. The major confounding factors are:

•    Errors might stem from either macro language or from the generated SAS code.

•    The line number references on macro error messages indicate which macro is causing the problem but not which line within the macro.

This chapter assumes familiarity with standard options such as MPRINT, SYMBOLGEN, and MLOGIC. In fact, Section 4.5 showed how to capture the current settings for these options, temporarily override them, and restore them to their original values at a later point. Current releases of the software have added more options, including:

•    MPRINTNEST and MLOGICNEST, which display the names of all currently executing macros (in nested order) rather than just the innermost executing macro.

•    MAUTOLOCDISPLAY, which displays the source of a macro when invoking it through the autocall facility.

The focus here, however, is on programming magic rather than switching an option on or off. For example, how can a program:

•    Store the generated SAS code in a separate file, so that the file can be run as a SAS program devoid of macro language?

•    Use a macro parameter to control whether or not diagnostic steps (such as running a PROC CONTENTS, printing 10 observations of a data set, or writing the values of macro variables) should execute?

•    End a program as soon as an error is encountered, along with generating a message about which DATA or PROC step contained the error?

Each of the next three sections addresses one of these topics.

11.1 Store the Generated SAS Code

When macro language error messages become difficult to interpret, store the macro-generated SAS statements in a separate file. The generated file may contain some obvious problems. But if not, run that file without any reference to macro language. The error messages should be easier to interpret.

It takes just two small steps to store the generated SAS statements. First, define a file using the keyword MPRINT as the FILENAME:

filename mprint 'path_to_some_file';

And, second, turn on the proper options:

options mfile mprint;

That’s all it takes! Running the program will now save the macro-generated SAS statements in the designated file. The spacing and indentation may not adhere to your usual high standards, but the statements will be captured and available to run again without interference from the macro facility.

The techniques from Section 4.2 can embellish these results in several ways. To begin, direct the generated source code to follow the location of the program:

filename mprint "%sysfunc(getoption(sysin))code";

options mfile mprint;

This combination directs the generated source code; when running prog1.sas, the code automatically gets saved as prog1.sascode.

What if the program runs several macros sequentially, generating thousands of lines of source code? It might be convenient to split up those lines into manageable sections. For example, if prog1.sas runs %MACRO1, then %MACRO2, and then %MACRO3, it might be convenient to save the generated source code as prog1.macro1, prog1.macro2, and prog1.macro3. Just a small amount of preparation can make this happen. First, retrieve the path to the program:

%let program_path = %sysfunc(getoption(sysin));

%let program_path = %substr(&program_path,1,%length(&program_path)-3);

At this point, &PROGRAM_PATH is the complete path to the program, minus the letters sas at the end.

Then modify the definition of %MACRO1, %MACRO2, and %MACRO3 by adding three lines to each:

filename mprint clear;

filename mprint "&program_path.&sysmacroname";

options mfile mprint;

The automatic variable &SYSMACRONAME contains the name of the currently executing macro.

11.2 Control Diagnostic Messages

When macros defy debugging, add diagnostic steps to the macro. %PUT statements display the values of macro variables and indicate whether a certain portion of the macro is executing. Investigate the data more carefully by adding a PROC CONTENTS here and there, along with a PROC PRINT of the first 10 observations of a SAS data set. Add a few more variables to the TABLES statement in PROC FREQ or the VAR statement in PROC MEANS. In short, modify the macro to provide more information that will help diagnose the problem.

Of course, once the problem has been found and corrected, all those diagnostic steps will need to be removed from the macro … or will they? Try a different approach. Write the macro with a parameter that controls whether the diagnostic steps run or not. Turn the diagnostic steps on when debugging is needed, but leave them off otherwise.

Here is a simple approach:

%macro whatever (debug=N,  . . .  more parameters);

&DEBUG controls whether or not the diagnostic steps should run. Within the macro, any of these additions could execute:

%if  &debug=Y %then %do;

     proc print data=people (obs=10);

     run;

     proc contents data=people;

     run;

%end;

proc means data=people;

      var salary age

          %if &debug=Y %then tax_bracket bonus;

          ;

run;

%if &debug=Y %then %put _user_;

Plan ahead by adding this extra parameter and anticipating which diagnostics would help. But remember, if the existing diagnostics don’t resolve the problem, it is always possible to add more … just keep their execution dependent upon &DEBUG.

11.3 End When the Error Occurs

Wouldn’t it be nice if a program would just end when an error occurs? Don’t execute any more DATA or PROC steps, don’t clutter up the log with messages about steps that got skipped, and don’t even execute any more macro language statements. Just halt. And as an added bonus, issue a message about which macro-generated DATA or PROC step is causing the error condition. Macro language can make this happen!

This section explores a variety of tools to consider. Along the way, you may notice that there is more than one “right” way to use these tools. Consider that:

•    Some tools require more effort than others.

•    It would be inconvenient to halt SAS entirely, if you are in the middle of an interactive session.

•    Warning messages may halt a program. Should halting require an error condition instead?

Let’s begin the discussion with an easy-to-use tool, the automatic macro variable &SYSERR. Every DATA and PROC step resets the value of &SYSERR, using this scheme of values:

•    0 = 100% successful execution (neither errors nor warnings)

•    1, 2, 5, 6 = the user took an action that cancelled the DATA or PROC step

•    3 = SAS entered syntax-checking mode

•    4 = the step generated a warning

Values greater than 6 indicate that the step contains some sort of error condition. The simplest application of &SYSERR requires 100% successful execution for a program to continue. Here is a simple example. Compare these two versions of a macro:

%macro run_many;           %macro run_many;

   %macro1                                 %macro1

   %macro2                                 %if &syserr=0 %then %macro2;

   %macro3                                 %if &syserr=0 %then %macro3;

%mend run_many;            %mend run_many;

The first version always attempts to run all three macros. The second version will run a macro only when there are no errors or warnings in the final step of the prior macro.

The subject of warning messages is a tricky one. It is possible for a macro to generate a series of DATA and PROC steps where one of them along the way generates a warning. The value of &SYSERR can get set to 4 by one DATA or PROC step and then reset to 0 by a later step. So the program on the right might run %MACRO2, even though there is an earlier warning message. Later on, this chapter will construct a convenient way to examine &SYSERR for every single DATA and PROC step individually. An alternative would be to switch from &SYSERR to &SYSCC, which does not get reset to 0 by a successful DATA or PROC step.

Finally, if warning messages are acceptable, this logic might be an improvement:

%if &syserr=0 or &syserr=4 %then %do;

Beginning with SAS 9.2, macro language supports the IN operator (as long as the MINOPERATOR option has been turned on):

%if &syserr in (0 4) %then %do;

Be sure to check both the syntax and related options when using the IN operator. To make things simple (and to accommodate the author’s preference as well), the remaining examples all assume there should be no errors and no warnings.

Halting a program is relatively easy. Here is one way (macro language also contains an %ABORT statement):

%if  &syserr > 0 %then %do;

     %put Now halting the program.;

     endsas;

%end;

The ENDSAS statement halts the entire program. If you are using interactive SAS, it will shut down SAS entirely. For cases when that is undesirable, this section will explore an alternative approach shortly.

What would it take to halt a program as soon as an error or warning appears? Is it really feasible to add this sort of checking after every DATA and PROC step? Wouldn’t that require enclosing code in a macro, due to the %IF %THEN statements? The combined answer to all these questions is a short macro:

%macro runn (message);

     run;

     %if &syserr > 0 %then %do;

         %put Now halting the program.;

         %put Location:  &message;

        endsas;

     %end;

%mend runn;

Programs would call this macro instead of using RUN statements:

proc means data=sales;

     var amount tax;

     class state; 

%runn (proc means just after the giant merge step)

The macro adds the RUN statement, causing PROC MEANS to run. It checks &SYSERR to determine if there was any sort of error or warning. If so, it generates a message and halts the program. And all these changes just involve replacing the RUN statement with a %RUNN statement. Even the message is optional. A null message will cause no harm. However, be sure to add parentheses for a null message:

%runn ( )

Otherwise the macro will wait to execute. In fact, an interactive session will just hang, never running the final DATA or PROC step. Why? Interactively, the programmer could submit this much:

%runn 

Then the programmer could submit the rest:

(proc means just after the giant merge step)

Without the parentheses, the next batch of submitted code could, in theory, be supplying a value for &MESSAGE. So the program waits …

Speaking of interactive sessions, it would be inconvenient to have SAS shut down because of an error or warning. The log would disappear, making debugging impossible. To work around this issue, consider the following program:

proc means data=sales;

     var amount tax;

     class state; 

run cancel;

The word “cancel” tells SAS to skip running PROC MEANS. The idea, once a program encounters an error or warning, now becomes:

•    Continue executing the program, but

•    Change all RUN statements to RUN CANCEL statements.

Subsequent DATA and PROC steps may add to the log, but they will not run. The last step that actually runs is the one generating the error.

Just a few changes to %RUNN will make this happen:

%macro runn (message);

   %global _cancel_;

   run &_cancel_;

   %if &syserr > 0 %then %do;

       %put *** Here is the PROBLEM ***;

       %put Location:  &message;

       %let _cancel_=cancel;

   %end;

%mend runn;

This variation nearly works, but it contains a somewhat hidden drawback. When the RUN CANCEL statement executes, SAS considers that the user has cancelled the step. The value of &SYSERR is set to 1, not 0. While all subsequent DATA and PROC steps will not run, every subsequent %RUNN will generate another message about where the problem lies. To correct that, make a slight change:

%macro runn (message);

   %global _cancel_;

   run &_cancel_;

   %if &syserr > 1 %then %do;

       %put *** Here is the PROBLEM ***;

       %put Macro:  &sysmacroname;

       %put Location:  &message;

       %let _cancel_=cancel;

   %end;

   options nosource nonotes;

%mend runn;

Finally, what would happen if you are late paying the bill for your software license renewal? Programs start generating warning messages when the software license is close to expiring. Would these warnings affect &SYSERR and cause your programs to terminate early? No. It turns out that these warnings have no impact on &SYSERR (at least for current releases of the software). Just to be safe, when many programs contain many %RUNN statements, there should be a way to turn them back into RUN; statements. Of course, complex approaches exist where a new global macro variable could control how %RUNN operates. But the simplest way requires adding three lines to the beginning of a program:

%macro runn;

   run;

%mend runn;

By defining %RUNN within the program, the program never needs to search the autocall library. Instead, it executes the already defined version wherever %RUNN appears.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.136.90