diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/update-crash-signatures.pl | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/scripts/update-crash-signatures.pl b/scripts/update-crash-signatures.pl new file mode 100755 index 000000000..179d0decb --- /dev/null +++ b/scripts/update-crash-signatures.pl @@ -0,0 +1,199 @@ +#!/usr/bin/perl + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use strict; +use warnings; +$| = 1; + +use FindBin qw($RealBin); +use lib "$RealBin/..", "$RealBin/../lib"; + +use constant BATCH_SIZE => 100; + +use Bugzilla; +use Bugzilla::Bug; +use Bugzilla::Constants; +use Bugzilla::Util qw( trim ); +use List::MoreUtils qw( any ); +use Text::Balanced qw( extract_bracketed extract_multiple ); + +Bugzilla->usage_mode(USAGE_MODE_CMDLINE); + +my $user = Bugzilla::User->check({ name => 'automation@bmo.tld' }); +$user->{groups} = [ Bugzilla::Group->get_all ]; +$user->{bless_groups} = [ Bugzilla::Group->get_all ]; +Bugzilla->set_user($user); + +my $dbh = Bugzilla->dbh; + +# find the bugs + +my $bugs = $dbh->selectall_arrayref( + "SELECT bug_id,cf_crash_signature FROM bugs WHERE resolution = '' AND cf_crash_signature != ''", + { Slice => {} } +); +my $count = scalar @$bugs; + +# update + +die "No bugs found\n" unless $count; +print "Found $count open bug(s) with crash signatures\nPress <Ctrl-C> to stop or <Enter> to continue..\n"; +getc(); + +my $updated = 0; +foreach my $rh_bug (@$bugs) { + my $bug_id = $rh_bug->{bug_id}; + my $signature = $rh_bug->{cf_crash_signature}; + + # check for updated signature + my $collapsed = collapse($signature); + next if is_same($signature, $collapsed); + + # ignore signatures malformed in a way that would result in updating on each pass + next if $collapsed ne collapse($collapsed); + + # update the bug, preventing bugmail + print "$bug_id\n"; + $dbh->bz_start_transaction; + my $bug = Bugzilla::Bug->check($bug_id); + $bug->set_all({ cf_crash_signature => $collapsed }); + $bug->update(); + $dbh->do("UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?", undef, $bug_id); + $dbh->bz_commit_transaction; + + # object caching causes us to consume a lot of memory + # process in batches + last if ++$updated == BATCH_SIZE; +} +print "Updated $updated bugs(s)\n"; + +sub is_same { + my ($old, $new) = @_; + $old =~ s/[\015\012]+/ /g; + $new =~ s/[\015\012]+/ /g; + return trim($old) eq trim($new); +} + +sub collapse { + my ($crash_signature) = @_; + + # ignore completely invalid signatures + return $crash_signature unless $crash_signature =~ /\[/ && $crash_signature =~ /\]/; + + # split + my @signatures = + grep { /\S/ } + extract_multiple($crash_signature, [ sub { extract_bracketed($_[0], '[]') } ]); + my @unbracketed = map { unbracketed($_) } @signatures; + + foreach my $signature (@signatures) { + # ignore invalid signatures + next unless $signature =~ /^\s*\[/; + next if unbracketed($signature) =~ /\.\.\.$/; + + # collpase + my $collapsed = collapse_crash_sig({ + signature => $signature, + open => '<', + replacement_open => '<', + close => '>', + replacement_close => 'T>', + exceptions => [], + }); + $collapsed = collapse_crash_sig({ + signature => $collapsed, + open => '(', + replacement_open => '', + close => ')', + replacement_close => '', + exceptions => ['anonymous namespace', 'operator'], + }); + $collapsed =~ s/\s+/ /g; + + # ignore sigs that collapse down to nothing + next if $collapsed eq '[@ ]'; + + # don't create duplicates + my $unbracketed = unbracketed($collapsed); + next if any { $unbracketed eq $_ } @unbracketed; + + push @signatures, $collapsed; + push @unbracketed, $unbracketed; + } + + return join("\015\012", map { trim($_) } @signatures); +} + +sub unbracketed { + my ($signature) = @_; + $signature =~ s/(^\s*\[\s*|\s*\]\s*$)//g; + return $signature; +} + +# collapsing code lifted from socorro: +# https://github.com/mozilla/socorro/blob/master/socorro/processor/signature_utilities.py#L110 + +my ($target_counter, $exception_mode, @collapsed); + +sub append_if_not_in_collapse_mode { + my ($character) = @_; + if (!$target_counter) { + push @collapsed, $character; + } +} + +sub is_exception { + my ($exceptions, $remaining_original_line, $line_up_to_current_position) = @_; + foreach my $exception (@$exceptions) { + if (substr($remaining_original_line, 0, length($exception)) eq $exception) { + return 1; + } + if (substr($line_up_to_current_position, -length($exception)) eq $exception) { + return 1; + } + } + return 0; +} + +sub collapse_crash_sig { + my ($params) = @_; + + $target_counter = 0; + @collapsed = (); + $exception_mode = 0; + my $signature = $params->{signature}; + + for (my $i = 0; $i < length($signature); $i++) { + my $character = substr($signature, $i, 1); + if ($character eq $params->{open}) { + if (is_exception($params->{exceptions}, substr($signature, $i + 1), substr($signature, 0, $i))) { + $exception_mode = 1; + append_if_not_in_collapse_mode($character); + next; + } + append_if_not_in_collapse_mode($params->{replacement_open}); + $target_counter++; + } + elsif ($character eq $params->{close}) { + if ($exception_mode) { + append_if_not_in_collapse_mode($character); + $exception_mode = 0; + } + else { + $target_counter--; + append_if_not_in_collapse_mode($params->{replacement_close}); + } + } + else { + append_if_not_in_collapse_mode($character); + } + } + + return join '', @collapsed; +} |