File Coverage

blib/lib/Devel/STrace/Monitor.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #/**
2             # Provides a minimal strace/truss-like utility for
3             # Perl scripts. Using
4             # Devel::RingBuffer, each new subroutine call is logged to an mmap'ed shared memory
5             # region (as provided by IPC::Mmap).
6             # As each statement is executed, the line number and Time::HiRes:;time() timestamp
7             # are written to the current ringbuffer slot. An external application can
8             # then monitor a running application by inspecting the mmap'ed area.
9             #

10             # Permission is granted to use this software under the same terms as Perl itself.
11             # Refer to the Perl Artistic License
12             # for details.
13             #
14             # @author D. Arnold
15             # @since 2006-05-01
16             # @see Devel::RingBuffer
17             # @see IPC::Mmap
18             # @see Devel::STrace
19             # @see perdebguts
20             # @self $self
21             #*/
22             package Devel::STrace::Monitor;
23              
24             require 5.008;
25 1     1     use Devel::RingBuffer;
  0            
  0            
26              
27             our $VERSION = '0.30';
28              
29             use strict;
30             use warnings;
31              
32             #/**
33             # Constructor. Opens the specified filename, or,
34             # if no filename is specified, the filename specified by
35             # the DEVEL_RINGBUF_FILE environment variable, using
36             # Devel::RingBuffer.
37             # Performs an initial scan of the file to create a PID/TID buffer map.
38             #
39             # @static
40             # @param $file name of the mmap()'d file (or namespace on Win32)
41             # @return on success, a new Devel::STrace::Monitor object;
42             # undef on failure.
43             #*/
44             sub open {
45             my ($class, $file) = @_;
46              
47             $file = $ENV{DEVEL_RINGBUF_FILE}
48             unless $file;
49              
50             #print $file, "\n";
51             my $ringbuffer = Devel::RingBuffer->monitor($file)
52             or return undef;
53              
54             my $self = bless {
55             _ring => $ringbuffer,
56             _filename => $file,
57             _map => {},
58             _slots => $ringbuffer->getSlots()
59             }, $class;
60             #
61             # load the map
62             #
63             # my @headers = $ringbuffer->getHeader();
64             # print "header is
65             # single: $headers[0]
66             # msgarea_sz: $headers[1]
67             # max_buffers: $headers[2]
68             # slots: $headers[3]
69             # slot_sz: $headers[4]
70             # stop_on_create: $headers[5]
71             # trace_on_create: $headers[6]
72             # global_sz: $headers[7]
73             # globmsg_total: $headers[8]
74             # globmsg_sz: $headers[9]
75             # ";
76              
77             return $self->refresh();
78             }
79              
80             #/**
81             # Refresh the PID/TID buffer map.
82             # Scans the mmap'ed file to refresh the PID/TID buffer map.
83             # (in order to collect buffers for new threads/processes, or to discard
84             # old buffers for threads/processes which have terminated)
85             #
86             # @return the Devel::STrace::Monitor object
87             #*/
88             sub refresh {
89             my $self = shift;
90              
91             my @bufmap = $self->{_ring}->getMap();
92             my $map = $self->{_map} = {};
93             #
94             # optimization: only inspect buffers that are alloc'd
95             #
96             my ($pid, $tid, $current, $depth);
97             foreach (0..$#bufmap) {
98             next
99             if $bufmap[$_];
100              
101             my $ring = $self->{_ring}->getRing($_);
102             ($pid, $tid, $current, $depth) = $ring->getHeader();
103             $map->{"$pid:$tid"} = $ring;
104             }
105             return $self;
106             }
107              
108             #/**
109             # Dump the mmap'ed ringbuffer file contents.
110             # Scans the mmap'ed file to refresh the PID/TID buffer map.
111             # (in order to collect buffers for new threads/processes, or to discard
112             # old buffers for threads/processes which have terminated)
113             #
114             # @param $trace_cb callback to which ringbuffer contents are posted
115             # @param @pid_tid_list optional list of PID's, or "PID:TID" keys
116             # for which ringbuffer contents are to be returned;
117             # if none are specified, all PID/TID keys are used;
118             # if only a PID is specified, all threads for the process
119             # are used.
120             #
121             # @return the Devel::STrace::Monitor object
122             #*/
123             sub trace {
124             my $self = shift;
125             my $trace_cb = shift;
126             #
127             # if pids or pid:tid's provided, return them
128             #
129             my @keys = sort keys %{$self->{_map}};
130             if (scalar @_) {
131             foreach my $pid (@_) {
132             #
133             # if full key, get it
134             #
135             $self->_get_trace($pid, $trace_cb),
136             next
137             if exists $self->{_map}{$pid};
138             #
139             # else scan for all matching pids
140             #
141             foreach (@keys) {
142             $self->_get_trace($_, $trace_cb)
143             if /^$pid:/;
144             }
145             }
146             return $self;
147             }
148             #
149             # else dump everything
150             #
151             $self->_get_trace($_, $trace_cb)
152             foreach (@keys);
153              
154             return $self;
155             }
156              
157             sub _get_trace {
158             my ($self, $key, $cb) = @_;
159              
160             my $ring = $self->{_map}{$key};
161             return undef unless $ring;
162              
163             my ($pid, $tid, $current, $depth) = $ring->getHeader();
164              
165             my $slot;
166             my $slots = $self->{_slots};
167             my ($trace, $line, $time);
168              
169             $slots = $depth if ($depth < $slots);
170             foreach (1..$slots) {
171             ($line, $time, $trace) = $ring->getSlot($current);
172             &$cb($key, $current, $depth, $line, $time, $trace);
173             $current--;
174             $current = $slots - 1 if ($current < 0);
175             }
176              
177             return $self;
178             }
179              
180             #/**
181             # Set the current ringbuffer global single
182             # control variable value. Setting this to a non-zero
183             # value causes Devel::STrace to trace data for all threads
184             # of all processes; setting it to zero may disable
185             # tracing, but only if the per-thread trace and signal
186             # control variables are also set to zero.
187             #
188             # @param $value new value to assign to single
189             #
190             # @return the prior value of the Devel::RingBuffer global single value
191             #*/
192             sub setSingle {
193             my $single = $_[0]->{_ring}->getSingle();
194             $_[0]->{_ring}->setSingle($_[1]);
195             return $single;
196             }
197              
198             #/**
199             # Get the current ringbuffer global single
200             # control variable value.
201             #
202             # @return the current Devel::RingBuffer global single value
203             #*/
204             sub getSingle {
205             return $_[0]->{_ring}->getSingle();
206             }
207             #/**
208             # Set the ringbuffer per-thread signal
209             # control variable value for the specified PID or PID:TID.
210             # Setting this to a non-zero
211             # value causes Devel::STrace to trace data for the specified threads
212             # of the specified processes; setting it to zero may disable
213             # tracing, but only if the global single variable, and the
214             # per-thread trace control variables are also set to zero.
215             #
216             # @param @pid_tid_list optional list of PIDs, or "PID:TID", keys to set signal on;
217             # if no keys are specified, all keys are used
218             # @param $value new value to assign to signal
219             #
220             # @return a hash of the prior values of the Devel::RingBuffer signal values, keyed
221             # by the "PID:TID"
222             #*/
223             sub setSignal {
224             my $self = shift;
225             my $value = pop;
226             my %pidtids = ();
227             if (scalar @_) {
228             foreach my $pidtid (keys %{$self->{_map}}) {
229             foreach (@_) {
230             $pidtids{$_} = $self->{_map}{$_}->getSignal(),
231             $self->{_map}{$_}->setSignal($value)
232             if ($_ eq $pidtid) ||
233             (substr($_, 0, length($pidtid) + 1) eq "$pidtid:");
234             }
235             }
236             }
237             else {
238             $pidtids{$_} = $self->{_map}{$_}->getSignal(),
239             $self->{_map}{$_}->setSignal($value)
240             foreach (keys %{$self->{_map}});
241             }
242             return %pidtids;
243             }
244              
245             #/**
246             # Get the ringbuffer per-thread signal
247             # control variable value for the specified PIDs or PID:TIDs.
248             #
249             # @param @pid_tid_list optional list of PIDs, or "PID:TID", keys to get signal for;
250             # if no keys are specified, all keys are used
251             #
252             # @return a hash of the Devel::RingBuffer signal values, keyed
253             # by the "PID:TID"
254             #*/
255             sub getSignal {
256             my $self = shift;
257             my %pidtids = ();
258             if (scalar @_) {
259             foreach my $pidtid (keys %{$self->{_map}}) {
260             foreach (@_) {
261             $pidtids{$_} = $self->{_map}{$_}->getSignal()
262             if ($_ eq $pidtid) ||
263             (substr($_, 0, length($pidtid) + 1) eq "$pidtid:");
264             }
265             }
266             }
267             else {
268             $pidtids{$_} = $self->{_map}{$_}->getSignal()
269             foreach (keys %{$self->{_map}});
270             }
271             return %pidtids;
272             }
273              
274             #/**
275             # Set the ringbuffer per-thread trace
276             # control variable value for the specified PID or PID:TID.
277             # Setting this to a non-zero
278             # value causes Devel::STrace to trace data for the specified threads
279             # of the specified processes; setting it to zero may disable
280             # tracing, but only if the global single variable, and the
281             # per-thread signal control variables are also set to zero.
282             #
283             # @param @pid_tid_list optional list of PIDs, or "PID:TID", keys to set trace on;
284             # if no keys are specified, all keys are used
285             # @param $value new value to assign to trace
286             #
287             # @return a hash of the prior values of the Devel::RingBuffer trace values, keyed
288             # by the "PID:TID"
289             #*/
290             sub setTrace {
291             my $self = shift;
292             my $value = pop;
293             my %pidtids = ();
294             if (scalar @_) {
295             foreach my $pidtid (keys %{$self->{_map}}) {
296             foreach (@_) {
297             $pidtids{$_} = $self->{_map}{$_}->getTrace(),
298             $self->{_map}{$_}->setTrace($value)
299             if ($_ eq $pidtid) ||
300             (substr($_, 0, length($pidtid) + 1) eq "$pidtid:");
301             }
302             }
303             }
304             else {
305             $pidtids{$_} = $self->{_map}{$_}->getTrace(),
306             $self->{_map}{$_}->setTrace($value)
307             foreach (keys %{$self->{_map}});
308             }
309             return %pidtids;
310             }
311              
312             #/**
313             # Get the ringbuffer per-thread trace
314             # control variable value for the specified PIDs or PID:TIDs.
315             #
316             # @param @pid_tid_list optional list of PIDs, or "PID:TID", keys to get trace for;
317             # if no keys are specified, all keys are used
318             #
319             # @return a hash of the Devel::RingBuffer trace values, keyed
320             # by the "PID:TID"
321             #*/
322             sub getTrace {
323             my $self = shift;
324             my %pidtids = ();
325             if (scalar @_) {
326             foreach my $pidtid (keys %{$self->{_map}}) {
327             foreach (@_) {
328             $pidtids{$_} = $self->{_map}{$_}->getTrace()
329             if ($_ eq $pidtid) ||
330             (substr($_, 0, length($pidtid) + 1) eq "$pidtid:");
331             }
332             }
333             }
334             else {
335             $pidtids{$_} = $self->{_map}{$_}->getTrace()
336             foreach (keys %{$self->{_map}});
337             }
338             return %pidtids;
339             }
340              
341              
342             #/**
343             # Get the current list of PID:TID keys.
344             #
345             # @return a list of currently active PID:TID keys from the Devel::RingBuffer
346             #*/
347             sub getPIDTIDs {
348             my $self = shift;
349             return sort keys %{$self->{_map}};
350             }
351              
352             1;