# HG changeset patch # User Vincent Tondellier # Date 1407448849 -7200 # Node ID 013953be0f3b85c098ff439104e6f3d8669204a0 # Parent 703f1af889d16db9105d2e534d60bc9d368b1a0c Add a new backtrace-processing filter stack Move many code from main to helpers or stack filters diff -r 703f1af889d1 -r 013953be0f3b CrashTest.pl --- a/CrashTest.pl Thu Aug 07 23:56:47 2014 +0200 +++ b/CrashTest.pl Fri Aug 08 00:00:49 2014 +0200 @@ -15,15 +15,11 @@ use Mojolicious::Lite; use UUID; -use Mojo::JSON; -use Mojo::ByteStream 'b'; -use Mojo::UserAgent; use lib 'lib'; -my @valid_params = qw/Add-ons Distributor ProductName ReleaseChannel StartupTime UserID Version BuildID CrashTime Comments/; -my $config = plugin 'Config'; - -plugin 'TagHelpers::BootstrapPagination'; +use CrashTest::Models::Frame; +use CrashTest::Models::Thread; +use CrashTest::StackFilter; app->attr(storage => sub { my $self = shift; @@ -53,54 +49,6 @@ ); }); -helper scm_file_link => sub { - my ($self, $file, $line) = @_; - - return "" unless(defined($file)); - - if($file =~ qr{([a-z]+):([a-z/.]+):(.+):(\d+)}) - { - my ($scm, $repo, $scmpath, $rev) = ($1, $2, $3, $4); - my $filename = File::Spec->splitpath($scmpath); - my $scmrepo = "$scm:$repo"; - if(exists($config->{ScmLinks}->{$scmrepo})) { - return b($self->link_to("$filename:$line" => - $self->render(inline => $config->{ScmLinks}->{$scmrepo}, - repo => $repo, scmpath => $scmpath, rev => $rev, line => $line, partial => 1) - )); - } - #return $file; - } - my $filebase = (File::Spec->splitpath($file))[-1]; - if(defined($line) && $line ne "") { - return "$filebase:$line"; - } - return $filebase; -}; - -helper shorten_signature => sub { - my ($self, $signature) = @_; - - return "" if(!defined($signature) || $signature eq ""); - - my $short_signature = $signature; - if($signature =~ qr{([^<]+)<.+>::([^()]+)\(.*\)(.*)}) { - # c++ with template - $short_signature = "$1<>::$2()$3"; - } elsif($signature =~ qr{([^()]+)\(.*\)(.*)}) { - # c/c++ - $short_signature = "$1()$2"; - } - return b($self->t(span => (title => $signature, class => "shortened-signature") => $short_signature)); -}; - -helper xml_escape_block => sub { - my ($c, $block) = @_; - my $result = $block->(); - $result = xml_escape $result; - return Mojo::ByteStream->new($result); -}; - get '/' => sub { my $self = shift; my $page = 1; @@ -116,13 +64,32 @@ get '/report/:uuid' => [ uuid => qr/[0-9a-fA-F-]+/ ] => sub { my $self = shift; - $self->stash(processed_data => $self->app->storage->get_processed_data($self->param('uuid'))); + + my $data = $self->app->storage->get_processed_data($self->param('uuid')); + $self->stash(processed_data => $data); + + my $stackfilter = CrashTest::StackFilter->new(config => $self->app->config, app => $self->app); + + my $crashing_thread = CrashTest::Models::Thread->new($data->{crashing_thread}); + $crashing_thread = $stackfilter->apply($crashing_thread); + $self->stash(crashing_thread => $crashing_thread); + + my $threads = []; + foreach my $raw_thread(@{$data->{threads}}) { + my $thread = CrashTest::Models::Thread->new($raw_thread); + $thread = $stackfilter->apply($thread); + push $threads, $thread; + } + $self->stash(threads => $threads); + $self->render('report/crash'); } => 'report'; post '/submit' => sub { my $self = shift; + #my @valid_params = qw/Add-ons Distributor ProductName ReleaseChannel StartupTime UserID Version BuildID CrashTime Comments/; + # save the dump in a file my $file = $self->req->upload('upload_file_minidump'); my %paramshash = map { $_ => $self->req->param($_) } $self->req->param; @@ -139,12 +106,17 @@ $self->render(text => $pjson->{status}); } ); - -}; +} => 'submit'; app->secrets([ 'My secret passphrase here' ]); push @{app->commands->namespaces}, 'CrashTest::Commands'; +push @{app->plugins->namespaces}, 'CrashTest::Helpers'; + +plugin 'Config'; +plugin 'TagHelpers::BootstrapPagination'; +plugin 'CrashTestHelpers'; + app->start; diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/Helpers/CrashTestHelpers.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/Helpers/CrashTestHelpers.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,35 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::Helpers::CrashTestHelpers; +use Mojo::Base 'Mojolicious::Plugin'; +use Mojo::ByteStream qw/b/; +use Mojo::Util qw/xml_escape/; + +sub register { + my ($self, $mojo, $params) = @_; + + $mojo->helper( + xml_escape_block => sub { + return $self->xml_escape_block(@_); + } + ); +} + +sub xml_escape_block { + my ($self, $c, $block) = @_; + my $result = $block->(); + return b(xml_escape($result)); +} + +1; diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/Models/Frame.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/Models/Frame.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,53 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::Models::Frame; +use Mojo::Base -base; +use Mojo::ByteStream 'b'; +use Mojolicious::Plugin::TagHelpers; +use File::Basename; + +# from json +has [ qw/frame module function file line trust/ ]; +has [ qw/function_offset module_offset offset/ ]; +has [ qw/missing_symbols corrupt_symbols/ ]; + +# added +has [ qw/module_name function_name file_link frame_number/ ]; +has [ qw/warnings infos/ ]; + +sub new { + my $self = shift->SUPER::new(@_); + + # defaults + $self->frame_number($self->frame); + $self->function_name($self->function); + $self->module_name($self->module); + if(defined($self->file)) { + my $filename = fileparse($self->file); + $self->file_link($filename . ":" . $self->line); + } + #$self->frame_number($self->frame); + $self->warnings([]); + $self->add_warning("hello world!"); + + return $self; +} + +sub add_warning { + my ($self, $args) = @_; + + push @{$self->warnings}, $args; +} + +1; diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/Models/Thread.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/Models/Thread.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,40 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::Models::Thread; +use Mojo::Base -base; +use Mojo::ByteStream 'b'; +use Mojolicious::Plugin::TagHelpers; +use CrashTest::Models::Frame; + +has [ qw/frame_count crashing_thread frames/ ]; + +sub new { + my $self = shift->SUPER::new(@_); + + foreach my $frame(@{$self->frames}) { + $frame = CrashTest::Models::Frame->new($frame); + } + + return $self; +} + +sub each_frame { + my ($self, $block) = @_; + + foreach my $frame(@{$self->frames}) { + $block->($frame); + } +} + +1; diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/StackFilter.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/StackFilter.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,86 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::StackFilter; +use Mojo::Base -base; +use Mojo::Loader; +use Data::Dumper; + +has [ qw/config app filters/ ]; + +sub new { + my $self = shift->SUPER::new(@_); + + if(defined($self->config->{StackFilters})) { + $self->load_modules($self->config->{StackFilters}); + } else { + $self->find_modules(); + } + + return $self; +} + +sub apply { + my ($self, $thread) = @_; + + foreach my $filter(@{$self->filters}) { + say "apply filter $filter"; + $thread = $filter->apply($thread); + } + + return $thread; +} + +sub load_modules { + my ($self, $modules) = @_; + + my @filters = (); + my $loader = Mojo::Loader->new; + for my $module (@$modules) { + my $e = $loader->load($module); + die qq{Loading "$module" failed: $e} and next if ref $e; + +# say "loading $module"; + push @filters, $module->new(config => $self->config, app => $self->app); + } + + $self->filters(\@filters); +} + +sub find_modules { + my ($self) = @_; + + my @modules = (); + my $loader = Mojo::Loader->new; + + # Find modules in a namespace + for my $module (@{$loader->search('CrashTest::StackFilters')}) { + my $e = $loader->load($module); + warn qq{Loading "$module" failed: $e} and next if ref $e; + + push @modules, [ $module, $module->priority ]; + } + + # sort by prio + @modules = sort { $b->[1] <=> $a->[1] } @modules; + + say Dumper(@modules); + # instanciate + my @filters = map { $_->[0]->new(config => $self->config, app => $self->app) } @modules; + + $self->filters(\@filters); +} + +1; + + diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/StackFilters/FileLink.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/StackFilters/FileLink.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,66 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::StackFilters::FileLink; +use Mojo::Base -base; + +sub priority { return 50; } + +has [ qw/config app/ ]; + +sub apply { + my ($self, $thread) = @_; + + $thread->each_frame(sub { + my $frame = shift; + + $frame->file_link($self->scm_file_link($frame->file, $frame->line)); + } + ); + + return $thread; +} + +sub scm_file_link { + my ($self, $file, $line) = @_; + + return "" unless(defined($file)); + + # if the file section looks like vcs:vcs_root_url:vcs_path:revision + if($file =~ qr{([a-z]+):([a-z/.]+):(.+):([a-zA-Z0-9]+)}) { + my ($scm, $repo, $scmpath, $rev) = ($1, $2, $3, $4); + my $filename = File::Spec->splitpath($scmpath); + my $scmrepo = "$scm:$repo"; + my $mt = Mojo::Template->new; + my $config = $self->config->{ScmLinks}; + + # and we have a browser configured for this repository + if(exists($config->{$scmrepo})) { + my $template = '% my ($repo, $scmpath, $rev, $line) = @_; ' . "\n" . $config->{$scmrepo}; + my $url = $mt->render($template, $repo, $scmpath, $rev, $line); + # then create a link to it + return $self->app->link_to("$filename:$line" => $url); + } + } + + # else display only the filename + my $filebase = (File::Spec->splitpath($file))[-1]; + if(defined($line) && $line ne "") { + return "$filebase:$line"; + } + + return $filebase; +} + +1; + diff -r 703f1af889d1 -r 013953be0f3b lib/CrashTest/StackFilters/HideArgs.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/CrashTest/StackFilters/HideArgs.pm Fri Aug 08 00:00:49 2014 +0200 @@ -0,0 +1,52 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package CrashTest::StackFilters::HideArgs; +use Mojo::Base -base; + +sub priority { return 50; } + +has [ qw/app/ ]; + +sub apply { + my ($self, $thread) = @_; + + $thread->each_frame(sub { + my $frame = shift; + + $frame->function_name($self->shorten_signature($frame->function_name)); + } + ); + + return $thread; +} + +sub shorten_signature { + my ($self, $signature) = @_; + + return "" if(!defined($signature) || $signature eq ""); + + my $short_signature = $signature; + if($signature =~ qr{([^<]+)<.+>::([^()]+)\(.*\)(.*)}) { + # c++ with template + $short_signature = "$1<>::$2()$3"; + } elsif($signature =~ qr{([^()]+)\(.*\)(.*)}) { + # c/c++ + $short_signature = "$1()$2"; + } + + return $self->app->t(span => (title => $signature, class => "shortened-signature") => sub { return $short_signature }); +} + +1; + diff -r 703f1af889d1 -r 013953be0f3b templates/report/backtrace.html.ep --- a/templates/report/backtrace.html.ep Thu Aug 07 23:56:47 2014 +0200 +++ b/templates/report/backtrace.html.ep Fri Aug 08 00:00:49 2014 +0200 @@ -1,6 +1,6 @@

Crashing thread top frames

- %= include 'report/backtrace/frames', frames => $crashing_thread->{frames} + %= include 'report/backtrace/frames', thread => $crashing_thread

All threads

@@ -10,7 +10,7 @@ %= t h4 => "Thread $thread_id" % end %= t div => ( id => "backtrace-thread-$thread_id", class => "collapse in" ) => begin - %= include 'report/backtrace/frames', frames => $thread->{frames} + %= include 'report/backtrace/frames', thread => $thread % $thread_id = $thread_id + 1; % end % } diff -r 703f1af889d1 -r 013953be0f3b templates/report/backtrace/frames.html.ep --- a/templates/report/backtrace/frames.html.ep Thu Aug 07 23:56:47 2014 +0200 +++ b/templates/report/backtrace/frames.html.ep Fri Aug 08 00:00:49 2014 +0200 @@ -1,18 +1,26 @@ -%= t table => (class => 'table table-striped table-bordered table-condensed') => begin - - - Frame - Module - Signature - Source - - -% foreach my $frame(@$frames) { - - %= t td => $frame->{frame} - %= t td => $frame->{module} - <%= shorten_signature $frame->{function} =%> - <%= scm_file_link($frame->{file}, $frame->{line}) =%> - -% } +%= t table => (class => 'table table-striped table-hover table-bordered table-condensed') => begin + %= t thead => begin + %= t tr => begin + %= t th => "Frame" + %= t th => "Module" + %= t th => (class => "signature-column") => "Signature" + %= t th => "Source" + %= end + %= end + % $thread->each_frame(sub { my $frame = shift; + %= t tr => begin + %= t td => begin + %= $frame->frame_number + % end + %= t td => begin + %= $frame->module_name + % end + %= t td => begin + %= $frame->function_name + % end + %= t td => begin + %= $frame->file_link + % end + % end + % }); % end diff -r 703f1af889d1 -r 013953be0f3b templates/report/crash.html.ep --- a/templates/report/crash.html.ep Thu Aug 07 23:56:47 2014 +0200 +++ b/templates/report/crash.html.ep Fri Aug 08 00:00:49 2014 +0200 @@ -16,10 +16,10 @@ %= t div => (class => 'tab-content') => begin %= t div => (class => 'tab-pane active', id => 'backtrace') => begin - %= include('report/backtrace', crashing_thread => $processed_data->{crashing_thread}, threads => $processed_data->{threads}); + %= include('report/backtrace', crashing_thread => $crashing_thread, threads => $threads); % end %= t div => (class => 'tab-pane', id => 'details') => begin - %= t table => (class => 'table table-striped table-bordered table-condensed') => begin + %= t table => (class => 'table table-striped table-hover table-bordered table-condensed') => begin %= include('report/client_info', client_info => $processed_data->{client_info}); %= include('report/system_info', system_info => $processed_data->{system_info}); % end