1
votes

Is there a system option or similar that will automatically echo macro invocations to the SAS log? When debugging code, I would like to see in the log every macro invocation, including what parameters were passed.

So that if I submit %Test(x=1) the log will show something like:

MACRO INVOKED: %TEST(x=1)

When calling a macro in open code, this is not an issue, because the macro call is shown in the usual log. But when outer macros call inner macros, the actual call to %inner is not shown by default. I tried turning on MLOGIC, MPRINT, etc, but couldn't find something that would show me the macro call itself. I think what I want is an MINVOCATION option.

Below I fake an MINVOCATION option by adding /parmbuff to macro definitions, but was hoping for a way to see macro calls without mucking with the macro definition.

%macro test(x=0,y=0,debug=0) /parmbuff ;
  %if &debug %then %put MINVOCATION: %nrstr(%%)&sysmacroname&syspbuff ;
  data _null_ ;
    x=&x ;
    y=&y ;
    put x= y= ;
  run ;
%mend test ;

%macro outer(debug=0) /parmbuff ;
  %if &debug %then %put MINVOCATION: %nrstr(%%)&sysmacroname&syspbuff ;
  %test(x=1,debug=&debug)
  %test(x=1,y=2,debug=&debug)
%mend outer ;

options mprint mprintnest ;
%outer(debug=1)

Returns the desired:

908  options mprint mprintnest ;
909  %outer(debug=1)
MINVOCATION: %OUTER(debug=1)
MINVOCATION: %TEST(x=1,debug=1)
MPRINT(OUTER.TEST):   data _null_ ;
MPRINT(OUTER.TEST):   x=1 ;
MPRINT(OUTER.TEST):   y=0 ;
MPRINT(OUTER.TEST):   put x= y= ;
MPRINT(OUTER.TEST):   run ;

x=1 y=0

MINVOCATION: %TEST(x=1,y=2,debug=1)
MPRINT(OUTER.TEST):   data _null_ ;
MPRINT(OUTER.TEST):   x=1 ;
MPRINT(OUTER.TEST):   y=2 ;
MPRINT(OUTER.TEST):   put x= y= ;
MPRINT(OUTER.TEST):   run ;

x=1 y=2
2
Looks like you may have made a mistake when testing mlogic? That's the option you need... see my answer below.Robert Penridge
Thanks @Robert but MLOGIC isn't quite what I want because it doesn't differentiate between a parameter explicitly passed during invocation versus a call which accepts a default parameter value. I added a comment to your answer. When testing/debugging macros, it would be nice to see the actual call.Quentin
MPRINT is the best way to see what a macro generates. If you want to see the value of macro VARIABLES that are referenced then use the SYMBOLGEN option.Tom
Understood, @Tom, but seeing what SAS code a macro generates (MPRINT) and seeing the values of macro variables (SYMBOLGEN) are different than seeing how a macro was invoked.Quentin
But what it is the PURPOSE? Why does the macro care how the value was passed? Why do you care?Tom

2 Answers

1
votes

I think you may be looking for option mlogic.

Example code:

option mprint mlogic ;

%macro y(blah);
  %put &blah;
%mend;

%macro x();
  %y(hello);
  %put x;
%mend;
%x;

Gives:

MLOGIC(X):  Beginning execution.
MLOGIC(Y):  Beginning execution.
MLOGIC(Y):  Parameter BLAH has value hello
MLOGIC(Y):  %PUT &blah
hello
MLOGIC(Y):  Ending execution.
MPRINT(X):  ;
MLOGIC(X):  %PUT x
x
MLOGIC(X):  Ending execution.

You can see it tells you when the macro begins execution, which macro is executing, and also the value of any paramters passed in.

UPDATE

Based on your clarifications, this was the closest I could find. Basically you need to setup a libname for 'stored' macros. When you define your macro, add the options / store source to tell it to store the source code for the macro into the stored macro library:

libname mac "e:\temp";
option mstored sasmstore=mac;

%macro blah(something=whatever) / store source;
  %put hi;
%mend;

You can later retrieve the source code by using the %copy macro (SAS v9+). This macro has options to write the source to a file rather than the log. You can then read in the file and extract the default parameter values yourself.

%COPY blah / source;

Gives:

%macro blah(something=whatever) / store source;
%put hi;
%mend;

This whitepaper goes into additional details.

It's a lot of extra steps I know but that seems to be a pretty unusual request.

You may be better off rethinking your strategy. For example, a far simpler method might simply be to define your defaults this way:

%macro hasDefaults(x=1,y=2);
  %local default_x default_y;
  %let default_x = 1;
  %let default_y = 2;
  %if &x ne &default_x %then %do;
    %put The default for x was changed from &default_x to &x.;
  %end;
%mend;

This is far from ideal as well, but you'll have to weigh up what will work better for your needs.

0
votes

If you're willing to update all of your macros, which it sounds like you'd have to do anyways, then what about adding:

%put _local_;

at the top of each? At macro invocation, the only local macro variables defined will be those parameters, right?

%macro mymacro(x=,y=,z=);
 %put _local_;
 proc print data=sashelp.class;
 run;
%mend mymacro;

%mymacro(x=1,y=2);

Gives a log of:

08  %mymacro(x=1,y=2);
MYMACRO X 1
MYMACRO Y 2
MYMACRO Z

NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: PROCEDURE PRINT used (Total process time):
      real time           0.05 seconds
      cpu time            0.03 seconds

You could always also put the macro name in there:

%macro mymacro(x=,y=,z=);
 %put MACRO INVOKED: &sysmacroname;
 %put Parameters:;
 %put _local_;
 proc print data=sashelp.class;
 run;
%mend mymacro;

%mymacro(x=1,y=2);

Though it's returned as part of the %put _local_ so it's probably extraneous.