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 |