=pod ################################################################################ =head1 NAME Apache::Voodoo::Debug - handles operations associated with debugging output. =head1 VERSION $Id: Debug.pm 6315 2007-11-16 18:52:40Z medwards $ =head1 SYNOPSIS This object is used by Voodoo internally to handling various types of debugging information and to produce end user display of that information. End users never interact with this module directly, instead they use the debug() and mark() methods from L. =cut ########################################################################### package Apache::Voodoo::Debug; use strict; use Time::HiRes; use HTML::Template; use Data::Dumper; $Data::Dumper::Terse = 1; $Data::Dumper::Sortkeys = 1; sub new { my $class = shift; my $self = {}; bless($self,$class); my $file = $INC{"Apache/Voodoo/Debug.pm"}; $file =~ s/Debug.pm/Template\/debug.tmpl/; $self->{'template'} = HTML::Template->new( 'filename' => $file, 'die_on_bad_params' => 0, 'shared_cache' => 1 ); $self->reset(); return $self; } sub reset { my $self = shift; $self->{'enabled'} = 1; undef $self->{'debug'}; undef $self->{'timer'}; $self->{'template'}->clear_params(); } sub enable { my $self = shift; my $set = shift; $self->{'enabled'} = (defined $set)?$set:1; } sub disable { my $self = shift; $self->{'enabled'} = 0; } sub mark { my $self = shift; return unless $self->{'enabled'}; push(@{$self->{'timer'}},[Time::HiRes::time,shift]); } sub debug { my $self = shift; return unless $self->{'enabled'}; # trace the execution stack. # caller($i+1)[3] has the method that called # caller($i)[2] has the line number that method was called from my $i=0; my $header; my $stack; while (my $method = (caller($i+1))[3]) { if ($method =~ /^Apache\:\:Voodoo/) { $i++; next; } my $line = (caller($i++))[2]; $header ||= "$method $line"; $stack = "$method~$line~$stack" unless $line == 0; } my $mesg; foreach my $entry (@_) { $mesg .= (ref($entry))? Dumper($entry) : "$entry\n"; } push(@{$self->{'debug'}},[$stack,$mesg]); print STDERR "$header\n$mesg\n"; } sub report { my $self = shift; my %data = @_; push(@{$self->{'timer'}},[Time::HiRes::time,"end"]); my $last = $#{$self->{'timer'}}; my $total_time = $self->{'timer'}->[$last]->[0] - $self->{'timer'}->[0]->[0]; $self->{'template'}->param('generate_time' => $total_time); if ($self->{'enabled'}) { $self->{'template'}->param('debug' => 1); my $times = $self->{'timer'}; $self->{'template'}->param('vd_timing' => [ map { { 'time' => sprintf("%.5f", $times->[$_]->[0] - $times->[$_-1]->[0]), 'percent' => sprintf("%5.2f%%",($times->[$_]->[0] - $times->[$_-1]->[0])/$total_time*100), 'message' => $times->[$_]->[1] } } (1 .. $last) ] ); # either dumper, or the param passing to template is a little weird. # if you inline the calls to dumper, it doesn't work. my %h; $h{'vd_debug'} = $self->_process_debug(); $h{'vd_template'} = Dumper($data{'params'}); $h{'vd_session'} = Dumper($data{'session'}); $h{'vd_conf'} = Dumper($data{'conf'}); $self->{'template'}->param(%h); } return $self->{'template'}->output; } sub _process_debug { my $self = shift; my @debug = (); my @last = (); foreach (@{$self->{'debug'}}) { my ($stack,$mesg) = @{$_}; my $i=0; my $match = 1; my ($x,$y,@stack) = split(/~/,$stack); foreach (@stack) { unless ($match && $_ eq $last[$i]) { $match=1; push(@debug,{ 'depth' => $i, 'name' => $_ }); } $i++; } @last = @stack; push(@debug, { 'depth' => ($#stack+1), 'name' => $mesg }); } return \@debug; } 1; =pod ################################################################################ =head1 AUTHOR Maverick, /\/\averick@smurfbaneDOTorg =head1 COPYRIGHT Copyright (c) 2005 Steven Edwards. All rights reserved. You may use and distribute Voodoo under the terms described in the LICENSE file include in this package or L. The summary is it's a legalese version of the Artistic License :) =cut ################################################################################