Add a new backtrace-processing filter stack
Move many code from main to helpers or stack filters
--- 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;
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
+
+
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
+
--- /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 <http://www.gnu.org/licenses/>.
+
+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;
+
--- 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 @@
<a data-toggle="collapse" href="#backtrace-crashing-thread"><h3>Crashing thread top frames</h3></a>
<div id="backtrace-crashing-thread" class="collapse in">
- %= include 'report/backtrace/frames', frames => $crashing_thread->{frames}
+ %= include 'report/backtrace/frames', thread => $crashing_thread
</div>
<a data-toggle="collapse" href="#backtrace-all-threads"><h3>All threads</h3></a>
<div id="backtrace-all-threads" class="collapse in">
@@ -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
% }
--- 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
-<thead>
- <tr>
- <th>Frame</th>
- <th>Module</th>
- <th class="signature-column">Signature</th>
- <th>Source</th>
- </tr>
-</thead>
-% foreach my $frame(@$frames) {
-<tr>
- %= t td => $frame->{frame}
- %= t td => $frame->{module}
- <td><%= shorten_signature $frame->{function} =%></td>
- <td><%= scm_file_link($frame->{file}, $frame->{line}) =%></td>
-</tr>
-% }
+%= 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
--- 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