Icon for Dimension.orgIcon for TalentIcon for ToolsIcon for Doorways.org
Icon for ToolsIcon for Perl Tools

MMAgic Demo: MMAgic/Trace.pm Source File

  • Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

MMAgic/Trace.pm

Go to the documentation of this file.
00001 package MMAgic::Trace;
00002 
00003 =head1 NAME
00004 
00005 MMAgic::Trace - Perl filter used to (de)activate trace statements.
00006 
00007 =head1 SYNOPSIS
00008 
00009     use MMAgic::Trace;
00010     
00011     # ...or...
00012     
00013     use MMAgic::Trace qw(XXX); # set XXX tag
00014     
00015     #!#     this line is a trace statement
00016     #*#     include this trace statement (easy one-line include)
00017     #$#     include trace statements to end of subroutine
00018     #!#     include this if use Trace qw(XXX);  #[XXX]
00019     #!#     include this if one of tags set     #[XXX YYY]
00020 
00021 =head1 DESCRIPTION
00022 
00023 The C<MMAgic::Trace> source filter works by I<changing> the source code
00024 early in the compilation process.
00025 Certain special comment sequences are used to indicate lines of code
00026 that contain trace statements.
00027 
00028 Unlike many trace mechanisms, the trace statements that are not activated
00029 are never executing at run-time.
00030 There is a minor compile-time penalty for using the source filter but once
00031 the program is running the un-activated statements are just comments and
00032 take up no CPU cycles whatsoever.
00033 
00034 It is possible to use this filter for anything, not (just) trace statements.
00035 For other uses it may be necessary to write a more specific filter.
00036 
00037 =head2 Special Comments
00038 
00039 Special comment sequences are used to mark lines that will potentially be
00040 activated by the filter.
00041 The first three characters of the line are C<#?#> where the center
00042 character (between the two octothorpes) determines the behavior of the line.
00043 The basic sequence is C<#!#> which indicates an unactivated line.
00044 
00045 Statements can be activated by changing them slightly, replacing C<#!#>
00046 with C<#*#> or C<#$#> to activate one or more lines.
00047 C<#*#> just activates that line, whereas C<#$#> activates that line and
00048 any other C<#!#> lines until the end of the current subroutine.
00049 
00050 There is no way to activate lines at this level of granularity without
00051 editing the source file to change the comment sequences.
00052 It is possible, of course, to activate lines that have additional control
00053 behavior that will be executed at run-time, gaining the best of both worlds.
00054 
00055 =head2 Tag Groups
00056 
00057 It is also possible to mark one or more lines with tags, grouping lines
00058 together so that they can be (de)activated at one time.
00059 These tags only apply to lines that begin with a comment sequence such as C<#!#>.
00060 The tags are added to the I<end> of such lines using a sequence C<#[TAG]> where
00061 C<TAG> is whatever tag is desired.
00062 Any number of lines can be marked with the same tag.
00063 Multiple tags can be added to a line within the square brackets separated by spaces.
00064 
00065 There are two means of activating lines by tag.
00066 The first is to add the desired tags onto the C<use> statement in a list which
00067 will be processed by the MMAgic::Trace::import() function.
00068 This requires editing of the source file.
00069 
00070 The second method is to use one or more command-line parameters of the form
00071 C<--trace=TAG>, allowing them to be activated I<at run-time>.
00072 These are parsed during the MMAgic::Trace::import() function and activate
00073 the specified tag(s) for all files that use those tags.
00074 This allows the tagged lines to be activated at run-time.
00075 This parsing occurs prior to changes made by C<Getopt::Long>.
00076 
00077 =head1 METHODS
00078 
00079 =over
00080 
00081 =cut
00082 
00083 
00084 use     strict;
00085 use     warnings;
00086 
00087 use     Filter::Util::Call;
00088 
00089 ###########################################################################
00090 ###########################################################################
00091 
00092 =item   C<import($class [ , @tags ])>
00093 
00094 Activate and configure trace filter.
00095 
00096 Using the package activates the source filter.
00097 Special comment sequences are recognized and converted to normal code lines
00098 based on the comment sequence and/or tags specified therein.
00099 
00100 The C<use> statement allows optional specification of tags to activate.
00101 When tags are activated the appropriately tagged comments are converted to
00102 code throughout, so that categories of trace statements can be activated
00103 all at once.
00104 
00105 =cut
00106 
00107 #=| @param  @tags   tags to be activated
00108 
00109 sub import
00110     {
00111     my  $self = bless { }, shift;
00112     my  @tags = @_;
00113     
00114     map {   # grab tags from special application arguments:
00115         /^--trace=(.*)$/ &&
00116         map { push @tags, $_ }   split(',', $1);
00117         # TBD:
00118         # tags are 'global', don't currently know how to figure out
00119         #   per-package tags or any such nonsense -- quick'n'dirty
00120         # also doesn't handle the way DOS batch files remove '='
00121         #   and separate into two arguments, though the basic
00122         #   getopt package does understand the resulting format
00123         } @ARGV;
00124     
00125     $self->{_stack_} = [ ];
00126     $self->{'*'}     = 1;
00127 
00128     map {
00129         if (/^no(.*)$/) { undef $self->{$1} }
00130         else            { $self->{$_} = 1   }
00131         } @tags;
00132     
00133     # Activate the filter:
00134     filter_add($self);
00135     }
00136 
00137 ###########################################################################
00138 
00139 =item   C<filter($self)>
00140 
00141 Filter source to activate trace statements.
00142 
00143 This is the basic mechanism used by C<Filter::Util::Call>.
00144 Should be called once for each line of code.
00145 
00146 Some might prefer C<Filter::Simple>, but this works fine for now.
00147 It is not all the complicated.
00148 
00149 =cut
00150 
00151 sub filter
00152     {
00153     my  $self = shift;
00154     my  $code = filter_read;
00155     my  $func = undef;
00156     
00157     return $code unless $code > 0;
00158     
00159     # Here is where the filtering occurs:
00160     
00161     if (/^#([-+])#/) # TBD: this never seemed to work properly, why?
00162         {
00163         push @{$self->{_stack_}}, $1 if $1 eq '-';
00164         pop  @{$self->{_stack_}}     if $1 eq '+' && @{$self->{_stack_}};
00165         }
00166     
00167     elsif (@{$self->{_stack_}})
00168         {
00169         $_ = '';
00170         }
00171     
00172     elsif (/^#[\!\$\*]#/)
00173         {
00174         # Found a line that needs processing:
00175         my  $doit = $self->{all};
00176         
00177         if ($doit)          {               }
00178         elsif (/^#\*#/)     {   $doit = 1;  }
00179         else
00180             {
00181             # Allow functions to be activated on-site:
00182             $self->{$self->{_function_}} = 1
00183                 if /^#\$#/
00184                 && $self->{_function_};
00185             
00186             # Must check and see whether to include line or not:
00187             my  @tags = ( );
00188             
00189             # Collect tags from line and from state parsed from file:
00190             push @tags, split(/\s+/, $1)    if /#\[\s*(.*)\s*\]\s*$/;
00191             push @tags, $self->{_function_} if $self->{_function_};
00192             push @tags, $self->{_package_}  if $self->{_package_};
00193             
00194             for my $tag (@tags)
00195                 {
00196                 next unless $self->{$tag};
00197                 $doit = 1;
00198                 last;
00199                 }
00200             
00201             # If no tags apply, just do it:
00202             $doit = 1 unless $doit || @tags;
00203             }
00204     
00205         # Fix line:
00206         s/^#[\!\$\*]#/   / if $doit;
00207         }
00208     
00209     elsif (/^[^#]*package\s*(\S*).*;/)
00210         {
00211         # Keep track of what package we're (supposedly) in:
00212         $self->{_package_} = "$1::";
00213         #   (this isn't going to be completely accurate)
00214         }
00215     
00216     elsif (/^[^#]*sub\s+(\w+)/)
00217         {
00218         # Keep track of what subroutine we're (supposedly) in:
00219         $self->{_function_} = "::$1";
00220         #   (this isn't going to be completely accurate)
00221         }
00222 
00223     $code   # always return status code
00224     }
00225 
00226 ###########################################################################
00227 ###########################################################################
00228 
00229 1
00230 
00231 __END__
00232 
00233 =back
00234 
00235 =head1 AUTHOR
00236 
00237 Marc M. Adkins, F<mailto:Perl@Doorways.org>
00238 
00239 =head1 COPYRIGHT AND LICENSE
00240 
00241 Copyright 2001-2008 by Marc M. Adkins
00242 
00243 =cut

Generated on Mon Dec 27 2010 15:15:42 for MMAgic Demo by  doxygen 1.7.1

www.dimension.org logo

(C)opyright 1998 - 2012 Dimension.org

WebMaster