File Coverage

blib/lib/Parse/StackTrace/Type/GDB.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Parse::StackTrace::Type::GDB;
2 2     2   18711 use Moose;
  0            
  0            
3             use Parse::StackTrace::Exceptions;
4             use Math::BigInt;
5              
6             extends 'Parse::StackTrace';
7              
8             our $VERSION = '0.08';
9              
10             our $WHITESPACE_ONLY = qr/^\s*$/;
11              
12             use constant HAS_TRACE => qr/
13             ^\#\d+\s+ # #1
14             (?:
15             (?:0x[A-Fa-f0-9]{4,}\s+in\b) # 0xdeadbeef in
16             |
17             (?:[A-Za-z_\*]\S+\s+\() # some_function_name
18             |
19             (?:<signal \s handler \s called>)
20             )
21             /mx;
22             use constant BIN_REGEX => qr/(?:Backtrace|Core) was generated (?:from|by) (?:`|')(.+)/;
23             #1num #2name
24             use constant THREAD_START_REGEX => (
25             qr/^Thread (\d+) \((.*)\):$/,
26             qr/^\[Switching to Thread (.+) \((.+)\)\]$/,
27             );
28              
29             use constant OPEN_PAREN_WITHOUT_CLOSE => qr/\s+\([^\)]*$/;
30              
31             use constant IGNORE_LINES => (
32             'No symbol table info available.',
33             'No locals.',
34             '---Type <return> to continue, or q <return> to quit---',
35             );
36              
37             # If we just have the default thread, return it when asked for Thread 1.
38             sub thread_number {
39             my $self = shift;
40             my ($number) = @_;
41             if (scalar @{ $self->threads } == 1 and !$self->threads->[0]->has_number
42             and $number == 1)
43             {
44             return $self->threads->[0];
45             }
46             return $self->SUPER::thread_number(@_)
47             }
48              
49             # The most common parsing error during testing was that traces would be
50             # malformed with extra newlines in the "args" section.
51             sub _get_next_trace_block {
52             my $self = shift;
53             my $frame_lines = $self->SUPER::_get_next_trace_block(@_);
54            
55             my $frame_text = join(' ', @$frame_lines);
56             # Check if the trace contains an open-paren after a space, but no
57             # close-paren after it.
58             if ($frame_text =~ OPEN_PAREN_WITHOUT_CLOSE) {
59             my ($lines) = @_;
60             my $next_line = $lines->[0];
61            
62             # If the next line is blank...
63             if (defined $next_line and $next_line =~ $WHITESPACE_ONLY) {
64             # Then get rid of it and re-parse the block.
65             shift @$lines;
66             unshift(@$lines, @$frame_lines);
67             return $self->_get_next_trace_block(@_);
68             }
69            
70             # Often people will cut up parts of a trace, and the very
71             # last frame wil have an open-paren with no close paren.
72             # So, if the next line is an end to this frame (or an end to the whole
73             # block of text being parsed), then we just have
74             # a really bad trace that we should try to deal with anyway by
75             # closing the parens on the actual line where the open-paren happens.
76             if (!defined $next_line or $self->_next_line_ends_frame($next_line)) {
77             my @real_frame_lines;
78            
79             while (my $line = shift @$frame_lines) {
80             if ($line =~ OPEN_PAREN_WITHOUT_CLOSE) {
81             $line .= ')';
82             push(@real_frame_lines, $line);
83             last;
84             }
85             push(@real_frame_lines, $line);
86             }
87            
88             # Put the remaining lines back into $lines, so that we don't
89             # think they're part of the trace.
90             unshift(@$lines, @$frame_lines);
91            
92             return \@real_frame_lines;
93             }
94             }
95            
96             return $frame_lines;
97             }
98              
99             # We also want to ignore any lines containing gdb commands.
100             sub _ignore_line {
101             my $class = shift;
102             my $result = $class->SUPER::_ignore_line(@_);
103             return $result if $result;
104             my ($line) = @_;
105             return $line =~ /^\(gdb\) / ? 1 : 0;
106             }
107              
108             sub _line_starts_thread {
109             my ($class, $line) = @_;
110             foreach my $re (THREAD_START_REGEX) {
111             if ($line =~ $re) {
112             my ($number, $desc) = ($1, $2);
113             if ($number =~ /^0x/ or $number !~ /^\d+$/) {
114             # Greater than 0xffffffff needs to be a BigInt for portability.
115             $number =~ s/^0x//;
116             if (length($number) > 8) {
117             $number = Math::BigInt->new("0x$number");
118             }
119             else {
120             $number = hex($number);
121             }
122             }
123             return ($number, $desc);
124             }
125             }
126             return ();
127             }
128              
129             __PACKAGE__->meta->make_immutable;
130              
131             1;
132              
133             __END__
134              
135             =head1 NAME
136              
137             Parse::StackTrace::Type::GDB - A stack trace produced by GDB, the GNU
138             Debugger
139              
140             =head1 DESCRIPTION
141              
142             This is an implementation of L<Parse::StackTrace> for GDB traces.
143              
144             The parser assumes that the text it is parsing contains only one
145             stack trace, so all detected threads and frames are part of a single
146             trace.
147              
148             GDB stack traces come in various levels of quality (some have threads,
149             some don't, some have symbols, some don't, etc.). The parser deals with
150             that just fine, but you should not expect all fields of threads and
151             frames to always be populated.
152              
153             =head1 SEE ALSO
154              
155             =over
156              
157             =item L<Parse::StackTrace::Type::GDB::Thread>
158              
159             =item L<Parse::StackTrace::Type::GDB::Frame>
160              
161             =back