File Coverage

blib/lib/POE/Devel/ProcAlike/POEInfo.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             # Declare our package
2             package POE::Devel::ProcAlike::POEInfo;
3 1     1   1629 use strict; use warnings;
  1     1   4  
  1         53  
  1         7  
  1         1  
  1         38  
4              
5             # Initialize our version
6 1     1   7 use vars qw( $VERSION );
  1         3  
  1         57  
7             $VERSION = '0.02';
8              
9             # Set our superclass
10 1     1   5 use base 'Filesys::Virtual::Async::inMemory';
  1         2  
  1         351  
11              
12             # portable tools
13 1     1   18 use File::Spec;
  1         3  
  1         31  
14              
15             # import the useful $poe_kernel
16 1     1   1437 use POE;
  0            
  0            
17             use POE::API::Peek;
18             my $api = POE::API::Peek->new();
19             my $have_stats = 0;
20             my $have_eventprofile = 0;
21              
22             sub new {
23             # do we have stats available?
24             eval { $have_stats = POE::Kernel::TRACE_STATISTICS() };
25             if ( $@ ) {
26             $have_stats = 0;
27             }
28             eval { $have_eventprofile = POE::Kernel::TRACE_PROFILE() };
29             if ( $@ ) {
30             $have_eventprofile = 0;
31             } else {
32             # do we have a new-enough POE to introspect the profile data?
33             if ( ! $poe_kernel->can( 'stat_getprofile' ) ) {
34             $have_eventprofile = 0;
35             }
36             }
37              
38             # make sure we set a readonly filesystem!
39             return __PACKAGE__->SUPER::new(
40             'readonly' => 1,
41             );
42             }
43              
44             #/kernel
45             # # place for kernel stuff
46             #
47             # id # $poe_kernel->ID
48             # is_running # $api->is_kernel_running
49             # which_loop # $poe_kernel->poe_kernel_loop
50             # safe_signals # $api->get_safe_signals
51             #
52             # active_session # $poe_kernel->get_active_session->ID
53             # active_event # $poe_kernel->get_active_event
54             #
55             # memory_size # $api->kernel_memory_size
56             # session_count # $api->session_count
57             # extref_count # $api->extref_count
58             # handle_count # $api->handle_count
59             # event_count # $poe_kernel->get_event_count
60             # next_event # $poe_kernel->get_next_event_time
61             #
62             # /statistics
63             # # stats gathered via TRACE_STATISTICS if available
64             #
65             # interval
66             #
67             # blocked
68             # blocked_seconds
69             # idle_seconds
70             # total_duration
71             # user_events
72             # user_seconds
73             #
74             # avg_blocked
75             # avg_blocked_seconds
76             # avg_idle_seconds
77             # avg_total_duration
78             # avg_user_events
79             # avg_user_seconds
80             #
81             # derived_idle
82             # derived_user
83             # derived_blocked
84             # derived_userload
85             #
86             # event_profile
87             #
88             # /eventqueue
89             # # a place for the event queue data ( basically a dump of POE::Queue::Array ) - from $api->event_queue_dump()
90             #
91             # /N
92             # # N is the ID of event in the queue
93             #
94             # id
95             # index
96             # priority
97             # event
98             # source
99             # destination
100             # type
101             #
102             # /sessions
103             # # place for all session info ( like /proc/pid ) - from $api->session_list
104             #
105             # /id
106             # # the id is the session ID
107             #
108             # id # $session->ID
109             # type # ref( $session )
110             # memory_size # $api->session_memory_size( $session )
111             # extref_count # $api->get_session_extref_count( $session )
112             # handle_count # $api->session_handle_count( $session )
113             #
114             # events_to # $api->event_count_to( $session )
115             # events_from # $api->event_count_from( $session )
116             # event_profile # $kernel->stat_getprofile( $session )
117             #
118             # watched_signals # $api->signals_watched_by_session( $session )
119             # events # $api->session_event_list( $session )
120             # aliases # $api->session_alias_list( $session )
121             #
122             # heap # Data::Dumper( $session->get_heap() )
123             my %fs = (
124             'id' => $poe_kernel->ID . "\n",
125             'is_running' => [ $api, 'is_kernel_running' ],
126             'which_loop' => $poe_kernel->poe_kernel_loop . "\n",
127             'safe_signals' => join( "\n", $api->get_safe_signals() ) . "\n",
128             'active_session' => [ $poe_kernel, 'get_active_session', sub { $_[0]->ID } ],
129             'active_event' => [ $poe_kernel, 'get_active_event' ],
130             # 'memory_size' => [ $api, 'kernel_memory_size' ],
131             'session_count' => [ $api, 'session_count', sub { $_[0] - 1 } ],
132             'extref_count' => [ $api, 'extref_count' ],
133             'handle_count' => [ $api, 'handle_count' ],
134             'event_count' => [ $poe_kernel, 'get_event_count' ],
135             'next_event' => [ $poe_kernel, 'get_next_event_time' ],
136              
137             'statistics' => \&manage_statistics,
138              
139             'eventqueue' => \&manage_queue,
140              
141             'sessions' => \&manage_sessions,
142             );
143              
144             # helper sub to keep track of stat variables
145             sub _get_statistics_metrics {
146             my @stats;
147              
148             # do we have event profiling?
149             if ( $have_eventprofile ) {
150             push( @stats, 'event_profile' );
151             }
152             if ( $have_stats ) {
153             push( @stats, qw( blocked blocked_seconds idle_seconds interval total_duration user_events user_seconds
154             avg_blocked avg_blocked_seconds avg_idle_seconds avg_user_events avg_user_seconds
155             derived_idle derived_user derived_blocked derived_userload
156             ) );
157             }
158              
159             return \@stats;
160             }
161             sub _get_statistics_metric {
162             my $metric = shift;
163              
164             # what metric?
165             if ( $metric eq 'event_profile' ) {
166             my %profile = $poe_kernel->stat_getprofile();
167              
168             # do we have stats?
169             if ( keys %profile == 0 ) {
170             return "\n";
171             }
172              
173             my $data = '';
174             foreach my $p ( keys %profile ) {
175             $data .= $profile{ $p } . ":$p\n";
176             }
177             return $data;
178             } else {
179             my %average = $poe_kernel->stat_getdata();
180              
181             # do we have stats?
182             if ( keys %average == 0 ) {
183             return "\n";
184             }
185              
186             # derived require calculations
187             if ( $metric =~ /^derived/ ) {
188             # Division by zero sucks.
189             $average{'interval'} ||= 1;
190             $average{'user_events'} ||= 1;
191              
192             if ( $metric eq 'derived_idle' ) {
193             return sprintf( "%2.1f%%\n", 100 * $average{'avg_idle_seconds'} / $average{'interval'} );
194             } elsif ( $metric eq 'derived_user' ) {
195             return sprintf( "%2.1f%%\n", 100 * $average{'avg_user_seconds'} / $average{'interval'} );
196             } elsif ( $metric eq 'derived_blocked' ) {
197             return sprintf( "%2.1f%%\n", 100 * $average{'avg_blocked'} / $average{'user_events'} );
198             } elsif ( $metric eq 'derived_userload' ) {
199             return sprintf( "%2.1f%%\n", 100 * $average{'avg_user_events'} / $average{'interval'} );
200             }
201             } else {
202             # simple hash access
203             return $average{ $metric } . "\n";
204             }
205             }
206             }
207              
208             sub manage_statistics {
209             my( $type, @path ) = @_;
210              
211             # what's the operation?
212             if ( $type eq 'readdir' ) {
213             return _get_statistics_metrics();
214             } elsif ( $type eq 'stat' ) {
215             # set some default data
216             my ($atime, $ctime, $mtime, $size, $modes);
217             $atime = $ctime = $mtime = time();
218             my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = ( 0, 0, 0, 1, (split( /\s+/, $) ))[0], $>, 1, 1024 );
219              
220             # trying to stat the dir or stuff inside it?
221             if ( defined $path[0] ) {
222             # is it a valid stat metric?
223             if ( ! grep { $_ eq $path[0] } @{ _get_statistics_metrics() } or defined $path[1] ) {
224             return;
225             }
226              
227             # a file, munge the data
228             $size = length( _get_statistics_metric( $path[0] ) );
229             $modes = oct( '100644' );
230             } else {
231             # a directory, munge the data
232             $size = 0;
233             $modes = oct( '040755' );
234             $nlink = 2;
235             }
236              
237             # finally, return the darn data!
238             return( [ $dev, $ino, $modes, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ] );
239             } elsif ( $type eq 'open' ) {
240             # is it a valid stat metric?
241             if ( ! grep { $_ eq $path[0] } @{ _get_statistics_metrics() } or defined $path[1] ) {
242             return;
243             }
244              
245             # return a scalar ref
246             my $data = _get_statistics_metric( $path[0] );
247             return \$data;
248             }
249             }
250              
251             # helper sub to simplify queue item processing
252             sub _get_queue_metrics {
253             return [ qw( id index priority event source destination type ) ];
254             }
255             sub _get_queue_metric {
256             my $queuedata = shift;
257             my $metric = shift;
258              
259             # some metrics require manipulation
260             if ( $metric eq 'source' or $metric eq 'destination' ) {
261             if ( ref $queuedata->{ $metric } ) {
262             return $queuedata->{ $metric }->ID . "\n";
263             }
264             }
265              
266             # simple hash access
267             return $queuedata->{ $metric } . "\n";
268             }
269              
270             sub manage_queue {
271             my( $type, @path ) = @_;
272              
273             # what's the operation?
274             if ( $type eq 'readdir' ) {
275             # trying to read the root or the queue event itself?
276             if ( defined $path[0] ) {
277             return _get_queue_metrics();
278             } else {
279             # get the queue events
280             my @queue = map { $_->{'ID'} } $api->event_queue_dump();
281             return \@queue;
282             }
283             } elsif ( $type eq 'stat' ) {
284             # set some default data
285             my ($atime, $ctime, $mtime, $size, $modes);
286             $atime = $ctime = $mtime = time();
287             my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = ( 0, 0, 0, 1, (split( /\s+/, $) ))[0], $>, 1, 1024 );
288              
289             # get the data to start off
290             my @queue = $api->event_queue_dump();
291              
292             # trying to stat the dir or stuff inside it?
293             if ( defined $path[0] ) {
294             # does the id exist?
295             my @data = grep { $_->{'ID'} eq $path[0] } @queue;
296             if ( ! @data ) {
297             return;
298             }
299              
300             # trying to stat the queue id or data inside it?
301             if ( defined $path[1] ) {
302             # is it a valid queue metric?
303             if ( ! grep { $_ eq $path[1] } @{ _get_queue_metrics() } or defined $path[2] ) {
304             return;
305             }
306              
307             # a file, munge the data
308             $size = length( _get_queue_metric( $data[0], $path[1] ) );
309             $modes = oct( '100644' );
310             } else {
311             # a directory, munge the data
312             $size = 0;
313             $modes = oct( '040755' );
314             $nlink = 2;
315             }
316             } else {
317             # a directory, munge the data
318             $size = 0;
319             $modes = oct( '040755' );
320             $nlink = 2 + scalar @queue;
321             }
322              
323             # finally, return the darn data!
324             return( [ $dev, $ino, $modes, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ] );
325             } elsif ( $type eq 'open' ) {
326             # get the data to start off
327             my @queue = $api->event_queue_dump();
328              
329             my @data = grep { $_->{'ID'} eq $path[0] } @queue;
330             if ( ! @data or ! defined $path[1] ) {
331             return;
332             }
333              
334             # is it a valid queue metric?
335             if ( ! grep { $_ eq $path[1] } @{ _get_queue_metrics() } or defined $path[2] ) {
336             return;
337             }
338              
339             # get the metric!
340             my $data = _get_queue_metric( $data[0], $path[1] );
341             return \$data;
342             }
343             }
344              
345             # helper sub to simplify session item processing
346             sub _get_sessions_metrics {
347             my @stats;
348              
349             # removed memory_size, watched_signals due to complications
350             push( @stats, qw( id type extref_count handle_count events_to events_from
351             events aliases heap
352             ) );
353              
354             # do we have profiling?
355             if ( $have_eventprofile ) {
356             push( @stats, 'event_profile' );
357             }
358              
359             return \@stats;
360             }
361             sub _get_sessions_metric {
362             my $session = shift;
363             my $metric = shift;
364              
365             # determine what to do
366             if ( $metric eq 'id' ) {
367             return $session->ID . "\n";
368             } elsif ( $metric eq 'type' ) {
369             return ref( $session ) . "\n";
370             } elsif ( $metric eq 'memory_size' ) {
371             return $api->session_memory_size( $session ) . "\n";
372             } elsif ( $metric eq 'extref_count' ) {
373             return $api->get_session_extref_count( $session ) . "\n";
374             } elsif ( $metric eq 'handle_count' ) {
375             return $api->session_handle_count( $session ) . "\n";
376             } elsif ( $metric eq 'events_to' ) {
377             return $api->event_count_to( $session ) . "\n";
378             } elsif ( $metric eq 'events_from' ) {
379             return $api->event_count_from( $session ) . "\n";
380             } elsif ( $metric eq 'watched_signals' ) {
381             return join( "\n", $api->signals_watched_by_session( $session ) ) . "\n";
382             } elsif ( $metric eq 'events' ) {
383             return join( "\n", $api->session_event_list( $session ) ) . "\n";
384             } elsif ( $metric eq 'aliases' ) {
385             return join( "\n", $api->session_alias_list( $session ) ) . "\n";
386             } elsif ( $metric eq 'heap' ) {
387             require Data::Dumper;
388              
389             # make sure we have "consistent" data
390             no warnings; # shutup "possible used only once" warning!
391             local $Data::Dumper::Terse = 1;
392             local $Data::Dumper::Sortkeys = 1;
393             use warnings;
394              
395             return Data::Dumper::Dumper( $session->get_heap() );
396             } elsif ( $metric eq 'event_profile' ) {
397             my %profile = $poe_kernel->stat_getprofile( $session );
398              
399             # do we have stats?
400             if ( keys %profile == 0 ) {
401             return "\n";
402             }
403              
404             my $data = '';
405             foreach my $p ( keys %profile ) {
406             $data .= $profile{ $p } . ":$p\n";
407             }
408             return $data;
409             } else {
410             die "unknown sessions metric: $metric\n";
411             }
412             }
413              
414             sub manage_sessions {
415             my( $type, @path ) = @_;
416              
417             # what's the operation?
418             if ( $type eq 'readdir' ) {
419             # trying to read the root or the session itself?
420             if ( defined $path[0] ) {
421             return _get_sessions_metrics();
422             } else {
423             # get the sessions
424             my @sessions = map { $_->ID } $api->session_list();
425             return \@sessions;
426             }
427             } elsif ( $type eq 'stat' ) {
428             # set some default data
429             my ($atime, $ctime, $mtime, $size, $modes);
430             $atime = $ctime = $mtime = time();
431             my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = ( 0, 0, 0, 1, (split( /\s+/, $) ))[0], $>, 1, 1024 );
432              
433             # get the data to start off
434             my @sessions = $api->session_list();
435              
436             # trying to stat the dir or stuff inside it?
437             if ( defined $path[0] ) {
438             # does the id exist?
439             my @data = grep { $_->ID eq $path[0] } @sessions;
440             if ( ! @data ) {
441             return;
442             }
443              
444             # trying to stat the session id or data inside it?
445             if ( defined $path[1] ) {
446             # is it a valid session metric?
447             if ( ! grep { $_ eq $path[1] } @{ _get_sessions_metrics() } or defined $path[2] ) {
448             return;
449             }
450              
451             # a file, munge the data
452             $size = length( _get_sessions_metric( $data[0], $path[1] ) );
453             $modes = oct( '100644' );
454             } else {
455             # a directory, munge the data
456             $size = 0;
457             $modes = oct( '040755' );
458             $nlink = 2;
459             }
460             } else {
461             # a directory, munge the data
462             $size = 0;
463             $modes = oct( '040755' );
464             $nlink = 2 + scalar @sessions;
465             }
466              
467             # finally, return the darn data!
468             return( [ $dev, $ino, $modes, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ] );
469             } elsif ( $type eq 'open' ) {
470             # get the data to start off
471             my @sessions = $api->session_list();
472              
473             my @data = grep { $_->ID eq $path[0] } @sessions;
474             if ( ! @data or ! defined $path[1] ) {
475             return;
476             }
477              
478             # is it a valid session metric?
479             if ( ! grep { $_ eq $path[1] } @{ _get_sessions_metrics() } or defined $path[2] ) {
480             return;
481             }
482              
483             # get the metric!
484             my $data = _get_sessions_metric( $data[0], $path[1] );
485             return \$data;
486             }
487             }
488              
489             # we cheat here and not implement a lot of stuff because we know the FUSE api never calls the "extra" APIs
490             # that ::Async provides. Furthermore, this is a read-only filesystem so we can skip even more APIs :)
491              
492             # _rmtree
493              
494             # _scandir
495              
496             # _move
497              
498             # _copy
499              
500             # _load
501              
502             sub _readdir {
503             my( $self, $path ) = @_;
504              
505             if ( $path eq File::Spec->rootdir() ) {
506             return [ keys %fs ];
507             } else {
508             # sanitize the path
509             my @dirs = File::Spec->splitdir( $path );
510             shift( @dirs ); # get rid of the root entry which is always '' for me
511             return $fs{ $dirs[0] }->( 'readdir', @dirs[ 1 .. $#dirs ] );
512             }
513             }
514              
515             # _rmdir
516              
517             # _mkdir
518              
519             # _rename
520              
521             # _mknod
522              
523             # _unlink
524              
525             # _chmod
526              
527             # _truncate
528              
529             # _chown
530              
531             # _utime
532              
533             # helper to process ARRAY fs type
534             sub _stat_arraymode {
535             my $file = shift;
536              
537             my $method = $fs{ $file }->[1];
538             my $data = $fs{ $file }->[0]->$method();
539              
540             # do we need to do more munging?
541             if ( defined $fs{ $file }->[2] ) {
542             $data = $fs{ $file }->[2]->( $data );
543             }
544              
545             # all done!
546             return $data . "\n";
547             }
548              
549             sub _stat {
550             my( $self, $path ) = @_;
551              
552             # stating the root?
553             if ( $path eq File::Spec->rootdir() ) {
554             my ($atime, $ctime, $mtime, $size, $modes);
555             $atime = $ctime = $mtime = time();
556             my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = ( 0, 0, 0, 1, (split( /\s+/, $) ))[0], $>, 1, 1024 );
557             $size = 0;
558             $modes = oct( '040755' );
559              
560             # count subdirs
561             $nlink = 2 + grep { ref $fs{ $_ } and ref( $fs{ $_ } ) ne 'ARRAY' } keys %fs;
562              
563             return( [ $dev, $ino, $modes, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ] );
564             }
565              
566             # sanitize the path
567             my @dirs = File::Spec->splitdir( $path );
568             shift( @dirs ); # get rid of the root entry which is always '' for me
569             if ( exists $fs{ $dirs[0] } ) {
570             # arg, stat is a finicky beast!
571             my $modes = oct( '100644' );
572             my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = ( 0, 0, 0, 1, (split( /\s+/, $) ))[0], $>, 1, 1024 );
573             my ($atime, $ctime, $mtime, $size);
574             $atime = $ctime = $mtime = time();
575              
576             # directory or file?
577             if ( ref $fs{ $dirs[0] } ) {
578             # array or code?
579             if ( ref( $fs{ $dirs[0] } ) eq 'ARRAY' ) {
580             # array operation, do what the data tells us to do!
581             $size = length( _stat_arraymode( $dirs[0] ) );
582             } else {
583             # trying to stat the dir or the subpath?
584             return $fs{ $dirs[0] }->( 'stat', @dirs[ 1 .. $#dirs ] );
585             }
586             } else {
587             # arg, stat is a finicky beast!
588             $size = length( $fs{ $dirs[0] } );
589             }
590              
591             # finally, return the darn data!
592             return( [ $dev, $ino, $modes, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ] );
593             } else {
594             return;
595             }
596             }
597              
598             # _write
599              
600             sub _open {
601             my( $self, $path ) = @_;
602              
603             # sanitize the path
604             my @dirs = File::Spec->splitdir( $path );
605             shift( @dirs ); # get rid of the root entry which is always '' for me
606             if ( exists $fs{ $dirs[0] } ) {
607             # directory or file?
608             if ( ref $fs{ $dirs[0] } ) {
609             # array or code?
610             if ( ref( $fs{ $dirs[0] } ) eq 'ARRAY' ) {
611             # array operation, do what the data tells us to do!
612             my $data = _stat_arraymode( $dirs[0] );
613             return \$data;
614             } else {
615             return $fs{ $dirs[0] }->( 'open', @dirs[ 1 .. $#dirs ] );
616             }
617             } else {
618             # return a scalar ref
619             return \$fs{ $dirs[0] };
620             }
621             } else {
622             return;
623             }
624             }
625              
626             1;
627             __END__