File Coverage

blib/lib/Runops/Trace.pm
Criterion Covered Total %
statement 39 45 86.6
branch 4 8 50.0
condition n/a
subroutine 13 15 86.6
pod 5 5 100.0
total 61 73 83.5


line stmt bran cond sub pod time code
1             package Runops::Trace;
2             # ABSTRACT: Trace your program's execution
3              
4             # vim:shiftwidth=4
5              
6 11     11   133028 use strict;
  11         25  
  11         456  
7 11     11   294 use warnings;
  11         16  
  11         274  
8 11     11   69 use Digest::MD5 ();
  11         19  
  11         167  
9 11     11   52 use File::Spec ();
  11         21  
  11         185  
10 11     11   59 use Scalar::Util ();
  11         19  
  11         325  
11              
12             our $VERSION = '0.14';
13              
14 11     11   53 use DynaLoader ();
  11         19  
  11         6484  
15             our @ISA = qw( DynaLoader Exporter );
16             Runops::Trace->bootstrap($VERSION);
17              
18             our @EXPORT_OK = qw(
19             trace_code checksum_code_path trace
20              
21             set_tracer get_tracer clear_tracer enable_tracing disable_tracing tracing_enabled
22              
23             set_trace_threshold get_trace_threshold get_op_counters
24             );
25              
26             our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
27              
28             sub checksum_code_path {
29 4     4 1 28 my ($f) = @_;
30              
31             # Preallocate a 2**19 byte long string. See
32             # http://perlmonks.org/?node_id=630323
33 4         140 open my $nul, '<', File::Spec->devnull;
34 4         7 my $ops = '';
35 4         80 sysread $nul, $ops, 2**19, 0;
36              
37             # Just stash the pointers.
38 4 50   56   62 _trace_function( sub { $ops .= ${ $_[0] || return } }, $f );
  56     2   110  
  56         348  
  2         21  
  2         5  
  2         44  
39              
40 4         95 return Digest::MD5::md5_hex($ops);
41             }
42              
43             sub trace_code {
44 3     3 1 31 my ($f) = @_;
45 3         6 my @ops;
46 3     14   63 _trace_function( sub { push @ops, $_[0] }, $f );
  14         79  
47              
48 3 50       46 return wantarray ? @ops : join "\n", @ops;
49             }
50              
51             sub trace {
52 0     0 1 0 my ( $tracer, $callback ) = @_;
53              
54 0         0 _trace_function( $tracer, $callback );
55 0         0 return;
56             }
57              
58             sub unmask_op {
59 1     1 1 7 unmask_op_type( _whatever_to_op_type($_) ) for @_;
60             }
61              
62             sub mask_op {
63 0     0 1 0 mask_op_type( _whatever_to_op_type($_) ) for @_;
64             }
65              
66             sub _whatever_to_op_type {
67 1     1   4 my $thingy = shift;
68              
69 1 50       9 if ( ref $thingy ) {
    50          
70 0         0 return $thingy->type;
71             } elsif ( Scalar::Util::looks_like_number($thingy) ) {
72 0         0 return $thingy;
73             } else {
74 1         7 require B;
75 1         17 return B::opnumber($thingy);
76             }
77             }
78              
79             1;
80              
81              
82              
83             =pod
84              
85             =head1 NAME
86              
87             Runops::Trace - Trace your program's execution
88              
89             =head1 VERSION
90              
91             version 0.14
92              
93             =head1 SYNOPSIS
94              
95             Per function tracing:
96              
97             use Runops::Trace 'checksum_code_path';
98             sub is_even { shift() % 2 == 0 ? 1 : 0 }
99              
100             my %sufficient;
101             for my $number ( 0 .. 10 ) {
102             # Get a signature for the code
103             my $codepath = checksum_code_path(
104             sub { is_even( $number ) }
105             );
106              
107             if ( not exists $sufficient{$codepath} ) {
108             $sufficient{$codepath} = $number;
109             }
110             }
111             print join ' ', keys %sufficient;
112              
113             Global tracing
114              
115             =head1 DESCRIPTION
116              
117             This module traces opcodes as they are executed by the perl VM. The
118             trace function can be turned on globally or just during the execution
119             of a single function.
120              
121             =head1 INTERFACE
122              
123             =over
124              
125             =item trace( TRACE, FUNCTION )
126              
127             This is a generic way of tracing a function. It ensures that your
128             C function is called before every operation in the C
129             function.
130              
131             The C function will be given the pointer of the opnode that is
132             about to be run. This is an interim API. The desired result is that a
133             B::OP object is passed instead of just the pointer. Also, it is always
134             the same scalar - only the value is changing. References taken to this
135             value will mutate. The C function will be called in void
136             context.
137              
138             The C function will be called in void context and will not
139             be given any parameters.
140              
141             There is no useful return value from this function.
142              
143             =item MD5SUM = checksum_code_path( FUNCTION )
144              
145             This returns a hex MD5 checksum of the ops that were visited. This is
146             a nice, concise way of representing a unique path through code.
147              
148             =item STRING = trace_code( FUNCTION )
149              
150             =item ARRAY = trace_code( FUNCTION )
151              
152             This returns a string representing the ops that were executed. Each op
153             is represented as its name and hex address in memory.
154              
155             If called in list context will return the list of L objects.
156              
157             =item set_tracer( FUNCTION )
158              
159             This sets the tracer function globally.
160              
161             C uses this.
162              
163             The code reference will be called once per op. The first argument is the
164             L object for C. The second argument is the operator's arity. This
165             might later be changed if arity methods are included in L itself. The
166             remaining arguments are the arguments for the operator taken from the stack,
167             depending on the operator arity.
168              
169             =item CODEREF = get_tracer()
170              
171             Get the tracing sub (if any).
172              
173             =item clear_tracer()
174              
175             Remove the tracing sub.
176              
177             =item enable_tracing()
178              
179             =item disable_tracing()
180              
181             Controls tracing globally.
182              
183             =item tracing_enabled()
184              
185             Returns whether or not tracing is enabled.
186              
187             =item set_trace_threshold( INT )
188              
189             =item INT = get_trace_threshold()
190              
191             =item HASHREF = get_op_counters()
192              
193             If set to a nonzero value then every opcode will be counted in a hash
194             (C returns that hash).
195              
196             The trace function would only be triggerred after the counter for that opcode
197             has reached a certain number.
198              
199             This is useful for when you only want to trace a certain hot path.
200              
201             =item mask_all()
202              
203             Disable tracing of all ops.
204              
205             =item mask_none()
206              
207             =item unmask_all()
208              
209             Enable tracing of all ops.
210              
211             =item mask_op( OPTYPE )
212              
213             =item unmask_op( OPTYPE )
214              
215             Change the masking of a specific op.
216              
217             Takes a L object, an op type, or an op name.
218              
219             =item clear_mask()
220              
221             Like C was called, but removes the mask entirely.
222              
223             =item ARITY_NULL
224              
225             =item ARITY_UNARY
226              
227             =item ARITY_BINARY
228              
229             =item ARITY_LIST
230              
231             =item ARITY_LIST_UNARY
232              
233             =item ARITY_LIST_BINARY
234              
235             =item ARITY_UNKNOWN
236              
237             These constants can be used to inspect the arity paramter.
238              
239             Note that for C (C) and C
240             (C) the arity value is the binary or of C and
241             C or C. Test with C<&> or with C<==> according to
242             what you are really interested in.
243              
244             C means no arguments (e.g. an C).
245              
246             Some operators do not have their arity figured out yet. Patches welcome.
247              
248             This should ideally become a method of L later.
249              
250             =back
251              
252             =head1 PERL HACKS COMPATIBILITY
253              
254             This module does not currently implement the interface as described in
255             the O'Reilly book Perl Hacks.
256              
257             =head1 ADVANCED NOTES
258              
259             =over
260              
261             =item THREAD-UNSAFE
262              
263             I made no attempt at thread safety. Do not use this module in a
264             multi-threaded process.
265              
266             =item WRITE YOUR OWN SUGAR
267              
268             The C function is sufficient to allow any
269             arbitrary kind of access to running code. This module is included with
270             two simple functions to return useful values. Consider looking at
271             their source code and writing your own.
272              
273             =item ON THE FLY CODE MODIFICATION
274              
275             If the L module is loaded, the B:: object that is passed
276             to the tracing function may also be modified. This would allow you to
277             modify the perl program as it is running. Thi
278              
279             =back
280              
281             =head1 AUTHOR
282              
283             Rewritten by Joshua ben Jore, originally written by chromatic, based
284             on L by Rafael Garcia-Suarez.
285              
286             Merged with Runops::Hook by Chia-Liang Kao and Yuval Kogman.
287              
288             This program is free software; you may redistribute it and/or modify
289             it under the same terms as Perl 5.8.x itself.
290              
291             =head1 AUTHOR
292              
293             Josh Jore
294              
295             =head1 COPYRIGHT AND LICENSE
296              
297             This software is copyright (c) 2011 by Josh Jore.
298              
299             This is free software; you can redistribute it and/or modify it under
300             the same terms as the Perl 5 programming language system itself.
301              
302             =cut
303              
304              
305             __END__