Index: branches/release-30/lib/MT/Core.pm
===================================================================
--- branches/release-30/lib/MT/Core.pm (revision 1369)
+++ branches/release-30/lib/MT/Core.pm (revision 1372)
@@ -499,4 +499,7 @@
             # Basename settings
             'AuthorBansenameLimit' => { default => 30 },
+            'PerformanceLogging' => { default => 0 },
+            'PerformanceLoggingThreshold' => { default => 0.1 },
+            'ProcessMemoryCommand' => { handler => \&ProcessMemoryCommand },
         },
         upgrade_functions => \&load_upgrade_fns,
@@ -801,4 +804,24 @@
 }
 
+sub ProcessMemoryCommand {
+    my $cfg = shift;
+    $cfg->set_internal( 'ProcessMemoryCommand', @_ ) if @_;
+    my $cmd = $cfg->get_internal('ProcessMemoryCommand');
+    unless ($cmd) {
+        my $os = $^O;
+        if ($os eq 'darwin') {
+            $cmd = 'ps $$ -o rss=';
+        }
+        elsif ($os eq 'linux') {
+            $cmd = 'ps -p $$ -o rss=';
+        }
+        elsif ($os eq 'MSWin32') {
+            $cmd = { command => q{tasklist /FI "PID eq $$" /FO TABLE /NH},
+                regex => qr/([\d,]+) K/ };
+        }
+    }
+    return $cmd;
+}
+
 sub SecretToken {
     my $cfg = shift;
Index: branches/release-30/lib/MT/Util/ReqTimer.pm
===================================================================
--- branches/release-30/lib/MT/Util/ReqTimer.pm (revision 1372)
+++ branches/release-30/lib/MT/Util/ReqTimer.pm (revision 1372)
@@ -0,0 +1,160 @@
+=head1 NAME
+
+MT::Util::ReqTimer
+
+=head1 DESCRIPTION
+
+This is a simple class to let us get custom and granular
+and log parts of the request cycle.
+
+Note that the timerlogger has got some custom stuff in
+it to handle the data we're passing into it, so that
+it can log pretty in some placess and machine-readable
+in other places.
+
+Requires installation of Time::HiRes and List::Util
+(Time::HiRes is core to Perl 5.8.0 and later).
+
+=cut
+
+package MT::Util::ReqTimer;
+
+use strict;
+use List::Util qw( sum );
+use Time::HiRes;
+
+our $Timer;
+sub instance {
+    return $Timer ||= shift->new;
+}
+
+sub new {
+    my $class = shift;
+    $class = ref $class || $class;
+    my $self = bless {}, $class;
+    $self->start( @_ );
+    return $self;
+}
+
+sub start {
+    my $self = shift;
+    $self->{uri} = shift || '';
+    $self->{prev} = $self->{first} = Time::HiRes::time();
+    $self->{paused} = [];
+    $self->{elapsed} = 0;
+    $self->{dur} = [];
+}
+
+sub mark {
+    my ($self, $mark_str) = @_;
+    $mark_str = (caller(1))[3] unless $mark_str;
+    my $now = Time::HiRes::time();
+    my $dur = $now - $self->{prev};
+    $self->{elapsed} += $dur;
+    push @{$self->{dur}}, [ $dur, $mark_str, $self->{elapsed} ];
+    if (@{$self->{paused}}) {
+        my $paused = pop @{$self->{paused}};
+        if (ref $paused ne 'ARRAY') {
+            $paused = [];
+        }
+        my ($start, $end) = @$paused;
+
+        #back up enough to account for the time spent doing other things
+        $self->{prev} = $start - ($end - $now);
+    } else {
+        $self->{prev} = $now;
+    }
+}
+
+sub log {
+    my $self = shift;
+    #my $timerlogger = Log::Log4perl::get_logger('TypeCore::Util::ReqTimer');
+    #$timerlogger->info($self->{uri}, $self->{dur});
+}
+
+sub dump_line {
+    my $self = shift;
+    my (@more) = @_;
+    my @lines;
+    push @lines, "pid=$$";
+    push @lines, "uri=[" . $self->{uri} . ']' if $self->{uri};
+    push @lines, @more if @more;
+    my $total = 0;
+    my $threshold = MT->config->PerformanceLoggingThreshold;
+    foreach ( @{$self->{dur}} ) {
+        my $dur = $_->[0];
+        if ($_->[1] =~ m/^total\b/) {
+            $dur = $_->[2];
+        }
+        if ($dur >= $threshold) {
+            push @lines, sprintf("%s=%.5f", $_->[1], $dur);
+        }
+        $total += $_->[0];
+    }
+    push @lines, sprintf("Total=%.5f", $total);
+    my @times = localtime(time);
+    my $mon = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[$times[4]];
+    my $day = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat')[$times[6]];
+    my $mday = $times[3];
+    my $year = $times[5]+1900;
+    my ($hr, $mn, $sc) = ($times[2], $times[1], $times[0]);
+    my $ts = sprintf("%s %s %s %02d:%02d:%02d %04d", $day, $mon, $mday, $hr, $mn, $sc, $year);
+
+    require Sys::Hostname; # available in core as of Perl 5.6.0
+    my $hostname = Sys::Hostname::hostname();
+
+    return "[$ts] $hostname pt-times: " . join(", ", @lines) . "\n";
+}
+
+sub dump {
+    my $self = shift;
+    my $lines = '';
+    my $total = 0;
+    my $threshold = MT->config->PerformanceLoggingThreshold;
+    foreach ( @{$self->{dur}} ) {
+        my $dur = $_->[0];
+        if ($_->[1] =~ m/^total\b/) {
+            $dur = $_->[2];
+        }
+        if ($dur >= $threshold) {
+            $lines .= sprintf("%s - %.5f %s\n", $$, $dur, $_->[1]);
+        }
+        $total += $_->[0];
+    }
+    $lines .= sprintf("%s - %.5f --Total-- %s\n", $$, $total, $self->{uri});
+    return $lines;
+}
+
+sub pause_partial {
+    my $self = shift;
+    my $now = Time::HiRes::time();
+    push @{$self->{paused}}, [$self->{prev}, $now];
+
+    $self->{prev} = $now;
+}
+
+sub unpause {
+    my ($self) = @_;
+    my $now = Time::HiRes::time();
+    my $dur = $now - $self->{prev};
+    $self->{elapsed} += $dur;
+    if (@{$self->{paused}}) {
+        my $paused = pop @{$self->{paused}};
+        if (ref $paused ne 'ARRAY') {
+            $paused = [];
+        }
+        my ($start, $end) = @$paused;
+
+        #back up enough to account for the time spent doing other things
+        $self->{prev} = $start - ($end - $now);
+    } else {
+        $self->{prev} = $now;
+    }
+}
+
+sub total_elapsed {
+    my $timer = shift;
+    return Time::HiRes::time() - $timer->{first};
+}
+
+1;
Index: branches/release-30/lib/MT/Builder.pm
===================================================================
--- branches/release-30/lib/MT/Builder.pm (revision 1174)
+++ branches/release-30/lib/MT/Builder.pm (revision 1372)
@@ -283,4 +283,9 @@
     #print STDERR syntree2str($tokens,0) unless $count++ == 1;
 
+    my $timer;
+    if ($MT::DebugMode & 8) {
+        $timer = MT->get_timer();
+    }
+
     if ($cond) {
         my %lcond;
@@ -331,9 +336,5 @@
             my($h, $type) = $ctx->handler_for($t->[0]);
             if ($h) {
-                my $start;
-                if ($MT::DebugMode & 8) {
-                    require Time::HiRes;
-                    $start = [ Time::HiRes::gettimeofday() ];
-                }
+                $timer->pause_partial if $timer;
                 local($ctx->{__stash}{tag}) = $t->[0];
                 local($ctx->{__stash}{tokens}) = ref($tokens) ? bless $tokens, 'MT::Template::Tokens' : undef;
@@ -382,5 +383,5 @@
                     my $err = $ctx->errstr;
                     if (defined $err) {
-                        return $build->error(MT->translate("Error in <mt:[_1]> tag: [_2]", $t->[0], $ctx->errstr));
+                        return $build->error(MT->translate("Error in <mt[_1]> tag: [_2]", $t->[0], $ctx->errstr));
                     }
                     else {
@@ -402,7 +403,8 @@
                 $res .= $out
                     if defined $out;
-                if ($MT::DebugMode & 8) {
-                    my $elapsed = Time::HiRes::tv_interval($start);
-                    print STDERR "Builder: Tag [" . $t->[0] . "] - $elapsed seconds\n" if $elapsed > 0.25;
+
+                if ($timer) {
+                    $timer->mark("tag_"
+                        . lc($t->[0]) . args_to_string(\%args));
                 }
             } else {
@@ -417,4 +419,44 @@
 }
 
+sub args_to_string {
+    my ($args) = @_;
+    my $str = '';
+    foreach my $a (keys %$args) {
+        next if $a eq '@';
+        next unless defined $args->{$a};
+        next if $args->{$a} eq '';
+        $str .= ';' . $a . ':';
+        if (ref $args->{$a} eq 'ARRAY') {
+            foreach my $aa (@{ $args->{$a} }) {
+                $aa = '...' if $aa =~ m/ /;
+                $str .= $aa . ';';
+            }
+            chop($str);
+        } else {
+            $str .= $args->{$a} =~ m/ / ? '...' : $args->{$a};
+        }
+    }
+    my $more_args = $args->{'@'};
+    if ($more_args && @$more_args) {
+        foreach my $a (@$more_args) {
+            if (ref $a->[1] eq 'ARRAY') {
+                $str .= ' ' . $a->[0] . '=';
+                foreach my $aa (@{ $a->[1] }) {
+                    $aa = '...' if $aa =~ m/ /;
+                    $str .= $aa . ';';
+                }
+                chop($str);
+            } else {
+                next if exists $args->{$a->[0]}
+                    && ($args->{$a->[0]} eq $a->[1]);
+                next unless defined $args->[1];
+                next if $args->[1] eq '';
+                $str .= ';' . $a->[0] . ':';
+                $str .= $a->[1];
+            }
+        }
+    }
+    return $str ne '' ? '[' . substr($str,1) . ']' : '';
+}
 1;
 __END__
Index: branches/release-30/lib/MT/Tag.pm
===================================================================
--- branches/release-30/lib/MT/Tag.pm (revision 1208)
+++ branches/release-30/lib/MT/Tag.pm (revision 1372)
@@ -325,4 +325,7 @@
 sub __load_tags {
     my $obj = shift;
+    my $t = MT->get_timer;
+    $t->pause_partial if $t;
+
     if (!$obj->id) {
         $obj->{__tags} = [];
@@ -347,4 +350,5 @@
     }
     $obj->{__tags} = [ map { $_->name } @tags ];
+    $t->mark('MT::Tag::__load_tags') if $t;
     $obj->{__tag_objects} = \@tags;
 }
@@ -375,4 +379,8 @@
     my @tags = @{ $obj->{__tags} };
     return 1 unless @tags;
+
+    my $t = MT->get_timer;
+    $t->pause_partial if $t;
+
     $obj->{__tag_objects} = [];
     my $blog_id = $obj->has_column('blog_id') ? $obj->blog_id : 0;
@@ -427,4 +435,5 @@
         MT::Memcached->instance->delete( $obj->tag_cache_key );
     }
+    $t->mark('MT::Tag::save_tags') if $t;
     1;
 }
Index: branches/release-30/lib/MT/App.pm
===================================================================
--- branches/release-30/lib/MT/App.pm (revision 1371)
+++ branches/release-30/lib/MT/App.pm (revision 1372)
@@ -2049,4 +2049,10 @@
     my $q = $app->param;
 
+    my $timer;
+    if ($app->config->PerformanceLogging) {
+        $timer = $app->get_timer();
+        $timer->pause_partial();
+    }
+
     my($body);
     eval {
@@ -2252,4 +2258,9 @@
         }
     }
+
+    if ($timer) {
+        $timer->mark(ref($app) . '::run');
+    }
+
     $app->takedown();
 }
@@ -2314,4 +2325,8 @@
     require MT::Auth;
     MT::Auth->release;
+
+    if ($app->config->PerformanceLogging) {
+        $app->log_times();
+    }
 
     $app->request->finish;
Index: branches/release-30/lib/MT/Entry.pm
===================================================================
--- branches/release-30/lib/MT/Entry.pm (revision 1174)
+++ branches/release-30/lib/MT/Entry.pm (revision 1372)
@@ -222,4 +222,6 @@
 sub __load_category_data {
     my $entry = shift;
+    my $t = MT->get_timer;
+    $t->pause_partial if $t;
     my $cache = MT::Memcached->instance;
     my $memkey = $entry->cache_key('categories');
@@ -231,4 +233,5 @@
         $cache->set($memkey, $rows, CATEGORY_CACHE_TIME);
     }
+    $t->mark('MT::Entry::__load_category_data') if $t;
     return $rows;
 }
Index: branches/release-30/lib/MT/Template.pm
===================================================================
--- branches/release-30/lib/MT/Template.pm (revision 1369)
+++ branches/release-30/lib/MT/Template.pm (revision 1372)
@@ -91,4 +91,5 @@
     $tmpl->{include_path} = $param{path};
     $tmpl->{include_filter} = $param{filter};
+    $tmpl->{__file} = $file;
     my $contents = $tmpl->load_file($file);
     if (defined $contents) {
@@ -211,4 +212,10 @@
     $ctx ||= $tmpl->context;
 
+    my ($timer, $start);
+    if (MT->config->PerformanceLogging) {
+        $timer = MT->get_timer();
+    }
+    local $timer->{elapsed} = 0 if $timer;
+
     local $ctx->{__stash}{template} = $tmpl;
     my $tokens = $tmpl->tokens
@@ -243,8 +250,18 @@
         $ctx->var( 'page_columns', $columns ) if $columns;
     }
-    defined(my $res = $build->build($ctx, $tokens, $cond)) or
+
+    $timer->pause_partial if $timer;
+
+    my $res = $build->build($ctx, $tokens, $cond);
+
+    if ($timer) {
+        $timer->mark("MT::Template::build[" . ($tmpl->name || $tmpl->{__file}).']');
+    }
+
+    unless (defined($res)) {
         return $tmpl->error(MT->translate(
             "Publish error in template '[_1]': [_2]",
             $tmpl->name || $tmpl->{__file}, $build->errstr));
+    }
     $res =~ s/^\s*//;
     return $res;
Index: branches/release-30/lib/MT/WeblogPublisher.pm
===================================================================
--- branches/release-30/lib/MT/WeblogPublisher.pm (revision 1365)
+++ branches/release-30/lib/MT/WeblogPublisher.pm (revision 1372)
@@ -1210,4 +1210,5 @@
     my ( $blog, $root_path, $map, $at, $ctx, $cond, $build_static, %specifier )
       = @_;
+
     my $finfo;
     my $archiver = $mt->archiver($at);
@@ -1408,4 +1409,10 @@
     return 1 if ( $tmpl->build_dynamic );
     return 1 if ( $entry && $entry->status != MT::Entry::RELEASE() );
+
+    my $timer = MT->get_timer;
+    if ($timer) {
+        $timer->pause_partial;
+    }
+    local $timer->{elapsed} = 0 if $timer;
 
     if (
@@ -1451,6 +1458,7 @@
 
         $html = $tmpl->build( $ctx, $cond );
-        defined($html)
-          or return $mt->error(
+        unless (defined($html)) {
+            $timer->unpause if $timer;
+            return $mt->error(
             (
                 $category ? MT->translate(
@@ -1473,4 +1481,5 @@
             )
           );
+        }
         my $orig_html = $html;
         MT->run_callbacks(
@@ -1506,5 +1515,8 @@
         ## changed. If not, we won't update the published
         ## file, so as not to modify the mtime.
-        return 1 unless $fmgr->content_is_updated( $file, \$html );
+        unless ($fmgr->content_is_updated( $file, \$html )) {
+            $timer->unpause if $timer;
+            return 1;
+        }
 
         ## Determine if we need to build directory structure,
@@ -1516,7 +1528,9 @@
           unless $path eq '/'; ## OS X doesn't like / at the end in mkdir().
         unless ( $fmgr->exists($path) ) {
-            $fmgr->mkpath($path)
-              or return $mt->trans_error( "Error making path '[_1]': [_2]",
-                $path, $fmgr->errstr );
+            if (!$fmgr->mkpath($path)) {
+                $timer->unpause if $timer;
+                return $mt->trans_error( "Error making path '[_1]': [_2]",
+                    $path, $fmgr->errstr );
+            }
         }
 
@@ -1528,12 +1542,16 @@
         my $use_temp_files = !$mt->{NoTempFiles};
         my $temp_file = $use_temp_files ? "$file.new" : $file;
-        defined( $fmgr->put_data( $html, $temp_file ) )
-          or return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
-            $temp_file, $fmgr->errstr );
+        unless ( defined $fmgr->put_data( $html, $temp_file ) ) {
+            $timer->unpause if $timer;
+            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
+                $temp_file, $fmgr->errstr );
+        }
         if ($use_temp_files) {
-            $fmgr->rename( $temp_file, $file )
-              or return $mt->trans_error(
-                "Renaming tempfile '[_1]' failed: [_2]",
-                $temp_file, $fmgr->errstr );
+            if (!$fmgr->rename( $temp_file, $file )) {
+                $timer->unpause if $timer;
+                return $mt->trans_error(
+                    "Renaming tempfile '[_1]' failed: [_2]",
+                    $temp_file, $fmgr->errstr );
+            }
         }
         MT->run_callbacks(
@@ -1568,4 +1586,6 @@
 
     }
+    $timer->mark("total:rebuild_file[template_id:" . $tmpl->id . "]")
+        if $timer;
     1;
 }
@@ -1689,4 +1709,10 @@
             $finfo->save();
         }
+
+        my $timer = MT->get_timer;
+        if ($timer) {
+            $timer->pause_partial;
+        }
+        local $timer->{elapsed} = 0 if $timer;
 
         my $ctx = MT::Template::Context->new;
@@ -1715,5 +1741,8 @@
 
         my $html = $tmpl->build($ctx);
-        return $mt->error( $tmpl->errstr ) unless defined $html;
+        unless (defined $html) {
+            $timer->unpause if $timer;
+            return $mt->error( $tmpl->errstr );
+        }
 
         my $orig_html = $html;
@@ -1752,7 +1781,9 @@
           unless $path eq '/';    ## OS X doesn't like / at the end in mkdir().
         unless ( $fmgr->exists($path) ) {
-            $fmgr->mkpath($path)
-              or return $mt->trans_error( "Error making path '[_1]': [_2]",
-                $path, $fmgr->errstr );
+            if (! $fmgr->mkpath($path) ) {
+                $timer->unpause if $timer;
+                return $mt->trans_error( "Error making path '[_1]': [_2]",
+                    $path, $fmgr->errstr );
+            }
         }
 
@@ -1760,12 +1791,15 @@
         my $use_temp_files = !$mt->{NoTempFiles};
         my $temp_file = $use_temp_files ? "$file.new" : $file;
-        defined( $fmgr->put_data( $html, $temp_file ) )
-          or return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
-            $temp_file, $fmgr->errstr );
+        unless (defined( $fmgr->put_data( $html, $temp_file ) )) {
+            $timer->unpause if $timer;
+            return $mt->trans_error( "Writing to '[_1]' failed: [_2]",
+                $temp_file, $fmgr->errstr );
+        }
         if ($use_temp_files) {
-            $fmgr->rename( $temp_file, $file )
-              or
-              return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
-                $temp_file, $fmgr->errstr );
+            if (!$fmgr->rename( $temp_file, $file )) {
+                $timer->unpause if $timer;
+                return $mt->trans_error( "Renaming tempfile '[_1]' failed: [_2]",
+                    $temp_file, $fmgr->errstr );
+            }
         }
         MT->run_callbacks(
@@ -1790,4 +1824,7 @@
             file         => $file
         );
+
+        $timer->mark("total:rebuild_indexes[template_id:" . $tmpl->id . ";file:$file]")
+            if $timer;
     }
     1;
Index: branches/release-30/lib/MT.pm.pre
===================================================================
--- branches/release-30/lib/MT.pm.pre (revision 1308)
+++ branches/release-30/lib/MT.pm.pre (revision 1372)
@@ -856,4 +856,8 @@
     }
 
+    if ($cfg->ProcessMemoryCommand) {
+        $mt->log_times();
+    }
+
     $mt->set_language( $cfg->DefaultLanguage );
 
@@ -866,4 +870,138 @@
 
     1;
+}
+
+{
+my ($memory_start);
+sub log_times {
+    my $pkg = shift;
+
+    my $timer = $pkg->get_timer;
+    return unless $timer;
+
+    my $memory;
+    my $cmd = $pkg->config->ProcessMemoryCommand;
+    if ($cmd) {
+        my $re;
+        if (ref($cmd) eq 'HASH') {
+            $re = $cmd->{regex};
+            $cmd = $cmd->{command};
+        }
+        $cmd =~ s/\$\$/$$/g;
+        $memory = `$cmd`;
+        if ($re) {
+            if ($memory =~ m/$re/) {
+                $memory = $1;
+                $memory =~ s/\D//g;
+            }
+        } else {
+            $memory =~ s/\s+//gs;
+        }
+    }
+
+    # Called at the start of the process; so we're only recording
+    # the memory usage at the start of the app right now.
+    unless ($timer->{elapsed}) {
+        $memory_start = $memory;
+        return;
+    }
+
+    my $log_file;
+
+    # Log filename calcluation; changes daily, so we don't cache this
+    require File::Spec;
+    my $dir = File::Spec->catdir( MT->instance->static_file_path,
+        'support', 'logs');
+    if (! -d $dir) {
+        require File::Path;
+        eval { File::Path::mkpath([$dir], 0, 0777) };
+        if ($@) {
+            $log_file = 0;
+            return;
+        }
+    }
+    my @time = localtime(time);
+    my $file = sprintf("pl-%04d%02d%02d.log", $time[5] + 1900, $time[4]+1, $time[3]);
+    $log_file = File::Spec->catfile( $dir, $file );
+
+    my $first_write = ! -f $log_file;
+
+    local *PERFLOG;
+    open PERFLOG, ">>$log_file";
+    require Fcntl;
+    flock(PERFLOG, Fcntl::LOCK_EX());
+
+    if ($first_write) {
+        require Config;
+        my ($osname, $osvers) = ($Config::Config{osname}, $Config::Config{osvers});
+        print PERFLOG "# Operating System: $osname/$osvers\n";
+        print PERFLOG "# Platform: $^O\n";
+        my $ver = ref($^V) eq 'version' ? $^V->normal : ( $^V ? join('.', unpack 'C*', $^V) : $] );
+        print PERFLOG "# Perl Version: $ver\n";
+        print PERFLOG "# Web Server: $ENV{SERVER_SOFTWARE}\n";
+        require MT::Object;
+        my $driver = MT::Object->driver;
+        if ($driver) {
+            my $dbh = $driver->r_handle;
+            if ($dbh) {
+                my $dbname = $dbh->get_info( 17 ); # SQL_DBMS_NAME
+                my $dbver = $dbh->get_info( 18 ); # SQL_DBMS_VER
+                if ($dbname && $dbver) {
+                    print PERFLOG "# Database: $dbname/$dbver\n";
+                }
+            }
+        }
+        my ($drname, $drh) = each %DBI::installed_drh;
+        print PERFLOG "# Database Library: DBI/" . $DBI::VERSION . "; DBD/" . $drh->{Version} . "\n";
+        if ($ENV{MOD_PERL}) {
+            print PERFLOG "# App Mode: mod_perl\n";
+        }
+        elsif ($ENV{FAST_CGI}) {
+            print PERFLOG "# App Mode: FastCGI\n";
+        }
+        else {
+            print PERFLOG "# App Mode: CGI\n";
+        }
+    }
+
+    if ($memory) {
+        print PERFLOG $timer->dump_line("mem_start=$memory_start", "mem_end=$memory");
+    } else {
+        print PERFLOG $timer->dump_line();
+    }
+
+    close PERFLOG;
+}
+}
+
+sub get_timer {
+    my $mt = shift;
+    $mt = MT->instance unless ref $mt;
+    my $timer = $mt->request('timer');
+    unless ($timer) {
+        my $uri;
+        if ($mt->isa('MT::App')) {
+            $uri = $mt->uri( args => { $mt->param_hash } );
+        }
+        require MT::Util::ReqTimer;
+        $timer = MT::Util::ReqTimer->new( $uri );
+        $mt->request('timer', $timer);
+    }
+    return $timer;
+}
+
+sub time_this {
+    my $mt = shift;
+    my ($str, $code) = @_;
+    my $timer = $mt->get_timer();
+    my $ret;
+    if ($timer) {
+        $timer->pause_partial();
+        $ret = $code->();
+        $timer->mark($str);
+    } else {
+        $ret = $code->();
+    }
+    return $ret;
 }
 
@@ -1077,4 +1215,9 @@
     my $mt = shift;
     my ($PluginSwitch, $use_plugins, $PluginPaths) = @_;
+
+    my $timer;
+    if ($mt->config->PerformanceLogging) {
+        $timer = $mt->get_timer();
+    }
 
     foreach my $PluginPath (@$PluginPaths) {
@@ -1108,5 +1251,7 @@
                     return 0 if exists $Plugins{$plugin_sig};
                     $Plugins{$plugin_sig}{full_path} = $plugin_full_path;
+                    $timer->pause_partial if $timer;
                     eval { require $plugin };
+                    $timer->mark("Loaded plugin " . $sig) if $timer;
                     if ($@) {
                         $Plugins{$plugin_sig}{error} = $@;
@@ -1867,5 +2012,4 @@
     return $mt->error(
         $mt->translate("Loading template '[_1]' failed.", $file)) unless $tmpl;
-    $tmpl->{__file} = $file if $type eq 'filename';
     $mt->set_default_tmpl_params($tmpl);
     $tmpl->param($param) if $param;
