File Coverage

blib/lib/Devel/SimpleTrace.pm
Criterion Covered Total %
statement 61 65 93.8
branch 22 26 84.6
condition 14 17 82.3
subroutine 7 8 87.5
pod n/a
total 104 116 89.6


line stmt bran cond sub pod time code
1             package Devel::SimpleTrace;
2 5     5   90731 use strict;
  5         12  
  5         199  
3              
4             {
5 5     5   27 no strict;
  5         10  
  5         4803  
6             $VERSION = '0.08';
7             }
8              
9             # Install warn() and die() substitutes
10             $SIG{'__WARN__'} = \&_do_warn;
11             $SIG{'__DIE__' } = \&_do_die;
12              
13             my $stderr = '';
14             my $in_eval = 0;
15             my %options = (
16             showrefs => 0,
17             );
18              
19              
20             #
21             # import()
22             # ------
23             sub import {
24 4     4   31 my $class = shift;
25            
26 4         3506 for my $opt (@_) {
27 1 50       28 exists $options{$opt} ? $options{$opt} = not $options{$opt}
28             : CORE::warn "warning: Unknown option: $opt\n"
29             }
30             }
31              
32              
33             #
34             # _use_data_dumper()
35             # ----------------
36             sub _use_data_dumper {
37 1     1   9 require Data::Dumper;
38 1         41 import Data::Dumper;
39 1         2 $Data::Dumper::Indent = 1; # no fancy indent
40 1         2 $Data::Dumper::Terse = 1; # don't use $VAR unless needed
41 1         2 $Data::Dumper::Sortkeys = 1; # sort keys
42             #$Data::Dumper::Deparse = 1; # deparse code refs
43             {
44 1         2 local $^W = 0;
  1         3  
45 1     0   6 *Devel::SimpleTrace::_use_data_dumper = sub {};
  0         0  
46             }
47             }
48              
49              
50             #
51             # _do_warn()
52             # --------
53             sub _do_warn {
54 1     1   511 local $SIG{'__WARN__'} = 'DEFAULT';
55            
56 1         3 my $msg = join '', @_;
57 1         7 $msg =~ s/ at (.+?) line (\d+)\.$//;
58 1         3 $stderr .= $msg;
59 1 50       5 $stderr .= "\n" if substr($msg, -1, 1) ne "\n";
60            
61 1         4 _stack_trace($1, $2);
62            
63 1         6 print STDERR $stderr;
64 1         7 $stderr = '';
65 1         8 $in_eval = 0;
66             }
67              
68              
69             #
70             # _do_die()
71             # -------
72             sub _do_die {
73 5     5   171 local $SIG{'__WARN__'} = 'DEFAULT';
74 5         14 local $SIG{'__DIE__' } = 'DEFAULT';
75            
76 5 100 100     35 CORE::die @_ if ref $_[0] and not $options{showrefs};
77 4 100       31 CORE::die @_ if index($_[0], "\n\tat ") >= 0;
78 2         6 my @args = @_;
79            
80 2 100       11 _use_data_dumper() if ref $args[0];
81 2 100       4 my $msg = join '', map { ref $_ ? "Caught exception object: $_\: ".Dumper($_) : $_ } @args;
  2         16  
82 2         113 $msg =~ s/ at (.+?) line (\d+)\.$//;
83 2         5 $stderr .= $msg;
84 2 50       12 $stderr .= "\n" if substr($msg, -1, 1) ne "\n";
85            
86 2         7 _stack_trace($1, $2);
87            
88 2 50       7 if($in_eval) {
89 2         8 $@ = $stderr;
90 2         4 $stderr = '';
91 2         4 $in_eval = 0;
92 2         44 CORE::die $@
93            
94             } else {
95 0         0 print STDERR $stderr;
96 0         0 $stderr = '';
97 0         0 exit -1
98             }
99             }
100              
101              
102             #
103             # _stack_trace()
104             # ------------
105             sub _stack_trace {
106 3     3   11 my($file,$line) = @_;
107 3   100     11 $file ||= ''; $line ||= '';
  3   100     12  
108 3 100       14 $file =~ '(eval \d+)' and $file = '<eval>';
109            
110 3         5 my $level = 2;
111 3         9 my @stack = ( ['', $file, $line] ); # @stack = ( [ function, file, line ], ... )
112            
113 3         21 while(my @context = caller($level++)) {
114 14   50     30 $context[1] ||= ''; $context[2] ||= '';
  14   50     24  
115 14 100 100     46 $context[1] =~ '(eval \d+)' and $context[1] = '<eval>' and $in_eval = 1;
116 14 100 100     86 $context[3] eq '(eval)' and $context[3] = '<eval>' and $in_eval = 1;
117 14         23 $stack[-1][0] = $context[3];
118 14         100 push @stack, [ '', @context[1, 2] ];
119             }
120 3   50     19 $stack[-1][0] = (caller($level-2))[0].'::' || 'main::';
121            
122 3         8 for my $func (@stack) {
123 17 100       32 $$func[1] eq '' and $$func[1] = 'unknown source';
124 17 100       38 $$func[2] and $$func[1] .= ':';
125 17         60 $stderr .= "\tat $$func[0]($$func[1]$$func[2])\n";
126             }
127             }
128              
129              
130             1;
131              
132             __END__
133              
134             =head1 NAME
135              
136             Devel::SimpleTrace - See where you code warns and dies using stack traces
137              
138             =head1 VERSION
139              
140             Version 0.08
141              
142             =head1 SYNOPSIS
143              
144             On the command-line:
145              
146             perl -wMDevel::SimpleTrace program_with_strange_errors.pl
147              
148             Inside a module:
149              
150             use Devel::SimpleTrace;
151              
152              
153             =head1 DESCRIPTION
154              
155             This module can be used to more easily spot the place where a program
156             or a module generates errors. Its use is extremely simple, reduced
157             to just C<use>ing it.
158              
159             This is achieved by modifying the functions C<warn()> and C<die()>
160             in order to replace the standard messages by complete stack traces
161             that precisely indicates how and where the error or warning occurred.
162             Other than this, their use should stay unchanged, even when using
163             C<die()> inside C<eval()>.
164              
165              
166             =head1 OPTIONS
167              
168             Options can be set at import time using:
169              
170             perl -wMDevel::SimpleTrace=option1,option2
171              
172             or
173              
174             use Devel::SimpleTrace qw(option1 option2);
175              
176             Available options are:
177              
178             =over 4
179              
180             =item C<showrefs>
181              
182             Using this option will tell C<Devel::SimpleTrace> to stringify objects and
183             references passed in argument to C<die()>. This option is disabled by default
184             in order to leave objects and references untouched.
185              
186             =back
187              
188              
189             =head1 EXAMPLE
190              
191             For example, C<HTTP::Proxy> 0.14 suffered from strange warnings, and
192             its author Philippe Bruhat had a hard time trying to understand where
193             they could come from.
194              
195             getsockname() on closed socket Symbol::GEN7 at /System/Library/Perl/darwin/IO/Socket.pm line 186.
196             Use of uninitialized value in numeric ne (!=) at /Library/Perl/HTTP/Daemon.pm line 53.
197              
198             Hmm.. There's obviously something wrong here, but spotting the right
199             line is not easy.
200              
201             Re-running the same script, unchanged, by just adding C<-MDevel::SimpleTrace>
202             to C<perl> arguments produces the following output:
203              
204             getsockname() on closed socket Symbol::GEN7
205             at IO::Socket::sockname(/System/Library/Perl/darwin/IO/Socket.pm:186)
206             at IO::Socket::INET::sockport(/System/Library/Perl/IO/Socket/INET.pm:231)
207             at HTTP::Daemon::url(/Library/Perl/HTTP/Daemon.pm:52)
208             at HTTP::Daemon::ClientConn::get_request(/Library/Perl/HTTP/Daemon.pm:139)
209             at HTTP::Proxy::serve_connections(/Library/Perl/HTTP/Proxy.pm:500)
210             at HTTP::Proxy::start(/Library/Perl/HTTP/Proxy.pm:392)
211             at t::Utils::fork_proxy(t/Utils.pm:72)
212             at main::(t/50standard.t:138)
213             Use of uninitialized value in numeric ne (!=)
214             at HTTP::Daemon::url(/Library/Perl/HTTP/Daemon.pm:53)
215             at HTTP::Daemon::ClientConn::get_request(/Library/Perl/HTTP/Daemon.pm:139)
216             at HTTP::Proxy::serve_connections(/Library/Perl/HTTP/Proxy.pm:500)
217             at HTTP::Proxy::start(/Library/Perl/HTTP/Proxy.pm:392)
218             at t::Utils::fork_proxy(t/Utils.pm:72)
219             at main::(t/50standard.t:138)
220              
221             Aha! Much better. Finding the bug is now a trivial task C<;-)>
222              
223              
224             =head1 DIAGNOSTICS
225              
226             =over 4
227              
228             =item Unknown option: %s
229              
230             B<(W)> This warning occurs if you try to set an unknown option.
231              
232             =back
233              
234              
235             =head1 CAVEATS
236              
237             This module is currently not compatible with other modules that also
238             work by overriding C<die()> and C<warn()>, like C<CGI::Carp>.
239              
240              
241             =head1 AUTHOR
242              
243             SE<eacute>bastien Aperghis-Tramoni E<lt>sebastien@aperghis.netE<gt>
244              
245              
246             =head1 BUGS
247              
248             Please report any bugs or feature requests to
249             C<bug-Devel-SimpleTrace@rt.cpan.org>, or through the web interface at
250             L<https://rt.cpan.org/Public/Dist/Display.html?Name=Devel-SimpleTrace>.
251             I will be notified, and then you'll automatically be notified of
252             progress on your bug as I make changes.
253              
254              
255             =head1 COPYRIGHT & LICENSE
256              
257             Devel::SimpleTrace is Copyright (C)2004-2011 SE<eacute>bastien Aperghis-Tramoni.
258              
259             This program is free software. You can redistribute it and/or modify it
260             under the same terms as Perl itself.
261              
262             =cut