[Catalyst] [PATCH] Properly order the debug action list

Andy Grundman andy at hybridized.org
Wed Dec 28 06:27:17 CET 2005


The debug action list has so far been a simple array, and is populated in 
reverse order when dealing with forwards.  This patch turns the action list into 
a proper tree that is displayed in the proper execution order.  This comes with 
a slight hit in performance, but it's debug mode, so not a big deal. :)

Example:

sub default : Private {
     my ( $self, $c ) = @_;
     $c->forward('one');
     $c->forward('two');
     $c->forward('three');
}

sub one : Private {
     my ( $self, $c ) = @_;
     $c->forward('two');
}

sub two : Private {
     my ( $self, $c ) = @_;
     $c->forward('three');
}

sub three : Private {
     my ( $self, $c ) = @_;
     $c->forward('four');
}

sub four : Private { }

Old display:
.------------------------------------------------------------------+-----------.
| Action                                                           | Time      |
+------------------------------------------------------------------+-----------+
| -> /four                                                         | 0.000021s |
| -> /three                                                        | 0.000322s |
| -> /two                                                          | 0.000612s |
| -> /one                                                          | 0.000888s |
| -> /four                                                         | 0.000019s |
| -> /three                                                        | 0.000257s |
| -> /two                                                          | 0.000477s |
| -> /four                                                         | 0.000019s |
| -> /three                                                        | 0.000237s |
| /default                                                         | 0.003647s |
'------------------------------------------------------------------+-----------'

New display:
.------------------------------------------------------------------+-----------.
| Action                                                           | Time      |
+------------------------------------------------------------------+-----------+
| /default                                                         | 0.008394s |
|  -> /one                                                         | 0.002403s |
|   -> /two                                                        | 0.001647s |
|    -> /three                                                     | 0.000890s |
|     -> /four                                                     | 0.000030s |
|  -> /two                                                         | 0.001735s |
|   -> /three                                                      | 0.000909s |
|    -> /four                                                      | 0.000031s |
|  -> /three                                                       | 0.000946s |
|   -> /four                                                       | 0.000029s |
'------------------------------------------------------------------+-----------'

-Andy
-------------- next part --------------
Index: lib/Catalyst.pm
===================================================================
--- lib/Catalyst.pm	(revision 2828)
+++ lib/Catalyst.pm	(working copy)
@@ -17,6 +17,8 @@
 use Time::HiRes qw/gettimeofday tv_interval/;
 use URI;
 use Scalar::Util qw/weaken/;
+use Tree::Simple;
+use Tree::Simple::Visitor::FindByUID;
 use attributes;
 
 __PACKAGE__->mk_accessors(
@@ -896,6 +898,7 @@
       : ( caller(1) )[3];
 
     my $action = '';
+    
     if ( $c->debug ) {
         $action = "$code";
         $action = "/$action" unless $action =~ /\-\>/;
@@ -910,7 +913,41 @@
         }
 
         $action = "-> $action" if $callsub =~ /forward$/;
+
+        my $node = Tree::Simple->new( {
+            action  => $action,
+            elapsed => undef,       # to be filled in later
+        } );
+        $node->setUID( "$code" . $c->counter->{"$code"} );
+        
+        unless ( ( $code->name =~ /^_.*/ )
+            && ( !$c->config->{show_internal_actions} ) )
+        {         
+            # is this a root-level call or a forwarded call?
+            if ( $callsub =~ /forward$/ ) {
+           
+                # forward, locate the caller
+                if ( my $parent = $c->stack->[-1] ) {
+                    my $visitor = Tree::Simple::Visitor::FindByUID->new;
+                    $visitor->searchForUID( 
+                        "$parent" . $c->counter->{"$parent"} );
+                    $c->{stats}->accept( $visitor );
+                    if ( my $result = $visitor->getResult ) {
+                        $result->addChild( $node );
+                    }
+                }
+                else {
+                    # forward with no caller may come from a plugin
+                    $c->{stats}->addChild( $node );
+                }
+            }
+            else {
+                # root-level call
+                $c->{stats}->addChild( $node );
+            }
+        }
     }
+    
     push( @{ $c->stack }, $code );
     my $elapsed = 0;
     my $start   = 0;
@@ -922,14 +959,27 @@
         unless ( ( $code->name =~ /^_.*/ )
             && ( !$c->config->{show_internal_actions} ) )
         {
-            push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ];
+            # FindByUID uses an internal die, so we save the existing error
+            my $error = $@;
+            
+            # locate the node in the tree and update the elapsed time
+            my $visitor = Tree::Simple::Visitor::FindByUID->new;
+            $visitor->searchForUID( "$code" . $c->counter->{"$code"} );
+            $c->{stats}->accept( $visitor );
+            if ( my $result = $visitor->getResult ) {
+                my $value = $result->getNodeValue;
+                $value->{elapsed} = sprintf( '%fs', $elapsed );
+                $result->setNodeValue( $value );
+            }
+            
+            # restore error
+            $@ = $error || undef;
         }
     }
     my $last = ${ $c->stack }[-1];
     pop( @{ $c->stack } );
 
     if ( my $error = $@ ) {
-
         if ( $error eq $DETACH ) { die $DETACH if $c->depth > 1 }
         else {
             unless ( ref $error ) {
@@ -1099,11 +1149,11 @@
     # Always expect worst case!
     my $status = -1;
     eval {
-        my @stats = ();
+        my $stats = ( $class->debug ) ? Tree::Simple->new : '';
 
         my $handler = sub {
             my $c = $class->prepare(@arguments);
-            $c->{stats} = \@stats;
+            $c->{stats} = $stats;
             $c->dispatch;
             return $c->finalize;
         };
@@ -1116,8 +1166,16 @@
             my $av = sprintf '%.3f',
               ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
             my $t = Text::SimpleTable->new( [ 64, 'Action' ], [ 9, 'Time' ] );
-
-            for my $stat (@stats) { $t->row( $stat->[0], $stat->[1] ) }
+            
+            $stats->traverse( sub {
+                my $action = shift;
+                my $stat = $action->getNodeValue;
+                $t->row( 
+                    ( q{ } x $action->getDepth ) . $stat->{action},
+                    $stat->{elapsed} || '??'
+                );
+            } );
+            
             $class->log->info(
                 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
         }


More information about the Catalyst mailing list