lib/CrashTest/Plugin/CrashSignatureExtractor/C_Cpp.pm
author Vincent Tondellier <tonton+hg@team1664.org>
Wed, 30 Dec 2015 19:57:32 +0100
changeset 90 6f091abdf5fb
parent 88 c82f5589db11
child 101 8e0a9f88fb47
permissions -rw-r--r--
Fix function shortening for complex cases, like: std::basic_ostream<A, B>& operator<< <A, B >(std::basic_ostream<A, B>&, T const&)

# 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::CrashSignatureExtractor::C_Cpp;
use Mojo::Base -base;
use Mojo::Util qw/dumper/;
use Text::Balanced qw/extract_bracketed/;

use CrashTest::Model::Thread;

has [ qw/app config/ ];

sub extract_signature {
    my ($self, $thread) = @_;

    my $frames = $thread->frames;

    $frames = _strip_top_frames($frames, $self->config->{TopIrrelevant}, $self->config->{TopFrame});

    #my @rev_frames = reverse @$frames;
    #    $frames = _strip_top_frames(\@rev_frames, $self->config->{BottomIrrelevant}, $self->config->{BottomFrame});

    #my @new_frames = reverse @$frames;
    my @new_frames = @$frames;

    my @short_frames;
    foreach my $frame(@new_frames) {
        my $short = _extract_function_name($frame->function);
        $short =~ s/^$_// foreach (@{$self->config->{RemoveNamespace}});
        push @short_frames, $short;
        # . "@" . $frame->function_offset;
    }

    return \@short_frames;
}

sub extract_signature_title {
    my ($self, $thread) = @_;

    return $self->extract_signature($thread)->[0];
}

sub _extract_function_name {
    my $signature = shift;

    return "" if(!defined($signature) || $signature eq "");

    my $short_signature = "";
    my $text = $signature;
    do {
        # the complex prefix is here to match normal function names (the last '[^<\(]*' part)
        # but also the complex case of unbalanced <> like in:
        # std::basic_ostream<A, B>& operator<< <A, B >(std::basic_ostream<A, B>&, T const&)
        my ($str, $next, $prefix) = extract_bracketed($text, '<(', qr/([^<\(]*operator[<>]{1,2})?[^<\(]*/);
        if($str) {
            $short_signature .= $prefix;
            $text = $next;
        } else {
            $short_signature .= $next;
            $text = undef;
        }
    } while($text);

    return $short_signature;
}

sub _smartmatch {
    my ($text, $pat) = @_;
    return $text =~ $pat if(ref($pat) eq "Regexp");
    return $text eq $pat;
}

sub _strip_top_frames {
    my ($frames, $skip, $stop) = @_;

    my $first_frame = 0;
    my $i = -1;
    TOPFRAME: foreach my $frame(@$frames) {
        my $f = _extract_function_name($frame->function);
        $i += 1;

        # if matching, mark first frame, and stop
        foreach my $m(@{$stop}) {
            if(_smartmatch($f, $m)) {
                $first_frame = $i;
                last TOPFRAME;
            }
        }
        # if matching, mark next frame as first, and continue
        my $skip_matched = 0;
        foreach my $m(@{$skip}) {
            if(_smartmatch($f, $m)) {
                $first_frame = $i + 1;
                $skip_matched = 1;
            }
        }
        # else, stop
        unless($skip_matched) {
            last TOPFRAME;
        }
    }

    my $last = scalar(@$frames) - 1;
    my @new_frames = @$frames[$first_frame .. $last];
    return \@new_frames;
}

1;