[Jifty-commit] r4133 - in jifty/trunk: lib/Jifty lib/Jifty/Plugin lib/Jifty/Plugin/LeakDetector

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Tue Sep 18 16:07:19 EDT 2007


Author: sartak
Date: Tue Sep 18 16:07:18 2007
New Revision: 4133

Added:
   jifty/trunk/lib/Jifty/Plugin/LeakDetector/
   jifty/trunk/lib/Jifty/Plugin/LeakDetector.pm
   jifty/trunk/lib/Jifty/Plugin/LeakDetector/Dispatcher.pm
   jifty/trunk/lib/Jifty/Plugin/LeakDetector/View.pm
Modified:
   jifty/trunk/   (props changed)
   jifty/trunk/lib/Jifty/Handler.pm

Log:
 r42785 at onn:  sartak | 2007-09-18 16:06:26 -0400
 Add new LeakDetector plugin. It kinda sorta works :)


Modified: jifty/trunk/lib/Jifty/Handler.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Handler.pm	(original)
+++ jifty/trunk/lib/Jifty/Handler.pm	Tue Sep 18 16:07:18 2007
@@ -27,6 +27,7 @@
 use base qw/Class::Accessor::Fast/;
 use Module::Refresh ();
 use Jifty::View::Declare::Handler ();
+use Class::Trigger;
 
 BEGIN {
     # Creating a new CGI object breaks FastCGI in all sorts of painful
@@ -191,35 +192,44 @@
         @_
     );
 
-    # Build a new stash for the life of this request
-    $self->stash( {} );
-    local $Jifty::WEB = Jifty::Web->new();
-
-    if ( Jifty->config->framework('DevelMode') ) {
-        Module::Refresh->refresh;
-        Jifty::I18N->refresh;
-    }
+    $self->call_trigger('before_request', $args{cgi});
 
-    Jifty::I18N->get_language_handle;
+    # this is scoped deeper because we want to make sure everything is cleaned
+    # up for the LeakDetector plugin. I tried putting the triggers in the
+    # method (Jifty::Server::handle_request) that calls this, but Jifty::Server
+    # isn't being loaded in time
+    {
+        # Build a new stash for the life of this request
+        $self->stash( {} );
+        local $Jifty::WEB = Jifty::Web->new();
+
+        if ( Jifty->config->framework('DevelMode') ) {
+            Module::Refresh->refresh;
+            Jifty::I18N->refresh;
+        }
 
-    $self->cgi( $args{cgi} );
-    $self->apache( HTML::Mason::FakeApache->new( cgi => $self->cgi ) );
+        Jifty::I18N->get_language_handle;
 
-    Jifty->web->request( Jifty::Request->new()->fill( $self->cgi ) );
-    Jifty->web->response( Jifty::Response->new );
-    Jifty->api->reset;
-    for ( Jifty->plugins ) {
-        $_->new_request;
-    }
-    Jifty->log->debug( "Received request for " . Jifty->web->request->path );
-    Jifty->web->setup_session;
+        $self->cgi( $args{cgi} );
+        $self->apache( HTML::Mason::FakeApache->new( cgi => $self->cgi ) );
 
-    # Return from the continuation if need be
-    Jifty->web->request->return_from_continuation;
-    Jifty->web->session->set_cookie;
-    $self->dispatcher->handle_request();
-    $self->cleanup_request();
+        Jifty->web->request( Jifty::Request->new()->fill( $self->cgi ) );
+        Jifty->web->response( Jifty::Response->new );
+        Jifty->api->reset;
+        for ( Jifty->plugins ) {
+            $_->new_request;
+        }
+        Jifty->log->debug( "Received request for " . Jifty->web->request->path );
+        Jifty->web->setup_session;
+
+        # Return from the continuation if need be
+        Jifty->web->request->return_from_continuation;
+        Jifty->web->session->set_cookie;
+        $self->dispatcher->handle_request();
+        $self->cleanup_request();
+    }
 
+    $self->call_trigger('after_request', $args{cgi});
 }
 
 =head2 cleanup_request

Added: jifty/trunk/lib/Jifty/Plugin/LeakDetector.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/LeakDetector.pm	Tue Sep 18 16:07:18 2007
@@ -0,0 +1,101 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::LeakDetector;
+use base qw/Jifty::Plugin Class::Data::Inheritable/;
+use Data::Dumper;
+use Devel::Events::Handler::ObjectTracker;
+use Devel::Events::Generator::Objects;
+use Devel::Size 'total_size';
+
+our $VERSION = 0.01;
+
+__PACKAGE__->mk_accessors(qw(tracker generator));
+our @requests;
+
+my $empty_array = total_size([]);
+
+sub init {
+    my $self = shift;
+    return if $self->_pre_init;
+
+    Jifty::Handler->add_trigger(
+        before_request => sub { $self->before_request(@_) }
+    );
+
+    Jifty::Handler->add_trigger(
+        after_request  => sub { $self->after_request(@_) }
+    );
+}
+
+sub before_request
+{
+    my $self = shift;
+    $self->tracker(Devel::Events::Handler::ObjectTracker->new());
+    $self->generator(
+        Devel::Events::Generator::Objects->new(handler => $self->tracker)
+    );
+
+    $self->generator->enable();
+}
+
+sub after_request
+{
+    my $self = shift;
+    my $handler = shift;
+    my $cgi = shift;
+
+    $self->generator->disable();
+
+    my $leaked = $self->tracker->live_objects;
+    my $leaks = keys %$leaked;
+
+    # XXX: Devel::Size seems to segfault Jifty at END time
+    my $size = total_size([ keys %$leaked ]) - $empty_array;
+
+    push @requests, {
+        url => $cgi->url(-absolute=>1,-path_info=>1),
+        size => $size,
+        objects => Dumper($leaked),
+        time => scalar gmtime,
+        leaks => $leaks,
+    };
+
+    $self->generator(undef);
+    $self->tracker(undef);
+}
+
+=head1 NAME
+
+Jifty::Plugin::LeakDetector
+
+=head1 DESCRIPTION
+
+Memory leak detection and reporting for your Jifty app
+
+=head1 USAGE
+
+Add the following to your site_config.yml
+
+ framework:
+   Plugins:
+     - LeakDetector: {}
+
+This makes the following URLs available:
+
+View any URL of your app and catch any leaked objects
+
+    http://your.app/leak/user/12
+
+View the top-level leak report (how much each request has leaked)
+
+    http://your.app/leaks
+
+View an individual request's detailed leak report (which objects were leaked)
+
+    http://your.app/leaks/3
+
+=cut
+
+1;
+

Added: jifty/trunk/lib/Jifty/Plugin/LeakDetector/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/LeakDetector/Dispatcher.pm	Tue Sep 18 16:07:18 2007
@@ -0,0 +1,22 @@
+package Jifty::Plugin::LeakDetector::Dispatcher;
+use warnings;
+use strict;
+
+use Jifty::Dispatcher -base;
+
+# http://your.app/leaks -- display full leak report
+on 'leaks' => run {
+        show "leaks/all";
+};
+
+# http://your.app/leaks/xxx -- display leak report for request ID xxx
+on 'leaks/#' => run {
+    my $leak = $Jifty::Plugin::LeakDetector::requests[$1]
+        or abort(404);
+    set leak => $leak;
+    set leakid => $1;
+    show "leaks/one";
+};
+
+1;
+

Added: jifty/trunk/lib/Jifty/Plugin/LeakDetector/View.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/LeakDetector/View.pm	Tue Sep 18 16:07:18 2007
@@ -0,0 +1,82 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::LeakDetector::View;
+use Jifty::View::Declare -base;
+
+=head1 NAME
+
+Jifty::Plugin::LeakDetector::View - Views for memory leak detection
+
+=head1 TEMPLATES
+
+=head2 leaks/chart
+
+This shows a chart using L<Chart>. It expects to find the arguments in the C<args> parameter, which is setup for it in L<Jifty::Plugin::Chart::Dispatcher>.
+
+This will output a PNG file unless there is an error building the chart.
+
+=cut
+
+template 'leaks/all' => sub {
+    html {
+        body {
+            table {
+                row {
+                    th { "ID" }
+                    th { "Leaks" }
+                    th { "Bytes leaked" }
+                    th { "Time" }
+                    th { "URL" }
+                };
+
+                my $id = 0;
+                for (@Jifty::Plugin::LeakDetector::requests)
+                {
+                    row {
+                        cell { a { attr { href => "leaks/$id" } $id } }
+                        cell { $_->{leaks} }
+                        cell { $_->{size} }
+                        cell { $_->{time} }
+                        cell { $_->{url} }
+                    };
+                    ++$id;
+                }
+            }
+        }
+    }
+};
+
+template 'leaks/one' => sub {
+    my $leak = get 'leak';
+    my $id = get 'leakid';
+
+    html {
+        body {
+            h1 { "Leaks from Request $id" }
+            ul {
+                li { "URL: $leak->{url}" }
+                li { "Time: $leak->{time}" }
+                li { "Objects leaked: $leak->{leaks}" }
+                li { "Total memory leaked: $leak->{size}" }
+            }
+            p { a { attr { href => "/leaks" } "Table of Contents" } }
+            hr {}
+            pre { $leak->{objects} }
+        }
+    }
+};
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::LeakDetector::Dispatcher>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Best Practical Solutions
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;


More information about the Jifty-commit mailing list