]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1178094 - Update crash signatures to match new Socorro signature generation
authorByron Jones <glob@mozilla.com>
Fri, 9 Oct 2015 04:57:36 +0000 (12:57 +0800)
committerByron Jones <glob@mozilla.com>
Fri, 9 Oct 2015 04:57:36 +0000 (12:57 +0800)
scripts/update-crash-signatures.pl [new file with mode: 0755]

diff --git a/scripts/update-crash-signatures.pl b/scripts/update-crash-signatures.pl
new file mode 100755 (executable)
index 0000000..179d0de
--- /dev/null
@@ -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;
+}