Refactor everything
- change db access method, use Mojo::{Pg,SQLite,...} instead of DBIx::Class
- use Minion as job queue
- refactor into a non-Lite Mojolicious app
--- a/CrashTest.conf Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-{
- Dumper => {
- JSONStackwalker => './stackwalker',
- SymbolsPath => 'breakpad-debug-symbols/*',
- },
-# Storage => {
-# Type => "CrashTest::Storage::FileSystem",
-# DataDir => 'data/',
-# },
- Storage => {
- Type => "CrashTest::Storage::Sql",
- DSN => "dbi:Pg:dbname=crashreports",
- DataDir => 'data/',
- },
- DecodeQueue => {
- Type => "CrashTest::Decode::Queue::NoQueue"
- },
-# DecodeQueue => {
-# Type => "CrashTest::Decode::Queue::Gearman",
-# GearmanServers => [ 'localhost:4730' ],
-# },
- WebInterface => {
- ScmLinks => {
- "svn:svn.example.org/testproject" => 'https://redmine.example.org/projects/testproject/repository/entry/<%= $scmpath =%>?rev=<%= $rev =%>#L<%= $line =%>',
- },
- ExtraColumns => {
- Index => [
- { id => 'os', db_column => "crash_user.os", name => 'Operating System' },
- ],
- },
- },
-};
-# vim:ft=perl:
--- a/CrashTest.pl Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-#!/usr/bin/env perl
-
-# 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/>.
-
-# ABSTRACT: Web interface for breakpad
-
-use Mojolicious::Lite;
-use Mojo::Loader qw/load_class/;
-use UUID;
-use lib 'lib';
-
-use CrashTest::Models::Frame;
-use CrashTest::Models::Thread;
-use CrashTest::StackFilter;
-
-app->attr(storage => sub {
- my $self = shift;
-
- my $storage_class = $self->app->config->{Storage}->{Type};
- if (my $e = load_class($storage_class)) {
- die ref $e ? "Exception: $e" : 'Not found!';
- }
-
- state $storage = $storage_class->new(
- config => $self->app->config->{Storage},
- extra_columns => $self->app->config->{WebInterface}->{ExtraColumns},
- );
- return $storage;
-});
-
-app->attr(decode_queue => sub {
- my $self = shift;
-
- my $decode_class = $self->app->config->{DecodeQueue}->{Type};
- if (my $e = load_class($decode_class)) {
- die ref $e ? "Exception: $e" : 'Not found!';
- }
-
- state $decode = $decode_class->new(
- config => $self->app->config->{DecodeQueue},
- dumper_config => $self->app->config->{Dumper},
- storage => $self->app->storage
- );
- return $decode;
-});
-
-app->attr(stackfilter => sub {
- my $self = shift;
-
- state $stackfilter = CrashTest::StackFilter->new(
- config => $self->app->config,
- app => $self->app
- );
- return $stackfilter;
-});
-
-get '/' => sub {
- my $self = shift;
- my $page = 1;
- my $crashs_per_page = 25;
-
- if($self->req->url =~ qr{.*\.atom$}) {
- $crashs_per_page = 100;
- }
-
- $self->validation->required('page')->like(qr/^[0-9]+$/);
- $page = scalar $self->validation->param("page") if $self->validation->is_valid('page');
-
- my $result = $self->app->storage->index($page, $crashs_per_page, $self->req->param('search'));
-
- $self->stash(files => $result->{crashs});
- $self->stash(pager => $result->{pager});
- $self->stash(extra_columns => $self->app->config->{WebInterface}->{ExtraColumns}->{Index});
- $self->render('index');
-} => 'index';
-
-get '/report/:uuid' => [ uuid => qr/[0-9a-fA-F-]+/ ] => sub {
- my $self = shift;
-
- my $data = $self->app->storage->get_processed_data($self->param('uuid'));
- $self->stash(processed_data => $data);
-
- my $crashing_thread = CrashTest::Models::Thread->new($data->{crashing_thread});
- $crashing_thread = $self->app->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 = $self->app->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');
-
- # TODO check for authorised values ...
- my %paramshash = map { $_ => $self->req->param($_) } $self->req->param;
-
- my ($uuid, $uuidstr);
- UUID::generate($uuid);
- UUID::unparse($uuid, $uuidstr);
-
- $self->render_later();
-
- $self->app->decode_queue->decode($file, \%paramshash, $uuidstr, sub {
- my $pjson = shift;
- # reply
- $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 'bootstrap_pagination';
-plugin 'CrashTestHelpers';
-
-app->start;
--- a/bin/fs_to_db.pl Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#!/usr/bin/env perl
-
-# 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/>.
-
-use Mojo::Base -strict;
-use lib 'lib';
-
-use Mojo::JSON qw/decode_json/;
-use Mojo::Util qw/decode slurp/;
-use Mojo::Loader qw/load_class/;
-use File::Basename;
-use Mojolicious;
-use Mojo::Home;
-
-if(scalar @ARGV == 0) {
- say "Usage: $0 <processed json files>";
- exit 1;
-}
-
-sub load_config {
- my $app = Mojolicious->new;
- $app->home(Mojo::Home->new("$FindBin::Bin/../"));
- $app->moniker("CrashTest");
- return $app->plugin('Config');
-}
-
-my $config = load_config();
-
-my $storage_class = $config->{Storage}->{Type};
-if (my $e = load_class($storage_class)) {
- die ref $e ? "Exception: $e" : 'Not found!';
-}
-
-my $storage = $storage_class->new(config => $config->{Storage});
-
-foreach my $arg (@ARGV) {
- eval {
- my $pjson = decode_json(slurp $arg);
-
- my($filename, $dirs, $suffix) = fileparse($arg, qr/\.[^.]*/);
-
- my $uuid = $filename;
- $storage->_db_insert_processed_data($uuid, $pjson);
- };
- warn $@ if $@;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/crash_test.conf Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,30 @@
+{
+ Processor => {
+ Common => {
+ JobQueue => {
+ Backend => {
+ Minion => { Pg => "postgresql:///crashtest" },
+ },
+ },
+ },
+ Breakpad => {
+ JSONStackwalker => 'stackwalker',
+ SymbolsPath => 'data/breakpad-debug-symbols/*',
+ },
+ },
+ Storage => [
+ { Type => "Sql", db => { Pg => "postgresql:///crashtest" } },
+ { Type => "File", DataDir => 'data/crashs/' },
+ ],
+ WebInterface => {
+ ScmLinks => {
+ "svn:svn.example.org/testproject" => 'https://redmine.example.org/projects/testproject/repository/entry/<%= $scmpath =%>?rev=<%= $rev =%>#L<%= $line =%>',
+ },
+ ExtraColumns => {
+ Index => [
+ { id => 'os', db_column => "crash_user.os", name => 'Operating System' },
+ ],
+ },
+ },
+};
+# vim:ft=perl:
--- a/dbicdh/PostgreSQL/deploy/1/001-auto-__VERSION.sql Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
---
--- Created by SQL::Translator::Producer::PostgreSQL
--- Created on Sun Feb 1 19:58:35 2015
---
-;
---
--- Table: dbix_class_deploymenthandler_versions.
---
-CREATE TABLE "dbix_class_deploymenthandler_versions" (
- "id" serial NOT NULL,
- "version" character varying(50) NOT NULL,
- "ddl" text,
- "upgrade_sql" text,
- PRIMARY KEY ("id"),
- CONSTRAINT "dbix_class_deploymenthandler_versions_version" UNIQUE ("version")
-);
-
-;
--- a/dbicdh/PostgreSQL/deploy/1/001-auto.sql Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
---
--- Created by SQL::Translator::Producer::PostgreSQL
--- Created on Sun Feb 1 19:58:35 2015
---
-;
---
--- Table: crash_users.
---
-CREATE TABLE "crash_users" (
- "id" serial NOT NULL,
- "user_id" character varying(40) NOT NULL,
- "os" character varying(40),
- "cpu_arch" character varying(10),
- "cpu_count" integer,
- "extra_info" text,
- PRIMARY KEY ("id")
-);
-
-;
---
--- Table: modules.
---
-CREATE TABLE "modules" (
- "id" serial NOT NULL,
- "debug_id" character varying(33) NOT NULL,
- "filename" character varying(128) NOT NULL,
- "version" character varying(64),
- PRIMARY KEY ("id"),
- CONSTRAINT "module_id" UNIQUE ("debug_id", "filename")
-);
-
-;
---
--- Table: products.
---
-CREATE TABLE "products" (
- "id" serial NOT NULL,
- "distributor" character varying(40),
- "name" character varying(40),
- "version" character varying(16),
- "release_channel" character varying,
- PRIMARY KEY ("id")
-);
-
-;
---
--- Table: crash_reports.
---
-CREATE TABLE "crash_reports" (
- "id" serial NOT NULL,
- "start_time" timestamp,
- "crash_time" timestamp,
- "uuid" character varying(36) NOT NULL,
- "bug_reference" character varying(20),
- "crash_user_id" integer NOT NULL,
- "product_id" integer NOT NULL,
- PRIMARY KEY ("id")
-);
-CREATE INDEX "crash_reports_idx_crash_user_id" on "crash_reports" ("crash_user_id");
-CREATE INDEX "crash_reports_idx_product_id" on "crash_reports" ("product_id");
-
-;
---
--- Table: crash_threads.
---
-CREATE TABLE "crash_threads" (
- "id" serial NOT NULL,
- "number" integer NOT NULL,
- "crashed" bool NOT NULL,
- "crash_report_id" integer NOT NULL,
- PRIMARY KEY ("id")
-);
-CREATE INDEX "crash_threads_idx_crash_report_id" on "crash_threads" ("crash_report_id");
-
-;
---
--- Table: crash_frames.
---
-CREATE TABLE "crash_frames" (
- "id" serial NOT NULL,
- "number" integer NOT NULL,
- "function" character varying(128),
- "source_file" character varying(128),
- "source_line" integer,
- "stack_walk_mode" character varying(10),
- "crash_thread_id" integer NOT NULL,
- "module_id" integer NOT NULL,
- PRIMARY KEY ("id")
-);
-CREATE INDEX "crash_frames_idx_crash_thread_id" on "crash_frames" ("crash_thread_id");
-CREATE INDEX "crash_frames_idx_module_id" on "crash_frames" ("module_id");
-
-;
---
--- Foreign Key Definitions
---
-
-;
-ALTER TABLE "crash_reports" ADD CONSTRAINT "crash_reports_fk_crash_user_id" FOREIGN KEY ("crash_user_id")
- REFERENCES "crash_users" ("id") DEFERRABLE;
-
-;
-ALTER TABLE "crash_reports" ADD CONSTRAINT "crash_reports_fk_product_id" FOREIGN KEY ("product_id")
- REFERENCES "products" ("id") DEFERRABLE;
-
-;
-ALTER TABLE "crash_threads" ADD CONSTRAINT "crash_threads_fk_crash_report_id" FOREIGN KEY ("crash_report_id")
- REFERENCES "crash_reports" ("id") DEFERRABLE;
-
-;
-ALTER TABLE "crash_frames" ADD CONSTRAINT "crash_frames_fk_crash_thread_id" FOREIGN KEY ("crash_thread_id")
- REFERENCES "crash_threads" ("id") DEFERRABLE;
-
-;
-ALTER TABLE "crash_frames" ADD CONSTRAINT "crash_frames_fk_module_id" FOREIGN KEY ("module_id")
- REFERENCES "modules" ("id") DEFERRABLE;
-
-;
--- a/dbicdh/PostgreSQL/deploy/2/002-extract_crashing_functions.sql Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-
-CREATE OR REPLACE FUNCTION extract_crashing_functions (processed_json jsonb) RETURNS text AS
-$$
- -- extract threads[crashing_idx]->frames[*]->function
- SELECT string_agg(functions, E'\n')
- FROM (
- SELECT jsonb_array_elements(
- (($1 #> ARRAY['threads', $1->'crash_info'->>'crashing_thread'])->'frames')
- )->>'function' AS functions
- ) AS frames
-$$
-LANGUAGE sql IMMUTABLE;
-
--- This extension is the contrib modules
-CREATE EXTENSION IF NOT EXISTS pg_trgm;
-
--- Used for searching function in the crashing thread backtrace
--- Search will be really slow if this index is not present
-CREATE INDEX crash_report_datas_idx_extract_crashing_functions ON crash_report_datas USING gist (
- extract_crashing_functions(processed) gist_trgm_ops
-);
-
--- a/dbicdh/SQLite/deploy/1/001-auto-__VERSION.sql Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
---
--- Created by SQL::Translator::Producer::SQLite
--- Created on Sun Feb 1 19:58:35 2015
---
-
-;
-BEGIN TRANSACTION;
---
--- Table: dbix_class_deploymenthandler_versions
---
-CREATE TABLE dbix_class_deploymenthandler_versions (
- id INTEGER PRIMARY KEY NOT NULL,
- version varchar(50) NOT NULL,
- ddl text,
- upgrade_sql text
-);
-CREATE UNIQUE INDEX dbix_class_deploymenthandler_versions_version ON dbix_class_deploymenthandler_versions (version);
-COMMIT;
--- a/dbicdh/SQLite/deploy/1/001-auto.sql Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
---
--- Created by SQL::Translator::Producer::SQLite
--- Created on Sun Feb 1 19:58:35 2015
---
-
-;
-BEGIN TRANSACTION;
---
--- Table: crash_users
---
-CREATE TABLE crash_users (
- id INTEGER PRIMARY KEY NOT NULL,
- user_id varchar(40) NOT NULL,
- os varchar(40),
- cpu_arch varchar(10),
- cpu_count int,
- extra_info text
-);
---
--- Table: modules
---
-CREATE TABLE modules (
- id INTEGER PRIMARY KEY NOT NULL,
- debug_id varchar(33) NOT NULL,
- filename varchar(128) NOT NULL,
- version varchar(64)
-);
-CREATE UNIQUE INDEX module_id ON modules (debug_id, filename);
---
--- Table: products
---
-CREATE TABLE products (
- id INTEGER PRIMARY KEY NOT NULL,
- distributor varchar(40),
- name varchar(40),
- version varchar(16),
- release_channel varchar
-);
---
--- Table: crash_reports
---
-CREATE TABLE crash_reports (
- id INTEGER PRIMARY KEY NOT NULL,
- start_time timestamp,
- crash_time timestamp,
- uuid varchar(36) NOT NULL,
- bug_reference varchar(20),
- crash_user_id int NOT NULL,
- product_id int NOT NULL,
- FOREIGN KEY (crash_user_id) REFERENCES crash_users(id),
- FOREIGN KEY (product_id) REFERENCES products(id)
-);
-CREATE INDEX crash_reports_idx_crash_user_id ON crash_reports (crash_user_id);
-CREATE INDEX crash_reports_idx_product_id ON crash_reports (product_id);
---
--- Table: crash_threads
---
-CREATE TABLE crash_threads (
- id INTEGER PRIMARY KEY NOT NULL,
- number int NOT NULL,
- crashed bool NOT NULL,
- crash_report_id int NOT NULL,
- FOREIGN KEY (crash_report_id) REFERENCES crash_reports(id)
-);
-CREATE INDEX crash_threads_idx_crash_report_id ON crash_threads (crash_report_id);
---
--- Table: crash_frames
---
-CREATE TABLE crash_frames (
- id INTEGER PRIMARY KEY NOT NULL,
- number int NOT NULL,
- function varchar(128),
- source_file varchar(128),
- source_line int,
- stack_walk_mode varchar(10),
- crash_thread_id int NOT NULL,
- module_id int NOT NULL,
- FOREIGN KEY (crash_thread_id) REFERENCES crash_threads(id),
- FOREIGN KEY (module_id) REFERENCES modules(id)
-);
-CREATE INDEX crash_frames_idx_crash_thread_id ON crash_frames (crash_thread_id);
-CREATE INDEX crash_frames_idx_module_id ON crash_frames (module_id);
-COMMIT;
--- a/dbicdh/_source/deploy/1/001-auto-__VERSION.yml Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
----
-schema:
- procedures: {}
- tables:
- dbix_class_deploymenthandler_versions:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- - deferrable: 1
- expression: ''
- fields:
- - version
- match_type: ''
- name: dbix_class_deploymenthandler_versions_version
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: UNIQUE
- fields:
- ddl:
- data_type: text
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: ddl
- order: 3
- size:
- - 0
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- upgrade_sql:
- data_type: text
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: upgrade_sql
- order: 4
- size:
- - 0
- version:
- data_type: varchar
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 1
- name: version
- order: 2
- size:
- - 50
- indices: []
- name: dbix_class_deploymenthandler_versions
- options: []
- order: 1
- triggers: {}
- views: {}
-translator:
- add_drop_table: 0
- filename: ~
- no_comments: 0
- parser_args:
- sources:
- - __VERSION
- parser_type: SQL::Translator::Parser::DBIx::Class
- producer_args: {}
- producer_type: SQL::Translator::Producer::YAML
- show_warnings: 0
- trace: 0
- version: 0.11020
--- a/dbicdh/_source/deploy/1/001-auto.yml Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,587 +0,0 @@
----
-schema:
- procedures: {}
- tables:
- crash_frames:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- - deferrable: 1
- expression: ''
- fields:
- - crash_thread_id
- match_type: ''
- name: crash_frames_fk_crash_thread_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields:
- - id
- reference_table: crash_threads
- type: FOREIGN KEY
- - deferrable: 1
- expression: ''
- fields:
- - module_id
- match_type: ''
- name: crash_frames_fk_module_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields:
- - id
- reference_table: modules
- type: FOREIGN KEY
- fields:
- crash_thread_id:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: crash_thread_id
- order: 7
- size:
- - 0
- function:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: function
- order: 3
- size:
- - 128
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- module_id:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: module_id
- order: 8
- size:
- - 0
- number:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: number
- order: 2
- size:
- - 0
- source_file:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: source_file
- order: 4
- size:
- - 128
- source_line:
- data_type: int
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: source_line
- order: 5
- size:
- - 0
- stack_walk_mode:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: stack_walk_mode
- order: 6
- size:
- - 10
- indices:
- - fields:
- - crash_thread_id
- name: crash_frames_idx_crash_thread_id
- options: []
- type: NORMAL
- - fields:
- - module_id
- name: crash_frames_idx_module_id
- options: []
- type: NORMAL
- name: crash_frames
- options: []
- order: 6
- crash_reports:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- - deferrable: 1
- expression: ''
- fields:
- - crash_user_id
- match_type: ''
- name: crash_reports_fk_crash_user_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields:
- - id
- reference_table: crash_users
- type: FOREIGN KEY
- - deferrable: 1
- expression: ''
- fields:
- - product_id
- match_type: ''
- name: crash_reports_fk_product_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields:
- - id
- reference_table: products
- type: FOREIGN KEY
- fields:
- bug_reference:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: bug_reference
- order: 5
- size:
- - 20
- crash_time:
- data_type: timestamp
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: crash_time
- order: 3
- size:
- - 0
- crash_user_id:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: crash_user_id
- order: 6
- size:
- - 0
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- product_id:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: product_id
- order: 7
- size:
- - 0
- start_time:
- data_type: timestamp
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: start_time
- order: 2
- size:
- - 0
- uuid:
- data_type: varchar
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: uuid
- order: 4
- size:
- - 36
- indices:
- - fields:
- - crash_user_id
- name: crash_reports_idx_crash_user_id
- options: []
- type: NORMAL
- - fields:
- - product_id
- name: crash_reports_idx_product_id
- options: []
- type: NORMAL
- name: crash_reports
- options: []
- order: 4
- crash_threads:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- - deferrable: 1
- expression: ''
- fields:
- - crash_report_id
- match_type: ''
- name: crash_threads_fk_crash_report_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields:
- - id
- reference_table: crash_reports
- type: FOREIGN KEY
- fields:
- crash_report_id:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: crash_report_id
- order: 4
- size:
- - 0
- crashed:
- data_type: bool
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: crashed
- order: 3
- size:
- - 0
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- number:
- data_type: int
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: number
- order: 2
- size:
- - 0
- indices:
- - fields:
- - crash_report_id
- name: crash_threads_idx_crash_report_id
- options: []
- type: NORMAL
- name: crash_threads
- options: []
- order: 5
- crash_users:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- fields:
- cpu_arch:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: cpu_arch
- order: 4
- size:
- - 10
- cpu_count:
- data_type: int
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: cpu_count
- order: 5
- size:
- - 0
- extra_info:
- data_type: text
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: extra_info
- order: 6
- size:
- - 0
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- os:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: os
- order: 3
- size:
- - 40
- user_id:
- data_type: varchar
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 0
- name: user_id
- order: 2
- size:
- - 40
- indices: []
- name: crash_users
- options: []
- order: 1
- modules:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- - deferrable: 1
- expression: ''
- fields:
- - debug_id
- - filename
- match_type: ''
- name: module_id
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: UNIQUE
- fields:
- debug_id:
- data_type: varchar
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 1
- name: debug_id
- order: 2
- size:
- - 33
- filename:
- data_type: varchar
- default_value: ~
- is_nullable: 0
- is_primary_key: 0
- is_unique: 1
- name: filename
- order: 3
- size:
- - 128
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- version:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: version
- order: 4
- size:
- - 64
- indices: []
- name: modules
- options: []
- order: 2
- products:
- constraints:
- - deferrable: 1
- expression: ''
- fields:
- - id
- match_type: ''
- name: ''
- on_delete: ''
- on_update: ''
- options: []
- reference_fields: []
- reference_table: ''
- type: PRIMARY KEY
- fields:
- distributor:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: distributor
- order: 2
- size:
- - 40
- id:
- data_type: int
- default_value: ~
- is_auto_increment: 1
- is_nullable: 0
- is_primary_key: 1
- is_unique: 0
- name: id
- order: 1
- size:
- - 0
- name:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: name
- order: 3
- size:
- - 40
- release_channel:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: release_channel
- order: 5
- size:
- - 0
- version:
- data_type: varchar
- default_value: ~
- is_nullable: 1
- is_primary_key: 0
- is_unique: 0
- name: version
- order: 4
- size:
- - 16
- indices: []
- name: products
- options: []
- order: 3
- triggers: {}
- views: {}
-translator:
- add_drop_table: 0
- filename: ~
- no_comments: 0
- parser_args:
- sources:
- - CrashFrame
- - CrashReport
- - CrashThread
- - CrashUser
- - Module
- - Product
- parser_type: SQL::Translator::Parser::DBIx::Class
- producer_args: {}
- producer_type: SQL::Translator::Producer::YAML
- show_warnings: 0
- trace: 0
- version: 0.11020
--- a/dist.ini Sun Sep 27 22:45:40 2015 +0200
+++ b/dist.ini Wed Nov 04 17:43:00 2015 +0100
@@ -1,10 +1,10 @@
name = CrashTest
-version = 0.1.0
+version = 0.2.0
author = Vincent Tondellier <tonton@team1664.org>
license = GPL_3
copyright_holder = Vincent Tondellier
-main_module = CrashTest.pl
+main_module = script/crash_test.pl
[@Basic]
[Mercurial::Check]
@@ -25,23 +25,24 @@
Mojolicious::Plugin::BootstrapPagination = 0.12
UUID = 0.0.3
Text::Balanced = 0
+Data::Page = 0
+Minion = 2.00
+
[Prereqs / RuntimeRecommends]
-; Gearman Queue (also contains the Worker class)
-Gearman::Client = 1.10
-; Database Storage (minimal tested versions, may work with older releases)
-DBIx::Class::DeploymentHandler = 0.002000
-DBIx::Class = 0.08196
-DBIx::Class::Candy = 0.002100
; For search field (only for SQL)
-Search::Query = 0.300
-Search::Query::Dialect::DBIxClass = 0.005
-; PostgreSQL
+Search::QueryParser = 0.90
+Search::QueryParser::Sql = 0.10
+; PostgreSQL (recommended)
DateTime::Format::Pg = 0
DBD::Pg = 0
-; SQLite
+Mojo::Pg = 2.10
+; SQLite (untested)
DateTime::Format::SQLite = 0
DBD::SQLite = 0
-; MySQL
+Mojo::SQLite = 0.010
+Minion::Backend::SQLite = 0
+; MySQL (untested)
DateTime::Format::MySQL = 0
DBD::mysql = 0
-
+Mojo::mysql = 0
+Minion::Backend::mysql = 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,53 @@
+package CrashTest;
+use Mojo::Base 'Mojolicious';
+
+use CrashTest::Model::Storage;
+use CrashTest::Model::StackFilter;
+use CrashTest::Model::CrashReport;
+use CrashTest::Model::CrashProcessor;
+
+# This method will run once at server start
+sub startup {
+ my $self = shift;
+
+ $self->secrets([
+ 'My secret passphrase here'
+ ]);
+
+ # External plugins
+ $self->plugin('Config');
+ $self->plugin('bootstrap_pagination');
+ # Documentation browser under "/perldoc"
+ #$self->plugin('PODRenderer');
+
+ # Commands
+ push @{$self->commands->namespaces}, 'CrashTest::Command';
+
+ # Helpers
+ $self->plugin("CrashTest::Helper::DateTime");
+ $self->plugin("CrashTest::Helper::Backtrace");
+ $self->plugin("CrashTest::Helper::XmlEscape");
+
+
+ $self->helper(crash_reports => sub { state $crash_reports = CrashTest::Model::CrashReport->new (app => $self); });
+
+ $self->helper(crash_processor => sub { state $crash_processor = CrashTest::Model::CrashProcessor->new (app => $self, config => $self->config); });
+ $self->helper(stackfilter => sub { state $crash_reports = CrashTest::Model::StackFilter->new (app => $self, config => $self->config); });
+ $self->helper(storage => sub { state $storage = CrashTest::Model::Storage->new (app => $self, config => $self->config); });
+
+ $self->plugin('Minion', $self->config->{Processor}->{Common}->{JobQueue}->{Backend}->{Minion});
+
+ $self->storage->load_plugins();
+ $self->crash_processor->load_plugins();
+
+ # Router
+ my $r = $self->routes;
+
+ # Normal route to controller
+ $r->get('/')->to('crash_reports#index')->name('index');
+ $r->get('/report/:uuid' => [ uuid => qr/[0-9a-fA-F-]+/ ])->to('crash_reports#report')->name('report');
+
+ $r->post('/submit')->to('crash_inserter#insert');
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Command/insert.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,55 @@
+# 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::Command::insert;
+use Mojo::Base 'Mojolicious::Command';
+use Mojo::JSON qw/decode_json/;
+use Mojo::Util qw/slurp/;
+use File::Spec::Functions qw/catdir catfile/;
+use File::Basename;
+
+# Short description
+has description => 'Insert crash';
+
+# Short usage message
+has usage => <<EOF;
+Usage: APPLICATION insert file ...
+EOF
+
+sub run {
+ my ($self, @args) = @_;
+
+ if(scalar @args < 1) {
+ say $self->usage;
+ exit 0;
+ }
+
+ foreach my $jsonfile(@args) {
+ my ($uuid,$path,$suffix) = fileparse($jsonfile, qw/.json/);
+
+ my $dmp = undef;
+ if(-e catfile($path, "$uuid.dmp")) {
+ $dmp = slurp(catfile($path, "$uuid.dmp"));
+ }
+
+ eval {
+ my $pjson = decode_json(slurp(catfile($path, "$uuid.json")));
+ $self->app->crash_reports->create($uuid, $pjson, $pjson->{client_info}, $dmp);
+ };
+ warn $@ if $@;
+
+ }
+
+}
+
+1;
--- a/lib/CrashTest/Commands/db.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-# 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::Commands::db;
-use Mojo::Base 'Mojolicious::Command';
-use v5.12;
-
-use DBIx::Class::DeploymentHandler;
-use CrashTest::Storage::Sql::Schema;
-
-# Short description
-has description => 'Setup database.';
-
-# Short usage message
-has usage => <<EOF;
-Usage: APPLICATION db [OPTIONS]
-
- create Create database
-
-EOF
-
-sub run {
- my ($self, @args) = @_;
-
- my $schema = CrashTest::Storage::Sql::Schema->connect($self->app->config->{Storage}->{DSN});
-
- my $dhargs = {
- schema => $schema,
- script_directory => "$FindBin::Bin/dbicdh",
- sql_translator_args => { add_drop_table => 0 },
- force_overwrite => 0,
- };
-
- if($args[0] eq "prepare") {
- $dhargs->{force_overwrite} = 1;
- my $dh = DBIx::Class::DeploymentHandler->new($dhargs);
- $dh->prepare_install;
-# $dh->prepare_upgrade;
- } elsif($args[0] eq "create") {
- my $dh = DBIx::Class::DeploymentHandler->new($dhargs);
- $dh->install;
-# } elsif($args[0] eq "upgrade") {
-# my $dh = DBIx::Class::DeploymentHandler->new($dhargs);
-# $dh->upgrade;
- } else {
- say "Invalid arguments";
- exit 1;
- }
-
-}
-
-1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Controller/CrashInserter.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,28 @@
+# 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::Controller::CrashInserter;
+use Mojo::Base 'Mojolicious::Controller';
+
+sub insert {
+ my $self = shift;
+
+ my $processor_task = $self->app->crash_processor->find_processor($self->req);
+ return $self->render(text => "No processor found", status => 421) if !$processor_task;
+
+ $self->app->crash_processor->decode($processor_task, $self->req);
+
+ return $self->render(text => "ok");
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Controller/CrashReports.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,69 @@
+# 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::Controller::CrashReports;
+use Mojo::Base 'Mojolicious::Controller';
+use Mojo::Util qw/dumper/;
+
+use CrashTest::Model::Thread;
+
+sub index {
+ my $self = shift;
+
+ my $page = 1;
+ my $crashs_per_page = 25;
+
+ if($self->req->url =~ qr{.*\.atom$}) {
+ $crashs_per_page = 100;
+ }
+
+ $self->validation->required('page')->like(qr/^[0-9]+$/);
+ $page = scalar $self->validation->param("page") if $self->validation->is_valid('page');
+
+ my ($results, $pager) = $self->crash_reports->index($page, $crashs_per_page, $self->req->param('search'));
+
+ #$self->app->log->debug(dumper $results);
+
+ $self->stash(crashs => $results);
+ $self->stash(pager => $pager);
+ $self->stash(extra_columns => []);
+
+ $self->render("index");
+}
+
+sub report {
+ my ($self, $uuid) = @_;
+
+ my $data = $self->app->crash_reports->get_processed_data($self->param('uuid'));
+ $self->stash(processed_data => $data);
+
+ my $crashing_thread = CrashTest::Model::Thread->new($data->{crashing_thread});
+ $crashing_thread = $self->app->stackfilter->apply($crashing_thread);
+ $self->stash(crashing_thread => $crashing_thread);
+
+ my @threads = ();
+ foreach my $raw_thread(@{$data->{threads}}) {
+ my $thread = CrashTest::Model::Thread->new($raw_thread);
+ $thread = $self->app->stackfilter->apply($thread);
+ push @threads, $thread;
+ }
+ $self->stash(threads => \@threads);
+
+ #my $similar = $self->app->storage->get_similar_crashs($self->param('uuid'));
+ #$self->stash(similar => $similar->{crashs});
+ $self->stash(extra_columns => $self->app->config->{WebInterface}->{ExtraColumns}->{Index});
+
+ $self->render("report/crash");
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Helper/Backtrace.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,44 @@
+# 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::Helper::Backtrace;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+ my ($self, $app, $conf) = @_;
+
+ $app->helper(module_warnings => sub { $self->_bt_warning("module_warnings", @_) } );
+ $app->helper(frame_warnings => sub { $self->_bt_warning("frame_warnings", @_) } );
+}
+
+sub _bt_warning {
+ my ($self, $type, $c, $frame) = @_;
+
+ my $warnings;
+ if($type eq "frame_warnings") {
+ $warnings = $frame->frame_warnings;
+ } elsif($type eq "module_warnings") {
+ $warnings = $frame->module_warnings;
+ }
+
+ return "" if(!$warnings);
+
+ my @ret;
+ foreach my $warning (@{$warnings}) {
+ push @ret, scalar $c->render_to_string('report/backtrace/warning', tooltip_text => $warning);
+ }
+
+ return Mojo::ByteStream->new(join "", @ret);
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Helper/DateTime.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,46 @@
+# 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::Helper::DateTime;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use DateTime::Format::Pg;
+
+sub register {
+ my ($self, $app, $conf) = @_;
+
+ $app->helper(date_from_db_utc => sub {
+ my $d = $self->_date_from_db_utc(@_);
+ return "" unless $d;
+ $d->set_time_zone('local')->strftime("%F %T");
+ });
+
+ $app->helper(date_with_tz_from_db_utc => sub {
+ my $d = $self->_date_from_db_utc(@_);
+ return "" unless $d;
+ $d->strftime("%FT%TZ");
+ });
+}
+
+sub _date_from_db_utc {
+ my ($self, $c, $dbdate) = @_;
+ my $utcdate;
+ eval {
+ $utcdate = DateTime::Format::Pg->parse_datetime($dbdate)->set_time_zone('UTC');
+ };
+ $c->app->log->warn($@) if $@;
+
+ return $utcdate;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Helper/XmlEscape.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,31 @@
+# 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::Helper::XmlEscape;
+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;
--- a/lib/CrashTest/Helpers/CrashTestHelpers.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-# 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/Model/CrashProcessor.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,78 @@
+# 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::Model::CrashProcessor;
+use Mojo::Base -base;
+use File::Temp;
+use UUID;
+
+has [ qw/app config/ ];
+has processors => sub { return [] };
+
+sub _register_instance {
+ my ($self, $processor_instance) = @_;
+
+ push @{$self->processors}, $processor_instance;
+}
+
+sub load_plugins {
+ my ($self) = @_;
+
+ my @conf_processors = grep(!/^Common$/, keys %{$self->config->{Processor}});
+ for my $module (@conf_processors) {
+ $self->app->plugin('CrashTest::Plugin::CrashProcessor::' . $module, {
+ config => $self->config,
+ cb => sub { my $i = shift; $self->_register_instance($i); }
+ });
+ }
+}
+
+sub find_processor {
+ my ($self, $req) = @_;
+
+ # find the first processor that accepts this kind of crash
+ foreach my $processor(@{$self->processors}) {
+ my $validator = $processor->validate($req);
+ if(!$validator->has_error) {
+ return $processor->task_name;
+ }
+ }
+ return undef;
+}
+
+sub decode {
+ my ($self, $task_name, $req) = @_;
+
+ my $tmpdir = $self->config->{Processor}->{Common}->{TmpDir};
+
+ my $files;
+ foreach my $up_name(map { $_->name } @{$req->uploads}) {
+ foreach my $up(@{$req->every_upload($up_name)}) {
+ my $fh = File::Temp->new(DIR => $tmpdir, SUFFIX => '.dat');
+ $fh->unlink_on_destroy(0);
+
+ #print $fh $up->slurp;
+ $up->move_to($fh->filename);
+
+ $files->{$up_name} ||= [];
+ push @{$files->{$up_name}}, $fh->filename;
+ }
+ }
+
+ my ($uuid, $uuidstr);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuidstr);
+ my $id = $self->app->minion->enqueue($task_name => [ $uuidstr, $req->params->to_hash, $files ]);
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Model/CrashReport.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,37 @@
+# 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::Model::CrashReport;
+use Mojo::Base -base;
+
+has [ qw/app config/ ];
+
+sub index {
+ my ($self, $page, $nperpage, $search_str) = @_;
+
+ return $self->app->storage->first("index", $page, $nperpage, $search_str);
+}
+
+sub create {
+ my ($self, $uuid, $pjson, $dump) = @_;
+
+ return $self->app->storage->each("create", $uuid, $pjson, $dump);
+}
+
+sub get_processed_data {
+ my ($self, $uuid) = @_;
+
+ return $self->app->storage->first("get_processed_data", $uuid);
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Model/Frame.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,49 @@
+# 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::Model::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 frame_number => undef;
+has module_name => "";
+has function_name => "";
+has file_link => "";
+has frame_warnings => sub { return []; };
+has module_warnings => sub { return []; };
+#has "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);
+ }
+
+ return $self;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Model/StackFilter.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,80 @@
+# 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::Model::StackFilter;
+use Mojo::Base -base;
+use Mojo::Loader qw/load_class find_modules/;
+
+has [ qw/config app filters/ ];
+
+sub new {
+ my $self = shift->SUPER::new(@_);
+
+ if(defined($self->config->{StackFilters})) {
+ $self->load_plugins($self->config->{StackFilters});
+ } else {
+ $self->find_stackfilters_plugins();
+ }
+
+ return $self;
+}
+
+sub apply {
+ my ($self, $thread) = @_;
+
+ foreach my $filter(@{$self->filters}) {
+ #say "apply filter $filter";
+ $thread = $filter->apply($thread);
+ }
+
+ return $thread;
+}
+
+sub load_plugins {
+ my ($self, $modules) = @_;
+
+ my @filters = ();
+ for my $module (@$modules) {
+ my $e = load_class($module);
+ die qq{Loading "$module" failed: $e} if ref $e;
+
+ #say "loading $module";
+ push @filters, $module->new(config => $self->config, app => $self->app);
+ }
+
+ $self->filters(\@filters);
+}
+
+sub find_stackfilters_plugins {
+ my ($self) = @_;
+
+ my @modules = ();
+
+ # Find modules in a namespace
+ for my $module (find_modules('CrashTest::Plugin::StackFilter')) {
+ my $e = load_class($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;
+
+ # 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/Model/Storage.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,70 @@
+# 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::Model::Storage;
+use Mojo::Base -base;
+
+has [ qw/app config/ ];
+has instances => sub { return [] };
+
+sub register {
+ my ($self, $storage_instance) = @_;
+
+ push @{$self->instances}, $storage_instance;
+
+ #$self->app->log->debug("Loaded $storage_instance");
+}
+
+sub load_plugins {
+ my $self = shift;
+
+ for my $storage(@{$self->config->{Storage}}) {
+ $self->app->plugin("CrashTest::Plugin::Storage::" . $storage->{Type}, $storage);
+ }
+}
+
+
+sub each {
+ my ($self, $proc, @args) = @_;
+
+ my $result = 1;
+ foreach my $storage(@{$self->instances}) {
+ if(defined(my $model = $storage->models->{CrashReport})) {
+ if($model->can($proc)) {
+ $result = $result && $model->$proc(@args);
+ }
+ }
+ }
+ return $result;
+}
+
+sub first {
+ my ($self, $proc, @args) = @_;
+
+ my @result;
+ foreach my $storage(@{$self->instances}) {
+
+ if(defined(my $model = $storage->models->{CrashReport})) {
+ if($model->can($proc)) {
+ @result = $model->$proc(@args);
+ if(@result) {
+ last;
+ }
+ }
+ }
+ }
+
+ return wantarray ? @result : shift @result;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Model/Thread.pm Wed Nov 04 17:43:00 2015 +0100
@@ -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::Model::Thread;
+use Mojo::Base -base;
+use Mojo::ByteStream 'b';
+use Mojolicious::Plugin::TagHelpers;
+use CrashTest::Model::Frame;
+
+has [ qw/frame_count crashing_thread frames/ ];
+
+sub new {
+ my $self = shift->SUPER::new(@_);
+
+ foreach my $frame(@{$self->frames}) {
+ $frame = CrashTest::Model::Frame->new($frame);
+ }
+
+ return $self;
+}
+
+sub each_frame {
+ my ($self, $block) = @_;
+
+ foreach my $frame(@{$self->frames}) {
+ $block->($frame);
+ }
+}
+
+1;
--- a/lib/CrashTest/Models/Frame.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-# 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;
--- a/lib/CrashTest/Models/Thread.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-# 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/Plugin/CrashProcessor/Breakpad.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,87 @@
+# 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::Plugin::CrashProcessor::Breakpad;
+use Mojo::Base 'Mojolicious::Plugin';
+use Mojo::JSON qw/decode_json/;
+use Mojo::Util qw/dumper/;
+use Mojolicious::Validator;
+
+has [ qw/app config dumper_config/ ];
+has task_name => "breakpad_decode_task";
+
+sub register {
+ my ($self, $app, $args) = @_;
+
+ $self->app($app);
+ $self->config($args->{config});
+ $self->dumper_config($self->config->{Processor}->{Breakpad});
+
+ $app->minion->add_task($self->task_name =>
+ sub {
+ my ($job, $uuid, $params, $files) = @_;
+ #$job->app->log->debug(dumper $params);
+
+ $job->on(failed => sub { $self->cleanup($files); });
+ #$job->on(finished => sub { $self->cleanup($files); });
+
+ $self->decode($uuid, $params, $files);
+ $self->cleanup($files);
+ }
+ );
+
+ $app->log->debug("Registered Breakpad");
+ $args->{cb}->($self);
+}
+
+sub validate {
+ my ($self, $req) = @_;
+
+ my $hash = $req->params->to_hash;
+ $hash->{$_} = $req->every_upload($_) for map { $_->name } @{$req->uploads};
+
+ my $validation = Mojolicious::Validator->new->validation->input($hash);
+
+ $validation->required('UserID');
+ #$validation->required('Version');
+ $validation->required('ProductName');
+
+ $validation->required('upload_file_minidump')->upload->size(1024, 4 * 1024 * 1024);
+
+ return $validation;
+}
+
+sub decode {
+ my ($self, $uuidstr, $client_info, $files) = @_;
+
+ my $dmp_file = $files->{upload_file_minidump}->[0];
+
+ my $cmd = $self->dumper_config->{JSONStackwalker} . " $dmp_file " . $self->dumper_config->{SymbolsPath};
+ my $out = qx($cmd 2>/dev/null) or die $!;
+
+ my $pjson = decode_json($out);
+
+ $self->app->crash_reports->create($uuidstr, $pjson, $client_info, $dmp_file);
+}
+
+sub cleanup {
+ my ($self, $files) = @_;
+ #$self->app->log->debug("cleanup " . dumper $files);
+ foreach my $values(values %$files) {
+ foreach my $f(@$values) {
+ unlink $f or $self->app->log->warn("Failed to unlink $f: $!");
+ }
+ }
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/StackFilter/FileLink.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,104 @@
+# 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::Plugin::StackFilter::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 _link_template {
+ my ($self, $scm, $repo) = @_;
+
+ my $linkconf = "$scm:$repo";
+ if(defined($self->{_template_cache}->{$linkconf})) {
+ return $self->{_template_cache}->{$linkconf};
+ }
+ my $template = $self->config->{WebInterface}->{ScmLinks}->{$linkconf};
+
+ # try the generic repository type
+ if(!defined($template)) {
+ $linkconf = $scm;
+ if(defined($self->{_template_cache}->{$linkconf})) {
+ return $self->{_template_cache}->{$linkconf};
+ }
+ $template = $self->config->{WebInterface}->{ScmLinks}->{$linkconf};
+ }
+
+ return undef if !defined($template);
+
+ $self->app->log->debug("Building template for $linkconf");
+
+ my $mt = Mojo::Template->new
+ ->prepend('my ($repo, $scmpath, $rev, $line) = @_;')
+ ->auto_escape(1)
+ ->escape(sub {
+ my $str = shift;
+ return Mojo::ByteStream::b($str)->url_escape;
+ })
+ ->parse($template)
+ ->build;
+ my $e = $mt->compile;
+ if($e) {
+ $self->app->log->error($e);
+ return undef;
+ }
+
+ $self->{_template_cache}->{$linkconf} = $mt;
+ return $mt;
+}
+
+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 =~ /([^:]+):([^:]+):([^:]+):(.+)/) {
+ my ($scm, $repo, $scmpath, $rev) = ($1, $2, $3, $4);
+ my $filename = File::Spec->splitpath($scmpath);
+
+ # and we have a link template configured for this specific repository or repository type
+ my $template = $self->_link_template($scm, $repo);
+ if(defined($template)) {
+ # build url using the configured template
+ my $url = $template->interpret($repo, $scmpath, $rev, $line);
+ # and 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/Plugin/StackFilter/FrameTrust.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,61 @@
+# 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::Plugin::StackFilter::FrameTrust;
+use Mojo::Base -base;
+
+sub priority { return 10; }
+
+has [ qw/app/ ];
+
+sub new {
+ my $self = shift->SUPER::new(@_);
+
+ return $self;
+}
+
+sub apply {
+ my ($self, $thread) = @_;
+
+ $thread->each_frame(sub {
+ my $frame = shift;
+
+ $self->set_trust($frame);
+ }
+ );
+
+ return $thread;
+}
+
+sub set_trust {
+ my ($self, $frame) = @_;
+
+ if(!defined($frame->module) && $frame->offset ne "0x0") {
+ push @{$frame->module_warnings}, "No module loaded at this address";
+ }
+
+ if($frame->missing_symbols) {
+ push @{$frame->module_warnings}, "Missing symbols";
+ }
+
+ if($frame->corrupt_symbols) {
+ push @{$frame->module_warnings}, "Corrupt symbols";
+ }
+
+ if(defined($frame->trust) && $frame->trust eq "scan") {
+ push @{$frame->frame_warnings}, "Imprecise stackwalking";
+ }
+
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/StackFilter/HideArgs.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,56 @@
+# 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::Plugin::StackFilter::HideArgs;
+use Mojo::Base -base;
+use Text::Balanced qw/extract_bracketed/;
+
+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 = "";
+ my $text = $signature;
+ do {
+ my ($str, $next, $prefix) = extract_bracketed($text, '<(', '[^<(]*');
+ if($str) {
+ $short_signature .= $prefix . substr($str, 0, 1) . substr($str, -1, 1);
+ $text = $next;
+ } else {
+ $short_signature .= $next;
+ $text = undef;
+ }
+ } while($text);
+
+ return $self->app->t(code => (title => $signature, class => "shortened-signature prettyprint lang-cpp") => $short_signature);
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/Base.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,48 @@
+# 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::Plugin::Storage::Base;
+use Mojo::Base -base;
+use Scalar::Util qw/weaken/;
+
+use Mojo::Loader qw/find_packages find_modules load_class/;
+has [ qw/app config/ ];
+
+sub _load_models {
+ my ($self, $model_ns) = @_;
+
+ my %models;
+
+ my @pkgs = find_packages $model_ns;
+ push @pkgs, find_modules $model_ns;
+ foreach my $pkg(@pkgs) {
+ my @pkg_comp = split(/::/, $pkg);
+ my $pkg_name = $pkg_comp[-1];
+
+ my $e = load_class $pkg;
+ warn qq{Loading "$pkg" failed: $e} and next if ref $e;
+
+ my $instance = "$pkg"->new(instance => $self, app => $self->app, config => $self->config);
+ weaken $instance->{instance};
+ weaken $instance->{app};
+ weaken $instance->{config};
+ $models{$pkg_name} = $instance;
+ }
+
+ weaken $self->{app};
+ weaken $self->{config};
+
+ return \%models;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/File.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,98 @@
+# 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::Plugin::Storage::File;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+ my ($self, $app, $conf) = @_;
+
+ $app->storage->register(CrashTest::Plugin::Storage::File::Instance->new(app => $app, config => $conf));
+}
+
+1;
+
+package CrashTest::Plugin::Storage::File::Instance;
+use Mojo::Base "CrashTest::Plugin::Storage::Base";
+
+has models => sub {
+ my $self = shift;
+
+ state $models = $self->_load_models("CrashTest::Plugin::Storage::File::Model");
+};
+
+1;
+
+package CrashTest::Plugin::Storage::File::Model::CrashReport;
+use Mojo::Base -base;
+use Mojo::Util qw/slurp spurt/;
+use Mojo::JSON qw/j decode_json/;
+use File::Spec;
+
+has [ qw/instance app config extra_columns data_path/ ];
+
+sub new {
+ my $self = shift->SUPER::new(@_);
+
+ $self->data_path($self->config->{DataDir});
+
+ return $self;
+}
+
+sub create {
+ my ($self, $uuid, $pjson, $client_info, $dump) = @_;
+
+ $self->_store_dump($uuid, $dump);
+ $self->_store_processed_data($uuid, $pjson, $client_info);
+}
+
+sub get_processed_data {
+ my ($self, $uuid) = @_;
+
+ my $jsonfilename = File::Spec->catfile($self->data_path, "$uuid.json");
+ my $json_content = slurp($jsonfilename) or die $!;
+
+ my $processed_data = decode_json($json_content);
+
+ return $processed_data;
+}
+
+sub _store_processed_data {
+ my ($self, $uuid, $pjson, $client_info) = @_;
+
+ # Create json for the params
+ $pjson->{client_info} = $client_info;
+
+ my $jsonfilename = File::Spec->catfile($self->data_path, "$uuid.json");
+ my $dmpfilename = File::Spec->catfile($self->data_path, "$uuid.dmp");
+
+ spurt(j($pjson), $jsonfilename) or die $!;
+
+ # Set time of the .dmp to the CrashTime
+ my $crashtime = $pjson->{client_info}->{CrashTime};
+ if(defined($crashtime)) {
+ utime $crashtime, $crashtime, $dmpfilename;
+ }
+}
+
+sub _store_dump {
+ my ($self, $uuid, $dmp_content) = @_;
+
+ my $dmp_file = File::Spec->catfile($self->data_path, "$uuid.dmp");
+ my $fh = IO::File->new($dmp_file, "w") or die($!);
+ $fh->binmode;
+ print $fh $dmp_content;
+ undef $fh;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/Sql.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,50 @@
+# 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::Plugin::Storage::Sql;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+ my ($self, $app, $conf) = @_;
+
+ push @{$app->commands->namespaces}, 'CrashTest::Plugin::Storage::Sql::Command';
+ $app->storage->register(CrashTest::Plugin::Storage::Sql::Instance->new(app => $app, config => $conf));
+}
+
+1;
+
+package CrashTest::Plugin::Storage::Sql::Instance;
+use Mojo::Base "CrashTest::Plugin::Storage::Base";
+use Mojo::Loader qw/load_class/;
+
+has dbtype => sub {
+ my $self = shift;
+ my @dbtypes = keys %{$self->config->{db}};
+ state $dbtype = $dbtypes[0];
+};
+
+has dbh => sub {
+ my $self = shift;
+
+ my $type = $self->dbtype;
+ load_class "Mojo::$type";
+ state $dbh = "Mojo::$type"->new($self->config->{db}->{$type});
+};
+
+has models => sub {
+ my $self = shift;
+
+ state $models = $self->_load_models("CrashTest::Plugin::Storage::Sql::Model");
+};
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/Sql/Command/db.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,65 @@
+# 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::Plugin::Storage::Sql::Command::db;
+use Mojo::Base 'Mojolicious::Command';
+use File::Spec::Functions 'catdir';
+use File::Basename;
+
+# Short description
+has description => 'Setup database';
+
+# Short usage message
+has usage => <<EOF;
+Usage: APPLICATION db [OPTIONS]
+
+create Create database
+reset Remove and re-create all tables
+upgrade Upgrade database to latest schema version
+
+EOF
+
+sub run {
+ my ($self, @args) = @_;
+
+ if(scalar @args < 1) {
+ say $self->usage;
+ exit 0;
+ }
+
+ my $path = dirname(__FILE__);
+
+ foreach my $storage_instance(@{$self->app->storage->instances}) {
+ if($storage_instance->isa("CrashTest::Plugin::Storage::Sql::Instance")) {
+
+ my $migrations = $storage_instance->dbh->migrations;
+ $migrations->name("crashtest");
+ $migrations->from_file(catdir($path, '..', 'migrations_pg.sql'));
+
+ if($args[0] eq "create") {
+ $migrations->migrate;
+ } elsif($args[0] eq "reset") {
+ $migrations->migrate(0)->migrate;
+ } elsif($args[0] eq "upgrade") {
+ $migrations->migrate;
+ } elsif($args[0] eq "downgrade") {
+ } else {
+ say "Invalid arguments";
+ exit 1;
+ }
+
+ }
+ }
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/Sql/Model/CrashReport.pm Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,213 @@
+# 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::Plugin::Storage::Sql::Model::CrashReport;
+use Mojo::Base -base;
+use Mojo::JSON qw/j decode_json/;
+use DateTime;
+use Data::Page;
+#use DBI::Log;
+#$DBI::Log::trace = 0;
+use Search::QueryParser::SQL;
+
+has [ qw/instance app config/ ];
+
+has qw/db/;
+
+sub new {
+ my $self = shift->SUPER::new(@_);
+
+ $self->db($self->instance->dbh->db);
+
+ return $self;
+}
+
+sub _build_query_from_search_string {
+ my ($self, $search) = @_;
+
+ my @values;
+
+ # define a callback to collect values safely (without magic markers)
+ my $cb = sub {
+ my ($column, $this_op, $value) = @_;
+ say $column;
+ if($column->type eq "fuzzy") {
+ $this_op = " ILIKE ";
+ $value = "%$value%";
+ }
+ push @values, $value;
+ return join('', $column->stringify, $this_op, '?');
+ };
+
+ my $search_fields = {
+ user_id => { callback => $cb, name => 'u.user_id' },
+ product => { callback => $cb, name => 'p.name' },
+ version => { callback => $cb, name => 'p.version' },
+ channel => { callback => $cb, name => 'p.release_channel' },
+ function => { callback => $cb, name => 'extract_crashing_functions(d.processed)', type => "fuzzy" },
+ };
+
+ my $parser = Search::QueryParser::SQL->new(
+ columns => $search_fields,
+ default_column => "function",
+ strict => 1,
+ );
+
+ my $query = $parser->parse($search, 1)
+ or die "Error in query: " . $parser->err;
+
+ # reset before calling dbi
+ @values = ();
+
+ return [ $query->dbi->[0], \@values ];
+}
+
+sub index {
+ my ($self, $pagen, $nperpage, $search_str) = @_;
+
+ my $where = "";
+ my @values = ();
+ if(defined($search_str) && $search_str ne "") {
+ my $q = $self->_build_query_from_search_string($search_str);
+ $where = "WHERE " . $q->[0];
+ @values = @{$q->[1]};
+ }
+
+ my $count = $self->db->query("
+ SELECT count(crash_reports.id) AS total FROM crash_reports
+ JOIN crash_users AS u ON crash_reports.crash_user_id = u.id
+ JOIN products AS p ON crash_reports.product_id = p.id
+ JOIN crash_report_datas AS d ON crash_reports.id = d.crash_report_id
+ $where
+ ", @values)->hash;
+
+ my $pager = Data::Page->new();
+ $pager->total_entries($count->{total});
+ $pager->entries_per_page($nperpage);
+ $pager->current_page($pagen);
+
+ my $results = $self->db->query("
+ SELECT crash_reports.*,
+ p.distributor AS p_distributor, p.name AS p_name, p.version AS p_version, p.release_channel AS p_release_channel,
+ u.os AS u_os, u.cpu_arch AS u_cpu_arch, u.cpu_count AS u_cpu_count, u.extra_info AS u_extra_info
+ FROM crash_reports
+ JOIN crash_users AS u ON crash_reports.crash_user_id = u.id
+ JOIN products AS p ON crash_reports.product_id = p.id
+ JOIN crash_report_datas AS d ON crash_reports.id = d.crash_report_id
+ $where
+ ORDER BY crash_time DESC
+ OFFSET (?) ROWS
+ FETCH NEXT (?) ROWS ONLY
+ ",
+ @values,
+ $pager->skipped, $pager->entries_per_page
+ )->hashes;
+
+ return ($results, $pager);
+}
+
+sub get_processed_data {
+ my ($self, $uuid) = @_;
+
+ my $dbdata = $self->db->query("SELECT processed FROM crash_report_datas WHERE crash_report_id = (SELECT id FROM crash_reports WHERE uuid = ?)", $uuid)->hash;
+
+ my $processed_data = decode_json($dbdata->{processed});
+
+ return $processed_data;
+}
+
+sub create {
+ my ($self, $uuid, $pjson, $client_info, $dmp_content) = @_;
+
+ if(!defined($client_info->{UserID})) {
+ $self->app->log->info("Invalid crash $uuid: no UserID");
+ return;
+ }
+
+ my $tx = $self->db->begin;
+
+ my ($start_time, $crash_time);
+ if($client_info->{StartupTime}) {
+ $start_time = DateTime->from_epoch(epoch => $client_info->{StartupTime});
+ }
+ if($client_info->{CrashTime}) {
+ $crash_time = DateTime->from_epoch(epoch => $client_info->{CrashTime});
+ }
+
+ my @product_values = (
+ $client_info->{Distributor},
+ $client_info->{ProductName},
+ $client_info->{Version},
+ );
+
+ my $dbproduct = $self->db->query("SELECT * FROM products WHERE distributor = ? AND name = ? AND version = ?", @product_values)->hash;
+ if(!$dbproduct) {
+ push @product_values, $client_info->{ReleaseChannel};
+ $dbproduct = $self->db->query(
+ "INSERT INTO products (distributor, name, version, release_channel) VALUES (?, ?, ?, ?) RETURNING *",
+ @product_values
+ )->hash;
+ }
+
+ my @user_values = (
+ $client_info->{UserID},
+ );
+
+ my $dbuser = $self->db->query("SELECT * FROM crash_users WHERE user_id = ?", @user_values)->hash;
+ if(!$dbuser) {
+ push @user_values, $pjson->{system_info}->{cpu_arch};
+ push @user_values, $pjson->{system_info}->{cpu_count};
+ push @user_values, $pjson->{system_info}->{os};
+ push @user_values, j($client_info);
+
+ $dbuser = $self->db->query(
+ "INSERT INTO crash_users (user_id, cpu_arch, cpu_count, os, extra_info) VALUES (?, ?, ?, ?, ?) RETURNING *",
+ @user_values
+ )->hash;
+ }
+
+ my $main_module;
+ {
+ my $i = $pjson->{main_module};
+ $main_module = $pjson->{modules}->[$i]->{filename};
+ }
+
+ my $dbcrash = $self->db->query(
+ "INSERT INTO crash_reports (start_time, crash_time, uuid, main_module, product_id, crash_user_id) VALUES (?, ?, ?, ?, ?, ?) RETURNING id",
+ $start_time, $crash_time, $uuid, $main_module, $dbproduct->{id}, $dbuser->{id}
+ )->hash;
+
+ $self->db->query(
+ "INSERT INTO crash_report_datas (crash_report_id, processed) VALUES (?, ?) RETURNING id",
+ $dbcrash->{id}, j($pjson)
+ );
+
+ $tx->commit;
+
+ return $dbcrash->{id};
+}
+
+sub update {
+ my ($self, $uuid, $pjson, $dmp_content) = @_;
+
+ my $tx = $self->db->begin;
+
+ my $dbcrash = $self->db->query("SELECT id FROM crash_reports WHERE uuid = ?", $uuid)->hash;
+ $self->db->query("UPDATE crash_report_datas SET processed = ? WHERE crash_report_id = ?", $pjson, $dbcrash->{id});
+
+ $tx->commit;
+
+ return 1;
+}
+
+1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CrashTest/Plugin/Storage/Sql/migrations_pg.sql Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,79 @@
+-- ###########################################################################
+-- 1 up
+-- ###########################################################################
+CREATE TABLE "crash_users" (
+ "id" serial NOT NULL,
+ "user_id" character varying(40) NOT NULL,
+ "os" character varying(40),
+ "cpu_arch" character varying(10),
+ "cpu_count" integer,
+ "extra_info" jsonb,
+ PRIMARY KEY ("id")
+);
+CREATE TABLE "products" (
+ "id" serial NOT NULL,
+ "distributor" character varying(40),
+ "name" character varying(40),
+ "version" character varying(40),
+ "release_channel" character varying,
+ PRIMARY KEY ("id")
+);
+CREATE TABLE "crash_reports" (
+ "id" serial NOT NULL,
+ "start_time" timestamp,
+ "crash_time" timestamp NOT NULL,
+ "uuid" uuid NOT NULL,
+ "main_module" character varying(40),
+ "crash_user_id" integer NOT NULL,
+ "product_id" integer NOT NULL,
+ CONSTRAINT "crash_reports_uuid_idx" UNIQUE ("uuid"),
+ PRIMARY KEY ("id")
+);
+CREATE INDEX "crash_reports_idx_crash_user_id" on "crash_reports" ("crash_user_id");
+CREATE INDEX "crash_reports_idx_product_id" on "crash_reports" ("product_id");
+CREATE INDEX "crash_reports_crash_time_idx" on "crash_reports" ("crash_time");
+CREATE TABLE "crash_report_datas" (
+ "id" serial NOT NULL,
+ "processed" jsonb NOT NULL,
+ "crash_report_id" integer NOT NULL,
+ CONSTRAINT "crash_report_datas_idx_crash_report_id" UNIQUE ("crash_report_id"),
+ PRIMARY KEY ("id")
+);
+-- CREATE INDEX "crash_report_datas_idx_crash_report_id" on "crash_report_datas" ("crash_report_id");
+ALTER TABLE "crash_reports" ADD CONSTRAINT "crash_reports_fk_crash_user_id" FOREIGN KEY ("crash_user_id")
+ REFERENCES "crash_users" ("id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+ALTER TABLE "crash_reports" ADD CONSTRAINT "crash_reports_fk_product_id" FOREIGN KEY ("product_id")
+ REFERENCES "products" ("id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+ALTER TABLE "crash_report_datas" ADD CONSTRAINT "crash_report_datas_fk_crash_report_id" FOREIGN KEY ("crash_report_id")
+ REFERENCES "crash_reports" ("id") ON DELETE CASCADE DEFERRABLE;
+
+CREATE OR REPLACE FUNCTION extract_crashing_functions (processed_json jsonb) RETURNS text AS
+$$
+ -- extract threads[crashing_idx]->frames[*]->function
+ SELECT string_agg(functions, E'\n')
+ FROM (
+ SELECT jsonb_array_elements(
+ (($1 #> ARRAY['threads', $1->'crash_info'->>'crashing_thread'])->'frames')
+ )->>'function' AS functions
+ ) AS frames
+$$
+LANGUAGE sql IMMUTABLE;
+
+-- This extension is the contrib modules
+CREATE EXTENSION IF NOT EXISTS pg_trgm;
+
+CREATE INDEX crash_report_datas_idx_extract_crashing_functions ON crash_report_datas USING gist (
+ extract_crashing_functions(processed) gist_trgm_ops
+);
+
+-- ###########################################################################
+-- 1 down
+-- ###########################################################################
+DROP TABLE crash_report_datas CASCADE;
+DROP TABLE crash_reports CASCADE;
+DROP TABLE products CASCADE;
+DROP TABLE crash_users CASCADE;
+DROP FUNCTION extract_crashing_functions (processed_json jsonb);
+
+
+-- vim:ft=pgsql:
--- a/lib/CrashTest/StackFilter.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-# 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 qw/load_class find_modules/;
-
-has [ qw/config app filters/ ];
-
-sub new {
- my $self = shift->SUPER::new(@_);
-
- if(defined($self->config->{StackFilters})) {
- $self->load_plugins($self->config->{StackFilters});
- } else {
- $self->find_stackfilters_plugins();
- }
-
- return $self;
-}
-
-sub apply {
- my ($self, $thread) = @_;
-
- foreach my $filter(@{$self->filters}) {
- #say "apply filter $filter";
- $thread = $filter->apply($thread);
- }
-
- return $thread;
-}
-
-sub load_plugins {
- my ($self, $modules) = @_;
-
- my @filters = ();
- for my $module (@$modules) {
- my $e = load_class($module);
- die qq{Loading "$module" failed: $e} if ref $e;
-
- #say "loading $module";
- push @filters, $module->new(config => $self->config, app => $self->app);
- }
-
- $self->filters(\@filters);
-}
-
-sub find_stackfilters_plugins {
- my ($self) = @_;
-
- my @modules = ();
-
- # Find modules in a namespace
- for my $module (find_modules('CrashTest::StackFilters')) {
- my $e = load_class($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;
-
- # instanciate
- my @filters = map { $_->[0]->new(config => $self->config, app => $self->app) } @modules;
-
- $self->filters(\@filters);
-}
-
-1;
--- a/lib/CrashTest/StackFilters/FileLink.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-# 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 _link_template {
- my ($self, $scm, $repo) = @_;
-
- my $linkconf = "$scm:$repo";
- if(defined($self->{_template_cache}->{$linkconf})) {
- return $self->{_template_cache}->{$linkconf};
- }
- my $template = $self->config->{WebInterface}->{ScmLinks}->{$linkconf};
-
- # try the generic repository type
- if(!defined($template)) {
- $linkconf = $scm;
- if(defined($self->{_template_cache}->{$linkconf})) {
- return $self->{_template_cache}->{$linkconf};
- }
- $template = $self->config->{WebInterface}->{ScmLinks}->{$linkconf};
- }
-
- return undef if !defined($template);
-
- $self->app->log->debug("Building template for $linkconf");
-
- my $mt = Mojo::Template->new
- ->prepend('my ($repo, $scmpath, $rev, $line) = @_;')
- ->auto_escape(1)
- ->escape(sub {
- my $str = shift;
- return Mojo::ByteStream::b($str)->url_escape;
- })
- ->parse($template)
- ->build;
- my $e = $mt->compile;
- if($e) {
- $self->app->log->error($e);
- return undef;
- }
-
- $self->{_template_cache}->{$linkconf} = $mt;
- return $mt;
-}
-
-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 =~ /([^:]+):([^:]+):([^:]+):(.+)/) {
- my ($scm, $repo, $scmpath, $rev) = ($1, $2, $3, $4);
- my $filename = File::Spec->splitpath($scmpath);
-
- # and we have a link template configured for this specific repository or repository type
- my $template = $self->_link_template($scm, $repo);
- if(defined($template)) {
- # build url using the configured template
- my $url = $template->interpret($repo, $scmpath, $rev, $line);
- # and 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;
-
--- a/lib/CrashTest/StackFilters/FrameTrust.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-# 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::FrameTrust;
-use Mojo::Base -base;
-
-sub priority { return 10; }
-
-has [ qw/app/ ];
-
-sub new {
- my $self = shift->SUPER::new(@_);
-
- # Search for inline templates in this class too
- push @{$self->app->renderer->classes}, __PACKAGE__;
-
- # bug in Mojolicious ?
- # force refresh of the template cache
- $self->app->renderer->_warmup;
-
- return $self;
-}
-
-sub apply {
- my ($self, $thread) = @_;
-
- # create a pseudo controller to render templates
- my $c = $self->app->controller_class->new(app => $self->app);
-
- $thread->each_frame(sub {
- my $frame = shift;
-
- $self->set_trust($c, $frame);
- }
- );
-
- return $thread;
-}
-
-sub set_trust {
- my ($self, $c, $frame) = @_;
-
- if(!defined($frame->module) && $frame->offset ne "0x0") {
- $frame->module_name(
- $c->render_to_string(
- template => "stackfilters/frame_trust/warning", partial => 1,
- content_text => $frame->module_name, popup_text => "No module loaded at this address"
- )
- );
- }
-
- if($frame->missing_symbols) {
- $frame->module_name(
- $c->render_to_string(
- template => "stackfilters/frame_trust/warning", partial => 1,
- content_text => $frame->module_name, popup_text => "Missing symbols"
- )
- );
- }
-
- if(defined($frame->trust) && $frame->trust eq "scan") {
- $frame->function_name(
- $c->render_to_string(
- template => "stackfilters/frame_trust/warning", partial => 1,
- content_text => $frame->function_name, popup_text => "Imprecise stackwalking"
- )
- );
- }
-
-}
-
-1;
-
-__DATA__
-
-@@ stackfilters/frame_trust/warning.html.ep
-%= tag a => (href => "#", class => "text-danger", "data-toggle" => "tooltip", "data-original-title" => $popup_text) => begin
- %= tag span => (class => "glyphicon glyphicon-warning-sign") => ""
-% end
-%= $content_text
-
--- a/lib/CrashTest/StackFilters/HideArgs.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-# 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;
-use Text::Balanced qw/extract_bracketed/;
-
-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 = "";
- my $text = $signature;
- do {
- my ($str, $next, $prefix) = extract_bracketed($text, '<(', '[^<(]*');
- if($str) {
- $short_signature .= $prefix . substr($str, 0, 1) . substr($str, -1, 1);
- $text = $next;
- } else {
- $short_signature .= $next;
- $text = undef;
- }
- } while($text);
-
- return $self->app->t(code => (title => $signature, class => "shortened-signature prettyprint lang-cpp") => sub { return $short_signature });
-}
-
-1;
-
--- a/lib/CrashTest/Storage/FileSystem.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-# 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::Storage::FileSystem;
-use Mojo::Base -base;
-use Mojo::Util qw/slurp spurt/;
-use Mojo::JSON qw/j decode_json/;
-use DateTime;
-use Data::Page;
-
-has [ qw/config data_path/ ];
-
-sub new {
- my $self = shift->SUPER::new(@_);
-
- $self->data_path($self->config->{DataDir});
-
- return $self;
-}
-
-sub index {
- my ($self, $page, $nperpage) = @_;
-
- my @files;
- my $dh;
- opendir($dh, $self->data_path) or die $!;
- my @allfiles = readdir $dh;
- foreach(@allfiles) {
- if($_ =~ /(.*)\.json$/) {
- my $filename = File::Spec->catfile($self->data_path, $_);
- if(-f $filename) {
- push @files, {
- file => $filename,
- uuid => $1,
- product => "",
- version => "",
- user => "",
- date => DateTime->from_epoch(epoch => ((stat $filename)[9])),
- };
- }
- }
- }
- closedir $dh;
-
- my $by_date = sub { $b->{date} <=> $a->{date} };
- my @sorted_files = sort $by_date @files;
-
- my $pager = Data::Page->new();
- $pager->total_entries(scalar @files);
- $pager->entries_per_page($nperpage);
- $pager->current_page($page);
-
- if($page <= $pager->last_page) {
- @sorted_files = @sorted_files[($pager->first - 1)..($pager->last - 1)];
- } else {
- @sorted_files = ();
- }
-
- foreach(@sorted_files)
- {
- $_->{product} = $self->get_processed_data($_->{uuid})->{client_info}->{ProductName};
- $_->{version} = $self->get_processed_data($_->{uuid})->{client_info}->{Version};
- $_->{user} = $self->get_processed_data($_->{uuid})->{client_info}->{UserID};
- }
-
- my $results = {
- pager => $pager,
- crashs => \@sorted_files,
- };
-
- return $results;
-}
-
-sub get_processed_data {
- my ($self, $uuid) = @_;
-
- my $jsonfilename = File::Spec->catfile($self->data_path, "$uuid.json");
- my $json_content = slurp($jsonfilename) or die $!;
-
- my $processed_data = decode_json($json_content);
-
- return $processed_data;
-}
-
-sub store_processed_data {
- my ($self, $uuid, $pjson) = @_;
-
- my $jsonfilename = File::Spec->catfile($self->data_path, "$uuid.json");
- my $dmpfilename = File::Spec->catfile($self->data_path, "$uuid.dmp");
-
- spurt(j($pjson), $jsonfilename) or die $!;
-
- # Set time of the .dmp to the CrashTime
- my $crashtime = $pjson->{client_info}->{CrashTime};
- if(defined($crashtime)) {
- utime $crashtime, $crashtime, $dmpfilename;
- }
-}
-
-sub store_dump {
- my ($self, $uuid, $file) = @_;
-
- my $dmp_file = File::Spec->catfile($self->data_path, "$uuid.dmp");
- my $fh = IO::File->new($dmp_file, "w") or die($!);
- $fh->binmode;
- print $fh $file;
- undef $fh;
-}
-
-1;
--- a/lib/CrashTest/Storage/Sql.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-# 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::Storage::Sql;
-use Mojo::Base "CrashTest::Storage::FileSystem";
-use Mojo::JSON qw/j decode_json/;
-use DateTime;
-use CrashTest::Storage::Sql::Schema;
-use Search::Query;
-
-has [ qw/extra_columns/ ];
-
-sub new {
- my $self = shift->SUPER::new(@_);
-
- $self->{schema} = CrashTest::Storage::Sql::Schema->connect($self->config->{DSN});
-
- return $self;
-}
-
-sub index {
- my ($self, $page, $nperpage, $search_str) = @_;
-
- my $dbic_query = {};
-
- if(defined($search_str) && $search_str ne "") {
- my $search_fields = {
- user_id => { alias_for => 'crash_user.user_id' },
- product => { alias_for => 'product.name' },
- version => { alias_for => 'product.version' },
- channel => { alias_for => 'product.release_channel' },
- function => { alias_for => 'extract_crashing_functions(processed)' },
- };
- my $query = Search::Query->parser(
- dialect => 'DBIxClass',
- fields => $search_fields,
- default_field => [ 'extract_crashing_functions(processed)' ],
- )->parse($search_str);
-
- if(defined($query)) {
- $dbic_query = $query->as_dbic_query;
- }
- }
-
- my @select = ();
- my @sel_as = ();
- my @extra_ids = ();
-
- if($self->extra_columns) {
- foreach my $extra_col(@{$self->extra_columns->{Index}}) {
- push @select, $extra_col->{db_column};
- push @sel_as, $extra_col->{id};
- push @extra_ids, $extra_col->{id};
- }
- }
-
- my $dbcrashs = $self->{schema}->resultset('CrashReport')->search_rs(
- $dbic_query,
- {
- prefetch => 'product',
- join => [ qw/crash_user crash_report_data/ ],
- '+select' => \@select,
- '+as' => \@sel_as,
- order_by => { -desc => 'crash_time' },
- page => $page,
- rows => $nperpage,
- },
- );
-
- my $results = {
- pager => $dbcrashs->pager,
- crashs => [],
- };
-
- for my $crash($dbcrashs->all) {
- my $filename = File::Spec->catfile($self->{data_path}, $crash->uuid);
-
- my $result = {
- file => $filename,
- uuid => $crash->uuid,
- product => $crash->product->name,
- version => $crash->product->version,
- date => $crash->crash_time,
- };
-
- foreach (@extra_ids) {
- $result->{$_} = $crash->get_column($_);
- }
-
- push @{$results->{crashs}}, $result;
- }
-
- return $results;
-}
-
-sub _db_insert_processed_data {
- my ($self, $uuid, $pjson) = @_;
-
- $self->{schema}->txn_do(sub {
-
- my $crash = $self->{schema}->resultset('CrashReport')->new({ uuid => $uuid });
-
- if($pjson->{client_info}->{StartupTime}) {
- $crash->start_time(DateTime->from_epoch(epoch => $pjson->{client_info}->{StartupTime}));
- }
- if($pjson->{client_info}->{CrashTime}) {
- $crash->crash_time(DateTime->from_epoch(epoch => $pjson->{client_info}->{CrashTime}));
- }
-
- my $product = {
- distributor => $pjson->{client_info}->{Distributor},
- name => $pjson->{client_info}->{ProductName},
- version => $pjson->{client_info}->{Version},
- };
-
- my $dbproduct = $self->{schema}->resultset('Product')->search($product)->first();
- if($dbproduct) {
- $crash->product($dbproduct);
- } else {
- $product->{release_channel} = $pjson->{client_info}->{ReleaseChannel};
- $crash->product($self->{schema}->resultset('Product')->new($product));
- }
-
- my $user = {
- user_id => $pjson->{client_info}->{UserID},
- };
-
- my $dbuser = $self->{schema}->resultset('CrashUser')->search($user)->first();
- if($dbuser) {
- $crash->crash_user($dbuser);
- } else {
- $user->{cpu_arch} = $pjson->{system_info}->{cpu_arch};
- $user->{cpu_count} = $pjson->{system_info}->{cpu_count};
- $user->{os} = $pjson->{system_info}->{os};
- $user->{extra_info} = j($pjson->{client_info});
- $crash->crash_user($self->{schema}->resultset('CrashUser')->new($user));
- }
-
- my $txt_json = j($pjson);
- $crash->create_related('crash_report_data', {
- processed => $txt_json,
- });
-
- $crash->insert;
- });
-}
-
-sub store_processed_data {
- my ($self, $uuid, $pjson) = @_;
-
- $self->_db_insert_processed_data($uuid, $pjson);
-}
-
-sub get_processed_data {
- my ($self, $uuid) = @_;
-
- my $crash = $self->{schema}->resultset('CrashReport')->search(
- { uuid => $uuid },
- { prefetch => 'crash_report_data' }
- )->first();
-
- return decode_json($crash->crash_report_data->processed);
-}
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# 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/>.
-
-use utf8;
-package CrashTest::Storage::Sql::Schema;
-use base qw/DBIx::Class::Schema/;
-
-our $VERSION = 2;
-
-__PACKAGE__->load_namespaces();
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Candy.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# 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::Storage::Sql::Schema::Candy;
-
-use Mojo::Base -strict;
-use base 'DBIx::Class::Candy';
-
-sub perl_version { 12 }
-sub autotable { 1 }
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Result/CrashReport.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-# 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::Storage::Sql::Schema::Result::CrashReport;
-
-use CrashTest::Storage::Sql::Schema::Candy;
-__PACKAGE__->load_components(qw/InflateColumn::DateTime Core/);
-
-primary_column id => {
- data_type => 'int',
- is_auto_increment => 1,
-};
-
-column start_time => {
- data_type => 'timestamp',
- inflate_datetime => 1,
- is_nullable => 1,
-};
-
-column crash_time => {
- data_type => 'timestamp',
- inflate_datetime => 1,
- is_nullable => 1,
-};
-
-column uuid => {
- data_type => 'uuid',
-};
-
-column bug_reference => {
- data_type => 'varchar',
- size => 20,
- is_nullable => 1,
-};
-
-has_one crash_report_data => 'CrashTest::Storage::Sql::Schema::Result::CrashReportData', 'crash_report_id';
-
-column crash_user_id => { data_type => 'int' };
-belongs_to crash_user => 'CrashTest::Storage::Sql::Schema::Result::CrashUser', 'crash_user_id';
-
-column product_id => { data_type => 'int' };
-belongs_to product => 'CrashTest::Storage::Sql::Schema::Result::Product', 'product_id';
-
-sub sqlt_deploy_hook {
- my ($self, $sqlt_table) = @_;
- $sqlt_table->add_index(
- name => 'crash_reports_uuid_idx',
- fields => [ 'uuid' ],
- type => 'unique',
- );
- $sqlt_table->add_index(
- name => 'crash_reports_crash_time_idx',
- fields => [ 'crash_time' ]
- );
-}
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Result/CrashReportData.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-# 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::Storage::Sql::Schema::Result::CrashReportData;
-
-use CrashTest::Storage::Sql::Schema::Candy;
-
-primary_column id => {
- data_type => 'int',
- is_auto_increment => 1,
-};
-
-column processed => {
- data_type => 'jsonb',
-};
-
-column crash_report_id => { data_type => 'int' };
-belongs_to crash_report => 'CrashTest::Storage::Sql::Schema::Result::CrashReport', 'crash_report_id';
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Result/CrashUser.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-# 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::Storage::Sql::Schema::Result::CrashUser;
-
-use CrashTest::Storage::Sql::Schema::Candy;
-
-primary_column id => {
- data_type => 'int',
- is_auto_increment => 1,
-};
-
-column user_id => {
- data_type => 'varchar',
- size => 40,
-};
-
-column os => {
- data_type => 'varchar',
- size => 40,
- is_nullable => 1,
-};
-
-column cpu_arch => {
- data_type => 'varchar',
- size => 10,
- is_nullable => 1,
-};
-
-column cpu_count => {
- data_type => 'int',
- is_nullable => 1,
-};
-
-column extra_info => {
- data_type => 'text',
- is_nullable => 1,
-};
-
-has_many crash_report => 'CrashTest::Storage::Sql::Schema::Result::CrashReport', 'crash_user_id';
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Result/Module.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-# 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::Storage::Sql::Schema::Result::Module;
-
-use CrashTest::Storage::Sql::Schema::Candy;
-
-primary_column id => {
- data_type => 'int',
- is_auto_increment => 1,
-};
-
-column debug_id => {
- data_type => 'varchar',
- size => 33,
-};
-
-column filename => {
- data_type => 'varchar',
- size => 128,
-};
-
-column version => {
- data_type => 'varchar',
- size => 64,
- is_nullable => 1,
-};
-
-unique_constraint module_id => ['debug_id', 'filename'];
-
-1;
--- a/lib/CrashTest/Storage/Sql/Schema/Result/Product.pm Sun Sep 27 22:45:40 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-# 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::Storage::Sql::Schema::Result::Product;
-
-use CrashTest::Storage::Sql::Schema::Candy;
-
-primary_column id => {
- data_type => 'int',
- is_auto_increment => 1,
-};
-
-column distributor => {
- data_type => 'varchar',
- size => 40,
- is_nullable => 1,
-};
-
-column name => {
- data_type => 'varchar',
- size => 40,
- is_nullable => 1,
-};
-
-column version => {
- data_type => 'varchar',
- size => 40,
- is_nullable => 1,
-};
-
-column release_channel => {
- data_type => 'varchar',
- is_nullable => 16,
-};
-
-has_many crash_report => 'CrashTest::Storage::Sql::Schema::Result::CrashReport', 'product_id';
-
-1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/CrashTest Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+# 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/>.
+
+# ABSTRACT: Web interface for breakpad
+
+use strict;
+use warnings;
+
+use lib 'lib';
+
+# Start command line interface for application
+require Mojolicious::Commands;
+Mojolicious::Commands->start_app('CrashTest');
--- a/templates/index.atom.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/index.atom.ep Wed Nov 04 17:43:00 2015 +0100
@@ -4,22 +4,22 @@
<link type="text/html" rel="alternate" href="<%= url_for()->to_abs =%>"/>
<link type="application/atom+xml" rel="self" href="<%= url_for('current', format => 'atom')->to_abs =%>"/>
<title>Recent Crashs</title>
- <updated><%= @$files[0]->{date}->strftime("%FT%TZ") =%></updated>
+ <updated><%= date_with_tz_from_db_utc(@$crashs[0]->{crash_time}) =%></updated>
-% foreach my $file(@$files) {
+% foreach my $crash(@$crashs) {
<entry>
- <id>tag:crash,2014:uuid::<%= $file->{uuid} =%></id>
- <link type="text/html" rel="alternate" href="<%= url_for('report', uuid => $file->{uuid})->to_abs =%>"/>
- <title><%= $file->{product} =%>: <%= $file->{signature} =%></title>
- <updated><%= $file->{date}->strftime("%FT%TZ") =%></updated>
- <published><%= $file->{date}->strftime("%FT%TZ") =%></published>
+ <id>tag:crash,2014:uuid::<%= $crash->{uuid} =%></id>
+ <link type="text/html" rel="alternate" href="<%= url_for('report', uuid => $crash->{uuid})->to_abs =%>"/>
+ <title><%= $crash->{p_name} =%> <%= $crash->{p_version} =%>: <%= $crash->{uuid} =%></title>
+ <updated><%= date_with_tz_from_db_utc($crash->{crash_time}) =%></updated>
+ <published><%= date_with_tz_from_db_utc($crash->{crash_time}) =%></published>
<author>
<name></name>
</author>
<content type="html">
%= xml_escape_block begin
- %= t h3 => $file->{date}->strftime("%F %T")
- %= t h3 => $file->{product} . " version " . $file->{version}
+ %= t h3 => date_from_db_utc($crash->{crash_time})
+ %= t h3 => $crash->{p_name} . " version " . $crash->{p_version}
% end
</content>
</entry>
--- a/templates/index.html.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/index.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -7,25 +7,26 @@
<th>Version</th>
<th>UUID</th>
% foreach my $extra_col(@$extra_columns) {
- %= t th => $extra_col->{name}
+ %= t th => $extra_col->{name}
% }
<th>Date</th>
</tr>
</thead>
-% foreach my $crash(@$files) {
+% foreach my $crash(@$crashs) {
%= t tr => begin
- %= t td => $crash->{product}
- %= t td => $crash->{version}
+ %= t td => $crash->{p_name}
+ %= t td => ($crash->{p_version} or "")
%= t td => (style => "font-family:monospace;") => begin
%= link_to $crash->{uuid} => url_for('report', uuid => $crash->{uuid})
% end
% foreach my $extra_col(@$extra_columns) {
- %= t td => $crash->{$extra_col->{id}}
+ %= t td => $crash->{$extra_col->{id}}
% }
- %= t td => $crash->{date}->set_time_zone('UTC')->set_time_zone('local')->strftime("%F %T")
+ %= t td => date_from_db_utc($crash->{crash_time})
% end
% }
% end
+
% if($pager->first_page != $pager->last_page) {
%= bootstrap_pagination($pager->current_page, $pager->last_page);
% }
--- a/templates/report/backtrace.html.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/report/backtrace.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -15,4 +15,3 @@
% end
% }
</div>
-
--- a/templates/report/backtrace/frames.html.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/report/backtrace/frames.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -13,9 +13,11 @@
%= $frame->frame_number
% end
%= t td => (class => 'col-md-2') => begin
+ %= module_warnings($frame)
%= $frame->module_name
% end
%= t td => (class => 'col-md-5') => begin
+ %= frame_warnings($frame)
%= $frame->function_name
% end
%= t td => (class => 'col-md-4') => begin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/report/backtrace/warning.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,3 @@
+%= tag a => (href => "#", class => "text-danger", "data-toggle" => "tooltip", "data-original-title" => $tooltip_text) => begin
+ %= tag span => (class => "glyphicon glyphicon-warning-sign") => ""
+% end
--- a/templates/report/client_info.html.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/report/client_info.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -1,3 +1,11 @@
+<tr>
+ %= t td => 'Start Time'
+ %= t td => $client_info->{StartupTime}
+</tr>
+<tr>
+ %= t td => 'Crash Time'
+ %= t td => $client_info->{CrashTime}
+</tr>
<tr>
%= t td => 'Product'
%= t td => $client_info->{ProductName}
@@ -16,9 +24,13 @@
</tr>
<tr>
%= t td => 'Release Channel'
- %= t td => $client_info->{ReleaseChannel}
+ %= t td => $client_info->{ReleaseChannel} || ""
</tr>
<tr>
%= t td => 'UUID'
%= t td => $self->param('uuid');
</tr>
+<tr>
+ %= t td => 'User ID'
+ %= t td => $client_info->{UserID};
+</tr>
--- a/templates/report/crash.html.ep Sun Sep 27 22:45:40 2015 +0200
+++ b/templates/report/crash.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -20,6 +20,7 @@
% end
%= t div => (class => 'tab-pane', id => 'details') => begin
%= t table => (class => 'table table-striped table-hover table-bordered table-condensed') => begin
+ %= include('report/crash_info', crash_info => $processed_data->{crash_info});
%= include('report/client_info', client_info => $processed_data->{client_info});
%= include('report/system_info', system_info => $processed_data->{system_info});
% end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/report/crash_info.html.ep Wed Nov 04 17:43:00 2015 +0100
@@ -0,0 +1,4 @@
+<tr>
+ %= t td => 'Crash Type'
+ %= t td => $crash_info->{type}
+</tr>