File Coverage

blib/lib/Debug/Trace.pm
Criterion Covered Total %
statement 104 109 95.4
branch 30 40 75.0
condition 5 9 55.5
subroutine 20 21 95.2
pod n/a
total 159 179 88.8


line stmt bran cond sub pod time code
1             #! perl
2              
3             package Debug::Trace;
4              
5 4     4   291209 use 5.00503; # Yes!
  4         16  
  4         215  
6             $VERSION = '0.05';
7              
8 4     4   24 use strict;
  4         6  
  4         139  
9             #use warnings; # Such a pity we cannot use this one...
10              
11 4     4   6451 use Data::Dumper;
  4         45860  
  4         316  
12 4     4   40 use Carp;
  4         9  
  4         4434  
13              
14             my @debug;
15              
16             sub import {
17 4     4   35 shift;
18 4         6916 push @debug, [ scalar caller, @_ ];
19             }
20              
21             # Fully qualify package names.
22             sub _q {
23 12     12   23 my($name, $pkg) = @_;
24              
25 12 50       52 $name =~ /::/ ? $name : $pkg . "::" . $name;
26             }
27              
28             # Nicely formatted argument values closure.
29             sub _mkv {
30 12     12   18 my $config = shift;
31              
32             return sub {
33 21     21   76 local $Data::Dumper::Indent = $config->{ indent };
34 21         31 local $Data::Dumper::Useqq = $config->{ useqq };
35 21         30 local $Data::Dumper::Maxdepth = $config->{ maxdepth };
36 21         29 local $Data::Dumper::Quotekeys = $config->{ quotekeys };
37 21         30 local $Data::Dumper::Sortkeys = $config->{ sortkeys };
38 21         109 my $args = Data::Dumper->Dump([shift]);
39 21 50       1314 $args = $1 if $args =~ /\[(.*)\];/s;
40 21         83 $args;
41 12         48 };
42             }
43              
44             # create appropriate output closure
45             sub _mkout {
46 12     12   17 my $config = shift;
47              
48 12         32 my $trunc;
49 12 100       34 if ( my $maxlen = $config->{maxlen} ) {
50             $trunc = sub {
51 2 100   2   6 if ( length($_[0]) > $maxlen ) {
52 1         11 return substr($_[0], 0, $maxlen - 3) . "...\n";
53             }
54             else {
55 1         4 return $_[0];
56             }
57 1         4 };
58             }
59              
60 12 50       34 if ( $config->{'warn'} ) {
61             return sub {
62 32 100   32   189 warn $trunc ? $trunc->(join("", @_)) : @_;
63 12         99 };
64             }
65             else {
66             return sub {
67 0 0   0   0 print STDERR $trunc ? $trunc->(join("", @_)) : @_;
68 0         0 };
69             }
70             }
71              
72             # create appropriate "TRACE: called..." closure
73             sub _mkpre {
74 12     12   22 my($config, $out) = @_;
75              
76 12         19 my $st = $config->{ stacktrace };
77 12 100       27 if ( $config->{'caller'} ) {
78             return sub {
79 14     14   54 my($pkg, $file, $line) = caller(1);
80 14         44 my(undef, undef, undef, $sub) = caller(2);
81 14 100       39 if ( $st ) {
82 2         4 local $Carp::CarpLevel = 1;
83 2         431 my $msg = Carp::longmess;
84 2         9 $msg =~ s/^ at .*\n//;
85 2         16 $msg =~ s/ called at .*?Trace\.pm line \d+\n\tDebug::Trace::__ANON__//g;
86 2         16 $out->("TRACE:\t", @_, " called at ",
87             "$file line $line\n", $msg);
88             }
89             else {
90 12 100       129 $out->("TRACE:\t", @_, " called at ",
91             "$file line $line ",
92             (defined $sub ? "sub $sub" : "package $pkg"),
93             "\n");
94             }
95 10         80 };
96             }
97             else {
98             return sub {
99 2     2   8 $out->("TRACE:\t", @_, "\n");
100 2         10 };
101             }
102             }
103              
104             # Generate the closure to handle the tracing.
105             sub _s {
106 12     12   27 my ($fqs, $cref, $config) = @_;
107              
108 12         29 my $out = _mkout($config);
109 12         35 my $pre = _mkpre($config, $out);
110 12         28 my $v = _mkv($config);
111              
112             sub {
113 16     16   1993 $pre->("$fqs(", $v->(\@_), ")");
114 16 100       138 if ( !defined wantarray ) {
    100          
115 11         22 &$cref;
116 11         117 $out->("TRACE:\t$fqs() returned\n");
117             }
118             elsif ( wantarray ) {
119 3         8 my @r = &$cref;
120 3         23 $out->("TRACE:\t$fqs() returned: (", $v->(\@r), ")\n");
121 3         26 @r;
122             }
123             else {
124 2         6 my $r = &$cref;
125 2         10 $out->("TRACE:\t$fqs() returned: ", $v->([$r]), "\n");
126 2         12 $r;
127             }
128 12         63 };
129             }
130              
131             # Better use CHECK, but this requires Perl 5.6 or later.
132             sub INIT {
133              
134             # configurable options
135 4     4   11 my %config;
136              
137 4         22 _default_config(\%config);
138              
139 4         12 for my $d ( @debug ) {
140 4         23 my($caller, @subs) = @$d;
141              
142 4         9 for my $s ( @subs ) {
143              
144             # is it a config option?
145 20 100       165 if ( $s =~ /^:\w/ ) {
146 8         16 _config_option(\%config, $s);
147 8         12 next;
148             }
149              
150 12         32 my $fqs = _q($s, $caller);
151 4     4   29 no strict 'refs';
  4         8  
  4         1995  
152 12         29 my $cref = *{ $fqs }{CODE};
  12         41  
153 12 50       29 if ( !$cref ) {
154 0 0       0 warn "Instrumenting unknown function $fqs\n" if $^W;
155 0         0 next;
156             }
157             # no warnings 'redefine';
158 12         38 local($^W) = 0;
159 12         33 *{ $fqs } = _s($fqs, $cref, \%config);
  12         73  
160             }
161             }
162             }
163              
164             # fill default config options
165             sub _default_config {
166 4     4   10 my $config = shift;
167              
168 4         14 $config->{ 'warn' } = 1;
169 4         10 $config->{ 'caller' } = 1;
170 4         8 $config->{ stacktrace } = 0;
171 4         44 $config->{ maxlen } = 0;
172              
173             # Data::Dumper specific options
174 4         10 $config->{ indent } = 0;
175 4         10 $config->{ useqq } = 1;
176 4         30 $config->{ maxdepth } = 2;
177 4         85 $config->{ quotekeys } = 0;
178 4         9 $config->{ sortkeys } = 0;
179              
180 4 50       25 if ( my $e = $ENV{PERL5DEBUGTRACE} ) {
181 4         37 for my $c ( split /[\s:]+(?!\()/, $e ) {
182 8 100       21 next unless $c;
183 4         20 _config_option($config, ":".$c);
184             }
185             }
186             }
187              
188             # process one config option
189             sub _config_option {
190 12     12   19 my $config = shift;
191 12         26 $_ = lc(shift);
192              
193 12 100 66     142 if ( /^:no(\w+)$/ && exists $config->{$1} ) {
    100 66        
    50 33        
194 4         12 $config->{$1} = 0;
195             }
196             elsif ( /^:(\w+)$/ && exists $config->{$1} ) {
197 7         24 $config->{$1} = 1;
198             }
199             elsif ( /^:(\w+)\s*\((-?\d+)\)$/ && exists $config->{$1} ) {
200 1         4 $config->{$1} = $2;
201             }
202             else {
203 0           warn "Unrecognized Debug::Trace config option $_\n";
204             }
205             }
206              
207             1;
208              
209             =head1 NAME
210              
211             Debug::Trace - Perl extension to trace subroutine calls
212              
213             =head1 SYNOPSIS
214              
215             perl -MDebug::Trace=foo,bar yourprogram.pl
216              
217             =head1 DESCRIPTION
218              
219             Debug::Trace instruments subroutines to provide tracing information
220             upon every call and return.
221              
222             Using Debug::Trace does not require any changes to your sources. Most
223             often, it will be used from the command line:
224              
225             perl -MDebug::Trace=foo,bar yourprogram.pl
226              
227             This will have your subroutines foo() and bar() printing call and
228             return information.
229              
230             Subroutine names may be fully qualified to denote subroutines in other
231             packages than the default main::.
232              
233             By default, the trace information is output using the standard warn()
234             function.
235              
236             =head2 MODIFIERS
237              
238             Modifiers can be inserted in the list of subroutines to change the
239             default behavior of this module. All modifiers can be used in three
240             ways:
241              
242             =over 4
243              
244             =item *
245              
246             C<:>I to enable a specific feature.
247              
248             =item *
249              
250             C<:no>I to disable a specific feature.
251              
252             =item *
253              
254             C<:>IC<(>IC<)> to set a feature to a specific value. In
255             general, C<:>I is equivalent to C<:>IC<(1)>, while
256             C<:no>I corresponds to C<:>IC<(0)>.
257              
258             =back
259              
260             The following modifiers are recognized:
261              
262             =over 4
263              
264             =item :warn
265              
266             Uses warn() to produce the trace output (default). C<:nowarn> Sends
267             trace output directly to STDERR.
268              
269             =item :caller
270              
271             Add basic call information to the trace message, including from where
272             the routine was called, and by whom. This is enabled by default.
273              
274             =item :stacktrace
275              
276             Add a stack trace (call history).
277              
278             =item :maxlen(I)
279              
280             Truncate the length of the lines of trace information to I
281             characters.
282              
283             =back
284              
285             The following modifiers can be used to control the way Data::Dumper
286             prints the values of parameters and return values. See also L.
287              
288             =over 4
289              
290             =item :indent
291              
292             Controls the style of indentation. It can be set to 0, 1, 2 or 3.
293             Style 0 spews output without any newlines, indentation, or spaces
294             between list items. C<:indent(0)> is the default.
295              
296             =item :useqq
297              
298             When enabled, uses double quotes for representing string values.
299             Whitespace other than space will be represented as C<[\n\t\r]>,
300             "unsafe" characters will be backslashed, and unprintable characters
301             will be output as quoted octal integers. This is the default,
302             use C<:nouseqq> to disable.
303              
304             =item :maxdepth(I)
305              
306             Can be set to a positive integer that specifies the depth beyond which
307             which we don't print structure contents. The default is 2, which means
308             one level of array/hashes in argument lists and return values is expanded.
309             If you use C<:nomaxdepth> or C<:maxdepth(0)>, nested structures are
310             fully expanded.
311              
312             =item :quotekeys
313              
314             Controls wether hash keys are always printed quoted. The default is
315             C<:noquotekeys>.
316              
317             =item sortkeys
318              
319             Controls whether hash keys are dumped in sorted order. The default is
320             C<:nosortkeys>.
321              
322             =back
323              
324             Modifiers apply only to the subroutines that follow in the list of
325             arguments.
326              
327             =head1 METHODS
328              
329             None, actually. Everything is handled by the module's import.
330              
331             =head1 ENVIRONMENT VARIABLES
332              
333             Environment variable C can be used to preset initial
334             modifiers, e.g.:
335              
336             export PERL5DEBUGTRACE=":warn:indent(2):nomaxdepth:quotekeys"
337              
338             =head1 SEE ALSO
339              
340             L, L
341              
342             =head1 AUTHOR
343              
344             Jan-Pieter Cornet ;
345             Jos Boumans ;
346             Johan Vromans ;
347              
348             This is an Amsterdam.pm production. See http://amsterdam.pm.org.
349              
350             Current maintainer is Johan Vromans .
351              
352             =head1 COPYRIGHT
353              
354             Copyright 2002,2013 Amsterdam.pm. All rights reserved.
355              
356             This program is free software; you can redistribute it and/or modify
357             it under the same terms as Perl itself.
358              
359             =cut