package Devel::StackTrace::AsHTML; use strict; use 5.008_001; our $VERSION = '0.15'; use Data::Dumper; use Devel::StackTrace; use Scalar::Util; no warnings 'qw'; my %enc = qw( & & > > < < " " ' ' ); # NOTE: because we don't know which encoding $str is in, or even if # $str is a wide character (decoded strings), we just leave the low # bits, including latin-1 range and encode everything higher as HTML # entities. I know this is NOT always correct, but should mostly work # in case $str is encoded in utf-8 bytes or wide chars. This is a # necessary workaround since we're rendering someone else's code which # we can't enforce string encodings. sub encode_html { my $str = shift; $str =~ s/([^\x00-\x21\x23-\x25\x28-\x3b\x3d\x3f-\xff])/$enc{$1} || '&#' . ord($1) . ';' /ge; utf8::downgrade($str); $str; } sub Devel::StackTrace::as_html { __PACKAGE__->render(@_); } sub render { my $class = shift; my $trace = shift; my %opt = @_; my $msg = encode_html($trace->frame(0)->as_string(1)); my $out = qq{Error: ${msg}}; $opt{style} ||= \<${$opt{style}}); } else { $out .= qq(); } $out .= < function toggleThing(ref, type, hideMsg, showMsg) { var css = document.getElementById(type+'-'+ref).style; css.display = css.display == 'block' ? 'none' : 'block'; var hyperlink = document.getElementById('toggle-'+ref); hyperlink.textContent = css.display == 'block' ? hideMsg : showMsg; } function toggleArguments(ref) { toggleThing(ref, 'arguments', 'Hide function arguments', 'Show function arguments'); } function toggleLexicals(ref) { toggleThing(ref, 'lexicals', 'Hide lexical variables', 'Show lexical variables'); }

Error trace

$msg
    HEAD my $i = 0; while (my $frame = $trace->next_frame) { $i++; my $next_frame = $trace->frame($i); # peek next $out .= join( '', '
  1. ', ($next_frame && $next_frame->subroutine) ? encode_html("in " . $next_frame->subroutine) : '', ' at ', $frame->filename ? encode_html($frame->filename) : '', ' line ', $frame->line, q(
    ),
                _build_context($frame) || '',
                q(
    ), _build_arguments($i, $next_frame), $frame->can('lexicals') ? _build_lexicals($i, $frame->lexicals) : '', q(
  2. ), ); } $out .= qq{
}; $out .= ""; $out; } my $dumper = sub { my $value = shift; $value = $$value if ref $value eq 'SCALAR' or ref $value eq 'REF'; my $d = Data::Dumper->new([ $value ]); $d->Indent(1)->Terse(1)->Deparse(1); chomp(my $dump = $d->Dump); $dump; }; sub _build_arguments { my($id, $frame) = @_; my $ref = "arg-$id"; return '' unless $frame && $frame->args; my @args = $frame->args; my $html = qq(

Show function arguments

); # Don't use while each since Dumper confuses that for my $idx (0 .. @args - 1) { my $value = $args[$idx]; my $dump = $dumper->($value); $html .= qq{}; $html .= qq{}; $html .= qq{}; $html .= qq{}; } $html .= qq(
\$_[$idx]} . encode_html($dump) . qq{
); return $html; } sub _build_lexicals { my($id, $lexicals) = @_; my $ref = "lex-$id"; return '' unless keys %$lexicals; my $html = qq(

Show lexical variables

); # Don't use while each since Dumper confuses that for my $var (sort keys %$lexicals) { my $value = $lexicals->{$var}; my $dump = $dumper->($value); $dump =~ s/^\{(.*)\}$/($1)/s if $var =~ /^\%/; $dump =~ s/^\[(.*)\]$/($1)/s if $var =~ /^\@/; $html .= qq{}; $html .= qq{}; $html .= qq{}; $html .= qq{}; } $html .= qq(
} . encode_html($var) . qq{} . encode_html($dump) . qq{
); return $html; } sub _build_context { my $frame = shift; my $file = $frame->filename; my $linenum = $frame->line; my $code; if (-f $file) { my $start = $linenum - 3; my $end = $linenum + 3; $start = $start < 1 ? 1 : $start; open my $fh, '<', $file or die "cannot open $file:$!"; my $cur_line = 0; while (my $line = <$fh>) { ++$cur_line; last if $cur_line > $end; next if $cur_line < $start; $line =~ s|\t| |g; my @tag = $cur_line == $linenum ? (q{}, '') : ('', ''); $code .= sprintf( '%s%5d: %s%s', $tag[0], $cur_line, encode_html($line), $tag[1], ); } close $file; } return $code; } 1; __END__ =encoding utf-8 =for stopwords =head1 NAME Devel::StackTrace::AsHTML - Displays stack trace in HTML =head1 SYNOPSIS use Devel::StackTrace::AsHTML; my $trace = Devel::StackTrace->new; my $html = $trace->as_html; =head1 DESCRIPTION Devel::StackTrace::AsHTML adds C method to L which displays the stack trace in beautiful HTML, with code snippet context and function parameters. If you call it on an instance of L, you even get to see the lexical variables of each stack frame. =head1 AUTHOR Tatsuhiko Miyagawa Emiyagawa@bulknews.netE Shawn M Moore HTML generation code is ripped off from L written by Tokuhiro Matsuno and Kazuho Oku. =head1 COPYRIGHT The following copyright notice applies to all the files provided in this distribution, including binary files, unless explicitly noted otherwise. Copyright 2009-2013 Tatsuhiko Miyagawa =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L L L =cut