File Coverage

blib/lib/Debug/LTrace.pm
Criterion Covered Total %
statement 63 63 100.0
branch 13 24 54.1
condition n/a
subroutine 14 14 100.0
pod 1 1 100.0
total 91 102 89.2


line stmt bran cond sub pod time code
1             package Debug::LTrace;
2              
3 1     1   27955 use warnings;
  1         3  
  1         33  
4 1     1   5 use strict;
  1         2  
  1         27  
5              
6 1     1   6 use Devel::Symdump;
  1         5  
  1         23  
7 1     1   1275 use Hook::LexWrap;
  1         4596  
  1         11  
8 1     1   22811 use Data::Dumper;
  1         8201  
  1         86  
9 1     1   2130 use Time::HiRes qw/gettimeofday tv_interval/;
  1         2014  
  1         7  
10              
11             =head1 NAME
12              
13             Debug::LTrace - Perl extension to locally trace subroutine calls
14              
15             =head1 VERSION
16              
17             Version 0.01
18              
19             =cut
20              
21             our $VERSION = '0.03';
22              
23             =head1 SYNOPSIS
24              
25             use Debug::LTrace;
26              
27             {
28            
29             my $tracer = Debug::LTrace->new('tsub'); # create local tracer
30             tsub(1); # Tracing is on while $tracer is alive
31            
32             }
33            
34             tsub(2); # Here tracing is off
35            
36             sub tsub {shift}
37              
38             #or
39              
40             perl -MDebug::LTrace='*' yourprogram.pl # trace all subroutines in package main
41              
42             =head1 DESCRIPTION
43              
44             Debug::LTrace instruments subroutines to provide tracing information
45             upon every call and return. Using Debug::LTrace does not require any changes to your sources.
46             The trace information is output using the standard warn() function.
47              
48             It was inspired by Debug::Trace, but introduces new features such as
49              
50             =over
51              
52             =item *
53              
54             Lexically scoped tracing
55              
56             =item *
57              
58             Implements tracing in such way that the standard C function works correctly
59              
60             =item *
61              
62             Enable package tracing (using '*' syntax)
63              
64             =item *
65              
66             Nice output formatting
67              
68             =item *
69              
70             More debug information (time of execution, call context...)
71              
72             =back
73              
74             Also Debug::LTrace supports Debug::Trace syntax (modifiers are not supported yet).
75              
76              
77             Devel::TraceCalls - Powerful CPAN module but too complex API and not so convenient as Debug::LTrace
78              
79              
80              
81             =head2 Some useful examples:
82              
83             =over
84              
85             =item from command line:
86              
87             # Trace "foo" and "bar" subroutines
88             perl -MDebug::LTrace=foo,bar yourprogram.pl
89              
90             # Trace all subroutines in current package ( "main" )
91             perl -MDebug::LTrace='*' yourprogram.pl
92            
93             # Trace all subroutines in package "SomeModule" and "AnotherModule::foo"
94             perl -MDebug::LTrace='SomeModule::*, AnotherModule::foo' yourprogram.pl
95              
96              
97             =item the same in code:
98              
99             # Trace "foo", "bar" subroutines in current package (can be not "main")
100             use Debug::LTrace qw/foo bar/;
101              
102             # Trace all subroutines in current package (can be not "main")
103             use Debug::LTrace qw/*/;
104            
105             # Trace all subroutines in package "SomeModule" and "AnotherModule::foo"
106             use Debug::LTrace qw/SomeModule::* AnotherModule::foo/;
107              
108             =item local tracing (is on only when $tracer is alive):
109              
110             # Trace foo, bar subroutines in current package (can be not "main")
111             my $tracer = Debug::LTrace->new( 'foo', 'bar' );
112            
113             # Trace all subroutines in current package (can be not "main")
114             my $tracer = Debug::LTrace->new('*');
115            
116             # Trace all subroutines in package SomeModule and AnotherModule::foo
117             my $tracer = Debug::LTrace->new('SomeModule::*', 'AnotherModule::foo');
118            
119             =back
120              
121             =head2 Output trace log using custom function
122              
123             Debug::LTrace outputs trace log using standart warn function. So you can catch SIGWARN with this code:
124              
125             $SIG{__WARN__} = sub {
126             if ( $_[0] =~ /^TRACE/ ) {
127             goto &custum_sub
128             } else {
129             print STDERR @_;
130             }
131             }
132            
133             =head1 METHODS
134              
135             =head2 Debug::LTrace->new($sub [, $sub2, $sub3 ...] );
136              
137             $sub can be fully-qualified subroutine name like C and will enable tracing for
138             subroutine C
139              
140             $sub can be short subroutine name like C willl enable tracing for subroutine C in current namespace
141              
142             $sub can be fully-qualified mask like C will enable tracing for all subroutines in
143             C namespace including improrted ones
144              
145             =cut
146              
147             my %import_params;
148             my @permanent_objects;
149              
150             sub import {
151 3     3   5927 shift;
152 3         7 $import_params{ ${ \scalar caller } } = [@_];
  3         14  
153             }
154              
155             INIT {
156 1     1   9 while ( my ( $package, $params ) = each %import_params ) {
157 1 50       10 push @permanent_objects, __PACKAGE__->_new( $package, @$params ) if @$params;
158             }
159             }
160              
161             # External constructor
162             sub new {
163 2 50   2 1 282 return unless defined wantarray;
164 2         7 my $self = shift->_new( scalar caller, @_ );
165 2         6 $self;
166             }
167              
168             # Internal constructor
169             sub _new {
170 2     2   43 my ( $class, $trace_package, @params ) = @_;
171 2         4 my $self;
172              
173             # Parse input parameters
174 2         5 foreach my $p (@params) {
175 2 50       6 next if $p =~ /^:\w+/; # TODO parse modifier and set config
176              
177             #process sub
178 2 50       8 $p = $trace_package . '::' . $p unless $p =~ m/::/;
179 2 50       21 push @{ $self->{subs} }, (
  2         329  
180             $p =~ /^(.+)::\*(\*?)$/
181 2 50       72 ? Devel::Symdump ->${ \( $2 ? 'rnew' : 'new' ) }($1)->functions()
182             : $p
183             );
184             }
185              
186 2         6 bless $self, $class;
187              
188 2         10 $self->_start_trace();
189 2         5 $self;
190             }
191              
192             # Bind all hooks for tracing
193             sub _start_trace {
194 2     2   4 my ($self) = @_;
195 2 50       6 return unless ref $self;
196              
197 2         7 $self->{wrappers} = {};
198 2         4 my @messages;
199              
200 2         3 foreach my $sub ( @{ $self->{subs} } ) {
  2         6  
201 12 50       453 next if $self->{wrappers}{$sub}; # Skip already wrapped
202              
203             $self->{wrappers}{$sub} = Hook::LexWrap::wrap(
204             $sub,
205             pre => sub {
206 20     20   411 pop();
207 20         46 my ( $pkg, $file, $line ) = caller(0);
208 20         806 my ($caller_sub) = ( caller(1) )[3];
209              
210 20         1055 my $args = __PACKAGE__->_dump( \@_ );
211              
212 20 50       107 my $msg = "/-$sub($args) called at $file line $line "
213             . ( defined $caller_sub ? "sub $caller_sub" : "package $pkg" );
214              
215 20         109 warn "TRACE C: " . "| " x @messages . "$msg\n";
216 20         164 unshift @messages, [ "$sub($args)", [ gettimeofday() ] ];
217             },
218             post => sub {
219 20     20   401 my $wantarray = ( caller(0) )[5];
220 20         596 my $call_data = shift(@messages);
221              
222 20 50       138 my $msg = $call_data->[0]
    100          
223             . (
224             defined $wantarray
225             ? ' returned: (' . __PACKAGE__->_dump( $wantarray ? pop : [pop] ) . ')'
226             : ' [VOID]'
227             )
228             . ' in '
229             . tv_interval( $call_data->[1], [gettimeofday] ) . ' sec';
230 20         395 warn "TRACE R: " . "| " x @messages . "\\_$msg\n";
231 12         161 } );
232             }
233 2         67 $self;
234             }
235              
236             # Make a nice dump of structure
237             sub _dump {
238 27     27   37 my ( $class, $ref ) = @_;
239 27         55 local $Data::Dumper::Indent = 0;
240 27         30 local $Data::Dumper::Maxdepth = 3;
241 27         94 my $string = Data::Dumper->Dump( [$ref] );
242 27 50       1798 $string = $1 if $string =~ /\[(.*)\];/s;
243 27         115 $string;
244             }
245              
246             =head1 TODO
247              
248             =over
249              
250             =item *
251              
252             improve Debug::LTrace compatibility (add modifiers support)
253              
254             =item *
255              
256             enabling tracing for whole tree of modules
257              
258             =item *
259              
260             callback support to handle debug output
261              
262             =back
263              
264              
265             =head1 AUTHOR
266              
267             "koorchik", C<< <"koorchik at cpan.org"> >>
268              
269              
270             =head1 BUGS
271              
272             Please report any bugs or feature requests to C, or through
273             the web interface at L. I will be notified, and then you'll
274             automatically be notified of progress on your bug as I make changes.
275              
276              
277             =head1 SUPPORT
278              
279             You can find documentation for this module with the perldoc command.
280              
281             perldoc Debug::LTrace
282              
283              
284             You can also look for information at:
285              
286             =over 4
287              
288             =item * RT: CPAN's request tracker
289              
290             L
291              
292             =item * AnnoCPAN: Annotated CPAN documentation
293              
294             L
295              
296             =item * CPAN Ratings
297              
298             L
299              
300             =item * Search CPAN
301              
302             L
303              
304             =back
305              
306              
307              
308             =head1 LICENSE AND COPYRIGHT
309              
310             Copyright 2010 "koorchik".
311              
312             This program is free software; you can redistribute it and/or modify it
313             under the terms of either: the GNU General Public License as published
314             by the Free Software Foundation; or the Artistic License.
315              
316             See http://dev.perl.org/licenses/ for more information.
317              
318             =head1 SEE ALSO
319              
320             L, L, L, L
321              
322             =cut
323              
324             1; # End of Debug::LTrace