| blib/lib/Mojolicious/Plugin/NYTProf.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % | 
| statement | 132 | 141 | 93.6 | 
| branch | 45 | 62 | 72.5 | 
| condition | 17 | 25 | 68.0 | 
| subroutine | 17 | 17 | 100.0 | 
| pod | 1 | 1 | 100.0 | 
| total | 212 | 246 | 86.1 | 
| line | stmt | bran | cond | sub | pod | time | code | 
|---|---|---|---|---|---|---|---|
| 1 | package Mojolicious::Plugin::NYTProf; | ||||||
| 2 | |||||||
| 3 | =head1 NAME | ||||||
| 4 | |||||||
| 5 | Mojolicious::Plugin::NYTProf - Auto handling of Devel::NYTProf in your Mojolicious app | ||||||
| 6 | |||||||
| 7 | =for html | ||||||
| 8 |  | ||||||
| 9 |   | ||||||
| 10 | |||||||
| 11 | =head1 VERSION | ||||||
| 12 | |||||||
| 13 | 0.23 | ||||||
| 14 | |||||||
| 15 | =head1 DESCRIPTION | ||||||
| 16 | |||||||
| 17 | This plugin enables L | ||||||
| 18 | profiles and routes for your app, it has been inspired by | ||||||
| 19 | L | ||||||
| 20 | |||||||
| 21 | =head1 SYNOPSIS | ||||||
| 22 | |||||||
| 23 | use Mojolicious::Lite; | ||||||
| 24 | |||||||
| 25 | plugin NYTProf => { | ||||||
| 26 | nytprof => { | ||||||
| 27 | ... # see CONFIGURATION | ||||||
| 28 | }, | ||||||
| 29 | }; | ||||||
| 30 | |||||||
| 31 | app->start; | ||||||
| 32 | |||||||
| 33 | Or | ||||||
| 34 | |||||||
| 35 | use Mojo::Base 'Mojolicious'; | ||||||
| 36 | |||||||
| 37 | ... | ||||||
| 38 | |||||||
| 39 | sub startup { | ||||||
| 40 | my $self = shift; | ||||||
| 41 | |||||||
| 42 | ... | ||||||
| 43 | |||||||
| 44 | my $mojo_config = $self->plugin('Config'); | ||||||
| 45 | $self->plugin(NYTProf => $mojo_config); | ||||||
| 46 | } | ||||||
| 47 | |||||||
| 48 | Then run your app - you should start your app with the env variables: | ||||||
| 49 | |||||||
| 50 | PERL5OPT='-d:NYTProf' | ||||||
| 51 | NYTPROF=start=no | ||||||
| 52 | |||||||
| 53 | without this, things go a bit haywire (most obviously manifested as broken links | ||||||
| 54 | in the report) because otherwise any code compiled before the C | ||||||
| 55 | cannot be covered, as described in the docs: | ||||||
| 56 | L | ||||||
| 57 | |||||||
| 58 | Profiles generated can be seen by visting /nytprof and reports | ||||||
| 59 | will be generated on the fly when you click on a specific profile. | ||||||
| 60 | |||||||
| 61 | =cut | ||||||
| 62 | |||||||
| 63 | 7 | 7 | 2790632 | use strict; | |||
| 7 | 54 | ||||||
| 7 | 229 | ||||||
| 64 | 7 | 7 | 34 | use warnings; | |||
| 7 | 13 | ||||||
| 7 | 188 | ||||||
| 65 | |||||||
| 66 | 7 | 7 | 455 | use Mojo::Base 'Mojolicious::Plugin'; | |||
| 7 | 155289 | ||||||
| 7 | 40 | ||||||
| 67 | 7 | 7 | 5690 | use Time::HiRes 'gettimeofday'; | |||
| 7 | 13 | ||||||
| 7 | 57 | ||||||
| 68 | 7 | 7 | 1315 | use File::Temp; | |||
| 7 | 8632 | ||||||
| 7 | 441 | ||||||
| 69 | 7 | 7 | 2772 | use File::Which; | |||
| 7 | 5984 | ||||||
| 7 | 331 | ||||||
| 70 | 7 | 7 | 447 | use File::Spec::Functions qw/catfile catdir/; | |||
| 7 | 718 | ||||||
| 7 | 15322 | ||||||
| 71 | |||||||
| 72 | our $VERSION = '0.23'; | ||||||
| 73 | |||||||
| 74 | =head1 METHODS | ||||||
| 75 | |||||||
| 76 | =head2 register | ||||||
| 77 | |||||||
| 78 | Registers the plugin with your app - this will only do something if the nytprof | ||||||
| 79 | key exists in your config hash | ||||||
| 80 | |||||||
| 81 | $self->register($app, \%config); | ||||||
| 82 | |||||||
| 83 | =head1 HOOKS AND Devel::NYTProf | ||||||
| 84 | |||||||
| 85 | The plugin adds hooks to control the level of profiling, Devel::NYTProf profiling | ||||||
| 86 | is started using a before_routes hook and the stopped with an around_dispatch hook. | ||||||
| 87 | |||||||
| 88 | The consequence of this is that you should see profiling only for your routes and | ||||||
| 89 | rendering code and will not see most of the actual Mojolicious framework detail. | ||||||
| 90 | |||||||
| 91 | The caveat with the use of hooks is that some hooks can fire out of order, and when | ||||||
| 92 | asynchronous code is used in your controllers you may see incomplete/odd profiling | ||||||
| 93 | behaviour - you can play around with the hook configuration to try to fix this. | ||||||
| 94 | |||||||
| 95 | You can override the hooks used to control when the profiling runs, see the | ||||||
| 96 | CONFIGURATION section below. | ||||||
| 97 | |||||||
| 98 | =head1 CONFIGURATION | ||||||
| 99 | |||||||
| 100 | Here's what you can control in myapp.conf: | ||||||
| 101 | |||||||
| 102 | { | ||||||
| 103 | # Devel::NYTProf will only be loaded, and profiling enabled, if the nytprof | ||||||
| 104 | # key is present in your config file, so either remove it or comment it out | ||||||
| 105 | # to completely disable profiling. | ||||||
| 106 | nytprof => { | ||||||
| 107 | |||||||
| 108 | # path to your nytprofhtml script (installed as part of Devel::NYTProf | ||||||
| 109 | # distribution). the plugin will do its best to try to find this so this | ||||||
| 110 | # is optional, just set if you have a none standard path | ||||||
| 111 | nytprofhtml_path => '/path/to/nytprofhtml', | ||||||
| 112 | |||||||
| 113 | # path to store Devel::NYTProf output profiles and generated html pages. | ||||||
| 114 | # options, defaults to "/path/to/your/app/root/dir/nytprof" | ||||||
| 115 | profiles_dir => '/path/to/nytprof/profiles/' | ||||||
| 116 | |||||||
| 117 | # set this to true to allow the plugin to run when in production mode | ||||||
| 118 | # the default value is 0 so you can deploy your app to prod without | ||||||
| 119 | # having to make any changes to config/plugin register | ||||||
| 120 | allow_production => 0, | ||||||
| 121 | |||||||
| 122 | # Devel::NYTProf environment options, see the documentation at | ||||||
| 123 | # https://metacpan.org/pod/Devel::NYTProf#NYTPROF-ENVIRONMENT-VARIABLE | ||||||
| 124 | # for a complete list. N.B. you can't supply start or file as these | ||||||
| 125 | # are used internally in the plugin so will be ignored if passed | ||||||
| 126 | env => { | ||||||
| 127 | trace => 1, | ||||||
| 128 | log => "/path/to/foo/", | ||||||
| 129 | .... | ||||||
| 130 | }, | ||||||
| 131 | |||||||
| 132 | # when to enable Devel::NYTProf profiling - the pre_hook will run | ||||||
| 133 | # to enable_profile and the post_hook will run to disable_profile | ||||||
| 134 | # and finish_profile. the values show here are the defaults so you | ||||||
| 135 | # do not need to provide these options | ||||||
| 136 | # | ||||||
| 137 | # bear in mind the caveats in the Mojolicious docs regarding hooks | ||||||
| 138 | # and that they may not fire in the order you expect - this can | ||||||
| 139 | # affect the NYTProf output and cause some things not to appear | ||||||
| 140 | # (or appear in the wrong order). the defaults below should be | ||||||
| 141 | # sufficient for profiling your code, however you can change these | ||||||
| 142 | # | ||||||
| 143 | # N.B. there is nothing stopping you reversing the order of the | ||||||
| 144 | # hooks, which would cause the Mojolicious framework code to be | ||||||
| 145 | # profiled, or providing hooks that are the same or even invalid. these | ||||||
| 146 | # config options should probably be used with some care | ||||||
| 147 | pre_hook => 'before_routes', | ||||||
| 148 | post_hook => 'around_dispatch', | ||||||
| 149 | }, | ||||||
| 150 | } | ||||||
| 151 | |||||||
| 152 | =head1 nytprofhtml LOCATION | ||||||
| 153 | |||||||
| 154 | The plugin does its best to find the path to your nytprofhtml executable, if | ||||||
| 155 | it cannot find it then it will die with an error. This also affects testing, | ||||||
| 156 | and any tests will be skipped if they cannot find nytprofhtml allowing you to | ||||||
| 157 | install the plugin - you will then need to make sure to set the path in your | ||||||
| 158 | config using nytprofhtml_path | ||||||
| 159 | |||||||
| 160 | =cut | ||||||
| 161 | |||||||
| 162 | sub register { | ||||||
| 163 | 88 | 88 | 1 | 445714 | my ($self, $app, $config) = @_; | ||
| 164 | |||||||
| 165 | 88 | 100 | 438 | if (my $nytprof = $config->{nytprof}) { | |||
| 166 | |||||||
| 167 | 87 | 100 | 100 | 364 | return if $app->mode eq 'production' and ! $nytprof->{allow_production}; | ||
| 168 | |||||||
| 169 | 86 | 739 | my $nytprofhtml_path; | ||||
| 170 | |||||||
| 171 | 86 | 100 | 351 | if ( $nytprofhtml_path = $nytprof->{nytprofhtml_path} ) { | |||
| 172 | # no sanity checking here, if a path is configured we use it | ||||||
| 173 | # and don't fall through to defaults | ||||||
| 174 | } else { | ||||||
| 175 | 85 | 386 | $nytprofhtml_path = _find_nytprofhtml(); | ||||
| 176 | } | ||||||
| 177 | |||||||
| 178 | 86 | 100 | 66 | 1231 | $nytprofhtml_path && -e $nytprofhtml_path | ||
| 179 | or die "Could not find nytprofhtml script. Ensure it's in your path, " | ||||||
| 180 | . "or set the nytprofhtml_path option in your config."; | ||||||
| 181 | |||||||
| 182 | # Devel::NYTProf will create an nytprof.out file immediately so | ||||||
| 183 | # we need to assign a tmp file and disable profiling from start | ||||||
| 184 | 85 | 50 | 410 | my $prof_dir = $nytprof->{profiles_dir} || 'nytprof'; | |||
| 185 | |||||||
| 186 | 85 | 694 | foreach my $dir ($prof_dir,catfile($prof_dir,'profiles')) { | ||||
| 187 | 170 | 50 | 2505 | if (! -d $dir) { | |||
| 188 | 0 | 0 | 0 | mkdir $dir | |||
| 189 | or die "$dir does not exist and cannot create - $!"; | ||||||
| 190 | } | ||||||
| 191 | } | ||||||
| 192 | |||||||
| 193 | # disable config option is undocumented, it allows testing where we | ||||||
| 194 | # don't actually load or run Devel::NYTProf | ||||||
| 195 | 85 | 100 | 434 | if (!$nytprof->{disable}) { | |||
| 196 | # https://metacpan.org/pod/Devel::NYTProf#NYTPROF-ENVIRONMENT-VARIABLE | ||||||
| 197 | # options for Devel::NYTProf - any can be passed but will always set | ||||||
| 198 | # the start and file options here | ||||||
| 199 | 4 | 17 | $nytprof->{env}{start} = 'no'; | ||||
| 200 | 4 | 8 | s/([:=])/\\$1/g for grep{ defined() } values %{ $nytprof->{env} }; | ||||
| 6 | 29 | ||||||
| 4 | 15 | ||||||
| 201 | |||||||
| 202 | $ENV{NYTPROF} = join( ':', | ||||||
| 203 | 6 | 46 | map { "$_=" . $nytprof->{env}{$_} } | ||||
| 204 | 4 | 11 | keys %{ $nytprof->{env} } | ||||
| 4 | 11 | ||||||
| 205 | ); | ||||||
| 206 | |||||||
| 207 | 4 | 3211 | require Devel::NYTProf; | ||||
| 208 | } | ||||||
| 209 | |||||||
| 210 | 85 | 4865 | $self->_add_hooks($app, $config, $nytprofhtml_path); | ||||
| 211 | } | ||||||
| 212 | } | ||||||
| 213 | |||||||
| 214 | sub _find_nytprofhtml { | ||||||
| 215 | # fall back, assume nytprofhtml_path in same dir as perl | ||||||
| 216 | 92 | 92 | 742 | my $nytprofhtml_path = $^X; | |||
| 217 | 92 | 651 | $nytprofhtml_path =~ s/w?perl[\d\.]*(?:\.exe)?$/nytprofhtml/; | ||||
| 218 | |||||||
| 219 | 92 | 50 | 2117 | if ( ! -e $nytprofhtml_path ) { | |||
| 220 | # last ditch attempt to find nytprofhtml, use File::Which | ||||||
| 221 | # (last ditch in that it may return a different nytprofhtml | ||||||
| 222 | # that is using a differently configured perl, e.g. system, | ||||||
| 223 | # this may die with incompat config errorrs but at least try) | ||||||
| 224 | 0 | 0 | $nytprofhtml_path = File::Which::which('nytprofhtml'); | ||||
| 225 | } | ||||||
| 226 | |||||||
| 227 | 92 | 50 | 33 | 1506 | return $nytprofhtml_path && -e $nytprofhtml_path | ||
| 228 | ? $nytprofhtml_path : undef; | ||||||
| 229 | } | ||||||
| 230 | |||||||
| 231 | sub _add_hooks { | ||||||
| 232 | 85 | 85 | 304 | my ($self, $app, $config, $nytprofhtml_path) = @_; | |||
| 233 | |||||||
| 234 | 85 | 176 | my $nytprof = $config->{nytprof}; | ||||
| 235 | 85 | 50 | 354 | my $prof_dir = $nytprof->{profiles_dir} || 'nytprof'; | |||
| 236 | 85 | 100 | 313 | my $pre_hook = $nytprof->{pre_hook} || 'before_routes'; | |||
| 237 | 85 | 100 | 271 | my $post_hook = $nytprof->{post_hook} || 'around_dispatch'; | |||
| 238 | 85 | 100 | 319 | my $disable = $nytprof->{disable} || 0; | |||
| 239 | 85 | 351 | my $log = $app->log; | ||||
| 240 | |||||||
| 241 | # add the nytprof/html directory to the static paths | ||||||
| 242 | # so we can serve these without having to add routes | ||||||
| 243 | 85 | 1056 | push @{$app->static->paths},catfile($prof_dir,'html'); | ||||
| 85 | 352 | ||||||
| 244 | |||||||
| 245 | # put the actual profile files into a profiles sub directory | ||||||
| 246 | # to avoid confusion with the *dirs* in nytprof/html | ||||||
| 247 | 85 | 1275 | my $prof_sub_dir = catfile( $prof_dir,'profiles' ); | ||||
| 248 | |||||||
| 249 | $app->hook($pre_hook => sub { | ||||||
| 250 | |||||||
| 251 | # figure args based on what the hook is | ||||||
| 252 | 2388 | 2388 | 637264 | my ($tx, $app, $next, $c, $path); | |||
| 253 | |||||||
| 254 | 2388 | 100 | 5722 | if ($pre_hook eq 'after_build_tx') { | |||
| 50 | |||||||
| 255 | 693 | 1272 | ($tx, $app) = @_[0,1]; | ||||
| 256 | 693 | 996 | $path = $pre_hook; # TODO - need better identifier for this? | ||||
| 257 | } elsif ($pre_hook =~ /around/) { | ||||||
| 258 | 0 | 0 | ($next, $c) = @_[0,1]; | ||||
| 259 | } else { | ||||||
| 260 | 1695 | 2326 | $c = $_[0]; | ||||
| 261 | 1695 | 3784 | $path = $c->req->url->to_string; | ||||
| 262 | 1695 | 100 | 190769 | return if $c->stash->{'mojo.static'}; # static files | |||
| 263 | } | ||||||
| 264 | |||||||
| 265 | 2385 | 100 | 15458 | return if $path =~ m{^/nytprof}; # viewing profiles | |||
| 266 | 2381 | 5971 | $path =~ s!^/!!g; | ||||
| 267 | 2381 | 3731 | $path =~ s!/!-!g; | ||||
| 268 | 2381 | 50 | 5584 | $path =~ s![:?]!-!g if $^O eq 'MSWin32'; | |||
| 269 | 2381 | 3217 | $path =~ s!\?.*$!!g; # remove URL query params | ||||
| 270 | |||||||
| 271 | 2381 | 5822 | my ($sec, $usec) = gettimeofday; | ||||
| 272 | 2381 | 13716 | my $profile = catfile($prof_sub_dir,"nytprof_out_${sec}_${usec}_${path}_$$"); | ||||
| 273 | 2381 | 50 | 33 | 6197 | if($^O eq 'MSWin32' && length($profile)>259){ | ||
| 274 | 0 | 0 | my $overflow = length($profile) - 259; | ||||
| 275 | 0 | 0 | $path = substr($path, 0,length($path) - $overflow -1); | ||||
| 276 | 0 | 0 | $profile = catfile($prof_sub_dir,"nytprof_out_${sec}_${usec}_${path}_$$"); | ||||
| 277 | } | ||||||
| 278 | 2381 | 6729 | $log->debug( 'starting NYTProf' ); | ||||
| 279 | # note that we are passing a custom file to enable_profile, this results in | ||||||
| 280 | # a timing bug causing multiple calls to this plugin (in the order of 10^5) | ||||||
| 281 | # to gradually slow down. see GH #5 | ||||||
| 282 | 2381 | 100 | 17833 | DB::enable_profile( $profile ) if ! $disable; | |||
| 283 | 2381 | 50 | 6920 | return $next->() if $pre_hook =~ /around/; | |||
| 284 | 85 | 915 | }); | ||||
| 285 | |||||||
| 286 | $app->hook($post_hook => sub { | ||||||
| 287 | # first arg is $next if the hook matches around | ||||||
| 288 | 2243 | 100 | 2243 | 370478 | shift->() if $post_hook =~ /around/; | ||
| 289 | 2243 | 100 | 63785 | DB::finish_profile() if ! $disable; | |||
| 290 | 2243 | 4011 | $log->debug( 'finished NYTProf' ); | ||||
| 291 | 85 | 1441 | }); | ||||
| 292 | |||||||
| 293 | $app->routes->get('/nytprof/profiles/:file' | ||||||
| 294 | => [file => qr/nytprof_out_\d+_\d+.*/] | ||||||
| 295 | => sub { | ||||||
| 296 | 2 | 2 | 1192 | $log->debug( "generating profile for $nytprofhtml_path" ); | |||
| 297 | 2 | 18 | _generate_profile(@_,$prof_dir,$nytprofhtml_path) | ||||
| 298 | } | ||||||
| 299 | 85 | 832 | ); | ||||
| 300 | |||||||
| 301 | 85 | 2 | 37060 | $app->routes->get('/nytprof' => sub { _list_profiles(@_,$prof_sub_dir) }); | |||
| 2 | 1256 | ||||||
| 302 | } | ||||||
| 303 | |||||||
| 304 | sub _list_profiles { | ||||||
| 305 | 2 | 2 | 5 | my $self = shift; | |||
| 306 | 2 | 5 | my $prof_dir = shift; | ||||
| 307 | |||||||
| 308 | 2 | 5 | my @profiles = _profiles($prof_dir); | ||||
| 309 | 2 | 12 | $self->app->log->debug( scalar( @profiles ) . ' profiles found' ); | ||||
| 310 | |||||||
| 311 | # could use epl here, but users might be using a different Template engine | ||||||
| 312 | 2 | 100 | 51 | my $list = @profiles | |||
| 313 | ? ' Select a profile run output from the list to view the HTML reports as produced by Devel::NYTProf. 
 | ||||||
| 314 | : ' No profiles found'; | ||||||
| 315 | |||||||
| 316 | 2 | 6 | foreach (@profiles) { | ||||
| 317 | 3 | 13 | $list .= qq{ | ||||
| 318 |  | ||||||
| 319 | $_->{label} | ||||||
| 320 | (PID $_->{pid}, $_->{created}, $_->{duration}) | ||||||
| 321 | |||||||
| 322 | }; | ||||||
| 323 | } | ||||||
| 324 | |||||||
| 325 | 2 | 100 | 11 | $list .= '' if $list !~ /No profiles found/; | |||
| 326 | |||||||
| 327 | 2 | 9 | my $html = <<"EndOfEp"; | ||||
| 328 | |||||||
| 329 | |||||||
| 330 |  | ||||||
| 331 | |||||||
| 332 | |||||||
| 333 | Profile run list | ||||||
| 334 | $list | ||||||
| 335 | |||||||
| 336 | |||||||
| 337 | EndOfEp | ||||||
| 338 | |||||||
| 339 | 2 | 12 | $self->render(text => $html); | ||||
| 340 | } | ||||||
| 341 | |||||||
| 342 | sub _profiles { | ||||||
| 343 | 7 | 7 | 36371 | my $prof_dir = shift; | |||
| 344 | |||||||
| 345 | 7 | 2230 | require Devel::NYTProf::Data; | ||||
| 346 | 7 | 50 | 65444 | opendir my $dirh, $prof_dir | |||
| 347 | or die "Unable to open profiles dir $prof_dir - $!"; | ||||||
| 348 | 7 | 173 | my @files = grep { /^nytprof_out/ } readdir $dirh; | ||||
| 29 | 97 | ||||||
| 349 | 7 | 79 | closedir $dirh; | ||||
| 350 | |||||||
| 351 | 7 | 22 | my @profiles; | ||||
| 352 | |||||||
| 353 | 7 | 45 | for my $file ( sort { | ||||
| 354 | 6 | 173 | (stat catfile($prof_dir,$b))[10] <=> (stat catfile($prof_dir,$a))[10] | ||||
| 355 | } @files ) { | ||||||
| 356 | 8 | 17 | my $profile; | ||||
| 357 | 8 | 59 | my $filepath = catfile($prof_dir,$file); | ||||
| 358 | 8 | 19 | my $label = $file; | ||||
| 359 | 8 | 51 | $label =~ s{nytprof_out_(\d+)_(\d+)_}{}; | ||||
| 360 | 8 | 32 | my ($sec, $usec) = ($1,$2); | ||||
| 361 | 8 | 18 | $label =~ s{\.}{/}g; | ||||
| 362 | 8 | 12 | $label =~ s{/(\d+)$}{}; | ||||
| 363 | 8 | 16 | my $pid = $1; | ||||
| 364 | |||||||
| 365 | 8 | 15 | my ($nytprof,$duration); | ||||
| 366 | 8 | 12 | eval { $nytprof = Devel::NYTProf::Data->new({filename => $filepath}); }; | ||||
| 8 | 62 | ||||||
| 367 | |||||||
| 368 | $profile->{duration} = $nytprof && $nytprof->attributes->{profiler_duration} | ||||||
| 369 | ? sprintf('%.4f secs', $nytprof->attributes->{profiler_duration}) | ||||||
| 370 | 8 | 100 | 66 | 4110 | : '??? seconds - corrupt profile data?'; | ||
| 371 | |||||||
| 372 | 8 | 312 | @{$profile}{qw/file url pid created label/} | ||||
| 8 | 50 | ||||||
| 373 | = ($file,"/nytprof/profiles/$file",$pid,scalar localtime($sec),$label); | ||||||
| 374 | 8 | 30 | push(@profiles,$profile); | ||||
| 375 | } | ||||||
| 376 | |||||||
| 377 | 7 | 71 | return @profiles; | ||||
| 378 | } | ||||||
| 379 | |||||||
| 380 | sub _generate_profile { | ||||||
| 381 | 2 | 2 | 5 | my $self = shift; | |||
| 382 | 2 | 5 | my $htmldir = my $prof_dir = shift; | ||||
| 383 | 2 | 4 | my $nytprofhtml_path = shift; | ||||
| 384 | |||||||
| 385 | 2 | 39 | my $file = $self->stash('file'); | ||||
| 386 | 2 | 31 | my $profile = catfile($prof_dir,'profiles',$file); | ||||
| 387 | 2 | 100 | 60 | return $self->reply->not_found if !-f $profile; | |||
| 388 | |||||||
| 389 | 1 | 11 | foreach my $sub_dir ( | ||||
| 390 | $htmldir, | ||||||
| 391 | catfile($htmldir,'html'), | ||||||
| 392 | catfile($htmldir,'html',$file), | ||||||
| 393 | ) { | ||||||
| 394 | 3 | 100 | 44 | if (! -d $sub_dir) { | |||
| 395 | 1 | 50 | 68 | mkdir $sub_dir | |||
| 396 | or die "$sub_dir does not exist and cannot create - $!"; | ||||||
| 397 | } | ||||||
| 398 | } | ||||||
| 399 | |||||||
| 400 | 1 | 11 | $htmldir = catfile($htmldir,'html',$file); | ||||
| 401 | |||||||
| 402 | 1 | 50 | 23 | if (! -f catfile($htmldir, 'index.html')) { | |||
| 403 | 1 | 197039 | system($nytprofhtml_path, "--file=$profile", "--out=$htmldir"); | ||||
| 404 | |||||||
| 405 | 1 | 50 | 141 | if ($? == -1) { | |||
| 50 | |||||||
| 50 | |||||||
| 406 | 0 | 0 | die "'$nytprofhtml_path' failed to execute: $!"; | ||||
| 407 | } elsif ($? & 127) { | ||||||
| 408 | 0 | 0 | 0 | die sprintf "'%s' died with signal %d, %s coredump", | |||
| 409 | $nytprofhtml_path,,($? & 127),($? & 128) ? 'with' : 'without'; | ||||||
| 410 | } elsif ($? != 0) { | ||||||
| 411 | 0 | 0 | die sprintf "'%s' exited with value %d", | ||||
| 412 | $nytprofhtml_path, $? >> 8; | ||||||
| 413 | } | ||||||
| 414 | } | ||||||
| 415 | |||||||
| 416 | 1 | 153 | $self->redirect_to("/${file}/index.html"); | ||||
| 417 | } | ||||||
| 418 | |||||||
| 419 | =head1 AUTHOR | ||||||
| 420 | |||||||
| 421 | Lee Johnson - C | ||||||
| 422 | |||||||
| 423 | =head1 LICENSE | ||||||
| 424 | |||||||
| 425 | This library is free software; you can redistribute it and/or modify it under | ||||||
| 426 | the same terms as Perl itself. If you would like to contribute documentation | ||||||
| 427 | please raise an issue / pull request: | ||||||
| 428 | |||||||
| 429 | https://github.com/Humanstate/mojolicious-plugin-nytprof | ||||||
| 430 | |||||||
| 431 | =cut | ||||||
| 432 | |||||||
| 433 | 1; | ||||||
| 434 | |||||||
| 435 | # vim: ts=2:sw=2:et |