| 1 | | | | | package Plack::Middleware::Debug::Profiler::NYTProf; |
| 2 | | | | | use 5.008; |
| 3 | | | | | use strict; |
| 4 | | | | | use warnings; |
| 5 | | | | | |
| 6 | | | | | use Plack::Util::Accessor qw(root exclude base_URL minimal no_merge_evals); |
| 7 | | | | | use Time::HiRes; |
| 8 | | | | | use File::Path qw(make_path); |
| 9 | | | | | |
| 10 | | | | | use parent 'Plack::Middleware::Debug::Base'; |
| 11 | | | | | our $VERSION = '0.06'; |
| 12 | | | | | |
| 13 | | | | | sub prepare_app { |
| 14 | | | | | my $self = shift; |
| 15 | | | | | $self->root($self->root || '/tmp'); |
| 16 | | | | | $self->base_URL($self->base_URL || ''); |
| 17 | | | | | $self->minimal($self->minimal || '0'); |
| 18 | | | | | $self->no_merge_evals($self->no_merge_evals || '0'); |
| 19 | | | | | $self->{files} = Plack::App::File->new(root => $self->root); |
| 20 | | | | | |
| 21 | | | | | unless(-d $self->root){ |
| 22 | | | | | make_path($self->root) or die "Cannot create directory " . $self->root; |
| 23 | | | | | } |
| 24 | | | | | |
| 25 | | | | | # start=begin - start immediately (the default) |
| 26 | | | | | # start=init - start at beginning of INIT phase (after compilation) |
| 27 | | | | | # start=end - start at beginning of END phase |
| 28 | | | | | # start=no - don't automatically start |
| 29 | | | | | $ENV{NYTPROF} ||= "addpid=1:start=begin:file=".$self->root."/nytprof.null.out"; |
| 30 | | | | | require Devel::NYTProf::Core; |
| 31 | | | | | require Devel::NYTProf; |
| 32 | | | | | |
| 33 | | | | | $self->exclude($self->exclude || [qw(.*\.css .*\.png .*\.ico .*\.js)]); |
| 34 | | | | | Carp::croak "exclude not an array" if ref($self->exclude) ne 'ARRAY'; |
| 35 | | | | | } |
| 36 | | | | | |
| 37 | | | | | sub call { |
| 38 | | | | | my($self, $env) = @_; |
| 39 | | | | | my $panel = $self->default_panel; |
| 40 | | | | | |
| 41 | | | | | if ($env->{PATH_INFO} =~ m!nytprofhtml!) { |
| 42 | | | | | $env->{'plack.debug.disabled'} = 1; |
| 43 | | | | | return $self->{files}->call($env); |
| 44 | | | | | } |
| 45 | | | | | |
| 46 | | | | | foreach my $pattern (@{$self->exclude}) { |
| 47 | | | | | if ($env->{PATH_INFO} =~ m!^$pattern$!) { |
| 48 | | | | | return $self->SUPER::call($env); |
| 49 | | | | | } |
| 50 | | | | | } |
| 51 | | | | | |
| 52 | | | | | # $ENV{NYTPROF}'s addpid=1 will append ".$$" to the file |
| 53 | 1 | 1.84ms | | | DB::enable_profile($self->root."/nytprof.out"); |
| 54 | | | | | |
| 55 | | | | | my $res = $self->SUPER::call($env); |
| 56 | | | | | DB::disable_profile(); |
| 57 | 1 | 1µs | | | DB::enable_profile($self->root."/nytprof.null.out"); |
| 58 | | | | | DB::disable_profile(); |
| 59 | | | | | |
| 60 | | | | | $self->report($env); |
| 61 | | | | | return $res; |
| 62 | | | | | } |
| 63 | | | | | |
| 64 | | | | | sub run { |
| 65 | | | | | my($self, $env, $panel) = @_; |
| 66 | | | | | return sub { |
| 67 | | | | | my $res = shift; |
| 68 | | | | | $panel->nav_subtitle(join ', ', 'OK', ( $self->minimal ? 'Minimal' : () )); |
| 69 | | | | | $panel->content('<a href="'.$self->base_URL.'/nytprofhtml.'.$$.'/index.html" target="_blank">(open in a new window)</a><br> |
| 70 | | | | | <iframe src ="'.$self->base_URL.'/nytprofhtml.'.$$.'/index.html" width="100%" height="100%"> |
| 71 | | | | | <p>Your browser does not support iframes.</p> |
| 72 | | | | | </iframe>'); |
| 73 | | | | | }; |
| 74 | | | | | } |
| 75 | | | | | |
| 76 | | | | | sub report { |
| 77 | | | | | my ( $self, $env ) = @_; |
| 78 | | | | | if ( -f $self->root . "/nytprof.out.$$" ) { |
| 79 | | | | | system "nytprofhtml" |
| 80 | | | | | , ( $self->minimal ? '--minimal' : () ) |
| 81 | | | | | , ( $self->no_merge_evals ? '--no-mergeevals' : () ) |
| 82 | | | | | , "-f", $self->root . "/nytprof.out.$$" |
| 83 | | | | | , "-o", $self->root . "/nytprofhtml.$$"; |
| 84 | | | | | } |
| 85 | | | | | } |
| 86 | | | | | |
| 87 | | | | | sub DESTROY { |
| 88 | | | | | DB::finish_profile(); |
| 89 | | | | | } |
| 90 | | | | | |
| 91 | | | | | 1; |
| 92 | | | | | __END__ |
| 93 | | | | | |
| 94 | | | | | =head1 NAME |
| 95 | | | | | |
| 96 | | | | | Plack::Middleware::Debug::Profiler::NYTProf - Runs NYTProf on your app |
| 97 | | | | | |
| 98 | | | | | =head2 SYNOPSIS |
| 99 | | | | | |
| 100 | | | | | use Plack::Builder; |
| 101 | | | | | |
| 102 | | | | | my $app = ...; ## Build your Plack App |
| 103 | | | | | |
| 104 | | | | | builder { |
| 105 | | | | | enable 'Debug', panels =>['Profiler::NYTProf']; |
| 106 | | | | | $app; |
| 107 | | | | | }; |
| 108 | | | | | |
| 109 | | | | | # or with options |
| 110 | | | | | |
| 111 | | | | | builder { |
| 112 | | | | | enable 'Debug', panels => [ |
| 113 | | | | | [ |
| 114 | | | | | 'Profiler::NYTProf', |
| 115 | | | | | base_URL => 'http://example.com/NYTProf', |
| 116 | | | | | root => '/path/to/NYTProf', |
| 117 | | | | | minimal => 1, |
| 118 | | | | | ] |
| 119 | | | | | ]; |
| 120 | | | | | $app; |
| 121 | | | | | }; |
| 122 | | | | | |
| 123 | | | | | |
| 124 | | | | | =head1 DESCRIPTION |
| 125 | | | | | |
| 126 | | | | | Adds a debug panel that runs and displays Devel::NYTProf on your perl source |
| 127 | | | | | code. |
| 128 | | | | | |
| 129 | | | | | =head1 OPTIONS |
| 130 | | | | | |
| 131 | | | | | This debug panel defines the following options. |
| 132 | | | | | |
| 133 | | | | | =head2 root |
| 134 | | | | | |
| 135 | | | | | Where to store nytprof.out and nytprofhtml output (default: '/tmp'). |
| 136 | | | | | |
| 137 | | | | | =head2 base_URL |
| 138 | | | | | |
| 139 | | | | | By default, this module will grab requests with the string B<nytprofhtml> to |
| 140 | | | | | the server, and deliver the reports with Plack::App::File. If instead you don't |
| 141 | | | | | want to serve the reports from the same server you're debugging, then you can |
| 142 | | | | | set this option to the URL where the B<root> folder above can be reached. |
| 143 | | | | | |
| 144 | | | | | =head2 exclude |
| 145 | | | | | |
| 146 | | | | | List of excluded paths (default: [qw(.*\.css .*\.png .*\.ico .*\.js)]). |
| 147 | | | | | |
| 148 | | | | | =head2 minimal |
| 149 | | | | | |
| 150 | | | | | By default, B<nytprofhtml> will generate graphviz .dot files and |
| 151 | | | | | block/sub-level reports. Setting this to a true value will disable this |
| 152 | | | | | behaviour and make B<nytprofhtml> considerably faster. |
| 153 | | | | | |
| 154 | | | | | =head2 no_merge_evals |
| 155 | | | | | |
| 156 | | | | | By defaut, B<nytprofhtml> will merge string evals in the reports. Setting this |
| 157 | | | | | to a true value will disable this behaviour. B<Warning>: this will make |
| 158 | | | | | B<nytprohtml> considerably slower, and might timeout the HTTP request. |
| 159 | | | | | |
| 160 | | | | | =head1 Environment Variable |
| 161 | | | | | |
| 162 | | | | | =head2 NYTPROF |
| 163 | | | | | |
| 164 | | | | | You can customize Devel::NYTProf's behaviour by setting the B<NYTPROF> |
| 165 | | | | | environment variable as specified in its documentation. However, this module |
| 166 | | | | | requires the following to hold: |
| 167 | | | | | |
| 168 | | | | | =head3 addpid=1 |
| 169 | | | | | |
| 170 | | | | | =head3 start=begin |
| 171 | | | | | |
| 172 | | | | | =head1 SEE ALSO |
| 173 | | | | | |
| 174 | | | | | L<Plack::Middleware::Debug> |
| 175 | | | | | L<Devel::NYTProf> |
| 176 | | | | | |
| 177 | | | | | =head1 AUTHOR |
| 178 | | | | | |
| 179 | | | | | Sebastian de Castelberg, C<< <sdecaste@cpan.org> >> |
| 180 | | | | | |
| 181 | | | | | =head1 CONTRIBUTORS |
| 182 | | | | | |
| 183 | | | | | Nuba Princigalli, C<< <nuba@cpan.org> >> |
| 184 | | | | | |
| 185 | | | | | =head1 COPYRIGHT & LICENSE |
| 186 | | | | | |
| 187 | | | | | This program is free software; you can redistribute it and/or modify |
| 188 | | | | | it under the same terms as Perl itself. |
| 189 | | | | | |
| 190 | | | | | =cut |