File Coverage

lib/Devel/Trepan/DB/Breakpoint.pm
Criterion Covered Total %
statement 75 186 40.3
branch 18 112 16.0
condition 6 19 31.5
subroutine 15 23 65.2
pod 0 10 0.0
total 114 350 32.5


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2014 Rocky Bernstein <rocky@cpan.org>
3             # largely rewritten from perl5db.
4              
5 13     13   213364 use Class::Struct;
  13         19673  
  13         74  
6 13     13   1643 use strict;
  13         24  
  13         2193  
7              
8             struct DBBreak => {
9             id => '$', # breakpoint/action number
10             enabled => '$', # True if breakpoint or action is enabled
11             type => '$', # 'tbrkpt', 'brkpt' or 'action'
12             condition => '$', # Condition to evaluate or '1' fo unconditional
13             # if type is 'action' this is the action to run
14             hits => '$', # Number of times the breakpoint/action hit
15             negate => '$', # break/step if ... or until .. ?
16             filename => '$',
17             line_num => '$'
18             };
19              
20             package DBBreak;
21             sub inspect($)
22             {
23 0     0   0 my $self = shift;
24 0   0     0 sprintf("id %d, file %s, line %s, type: %s, enabled: %d, negate %s, hits: %s, cond: %s",
25             $self->id,
26             $self->filename, $self->line_num,
27             $self->type,
28             $self->enabled,
29             $self->negate || 0,
30             $self->hits, $self->condition
31             );
32             };
33              
34             sub icon_char($)
35             {
36 0     0   0 my $self = shift;
37 0 0       0 if ('tbrkpt' eq $self->type) {
    0          
    0          
38 0         0 return 'T';
39             } elsif ('brkpt' eq $self->type) {
40 0         0 return 'B';
41             } elsif ('action' eq $self->type) {
42 0         0 return 'A';
43             }
44             }
45              
46             package DB;
47 13     13   87 use vars qw($brkpt $package $lineno $max_bp $max_action);
  13         25  
  13         1004  
48 13     13   77 use strict; use warnings; no warnings 'redefine';
  13     13   24  
  13     13   260  
  13         57  
  13         30  
  13         379  
  13         63  
  13         28  
  13         472  
49 13     13   975 use English qw( -no_match_vars );
  13         6085  
  13         69  
50 13     13   4055 use Scalar::Util;
  13         26  
  13         538  
51              
52 13     13   101 use rlib '../../..';
  13         41  
  13         78  
53 13     13   10015 use Devel::Trepan::DB::LineCache; # For eval_ok
  13         36  
  13         2438  
54              
55             BEGIN {
56 13     13   46 $DB::brkpt = undef; # current breakpoint
57 13         21879 $max_bp = $max_action = 0;
58             }
59              
60             # return info on lines with actions
61             sub line_events {
62 0     0 0 0 my $s = shift;
63 0         0 my $fname = shift;
64 0         0 my(%ret) = ();
65 0 0       0 $fname = $DB::filename unless $fname;
66 0         0 local(*DB::dbline) = "::_<$fname";
67 0         0 for (my $i = 1; $i <= $#DB::dbline; $i++) {
68 0 0       0 $ret{$i} = $DB::dbline[$i] if defined $DB::dbline{$i};
69             }
70 0         0 return %ret;
71             }
72              
73             # Find a subroutine. Return ($filename, $fn_name, $start_line);
74             # If not found, return (undef, undef, undef);
75             sub find_subline($;$) {
76 0     0 0 0 my $fn_name = shift;
77 0         0 $fn_name =~ s/\'/::/;
78 0 0       0 $fn_name = "${DB::package}\:\:" . $fn_name if $fn_name !~ /::/;
79 0 0       0 $fn_name = "main" . $fn_name if substr($fn_name,0,2) eq "::";
80 0         0 my $filename = $DB::filename;
81 0 0       0 if (exists $DB::sub{$fn_name}) {
82 0         0 my($filename, $from, $to) = ($DB::sub{$fn_name} =~ /^(.*):(\d+)-(\d+)$/);
83 0 0       0 if ($from) {
84 0         0 local *DB::dbline = "::_<$filename";
85 0         0 return ($filename, $fn_name, $from);
86             }
87             }
88 0         0 return (undef, undef, undef);
89             }
90              
91             # Return a warning message if breakpoint position is invalid.
92             # undef is returned if the breakpont is valid.
93             sub break_invalid {
94 1     1 0 1030 my ($s, $filename_ref, $fn_or_lineno) = @_;
95 1         3 my $filename = $$filename_ref;
96 1 50       3 $filename = $DB::filename unless defined $filename;
97 1         2 my $change_dbline = $filename ne $DB::filename;
98 1   33     2 $fn_or_lineno ||= $DB::lineno;
99              
100             # If we're not in that file, switch over to it.
101 1 50       3 if ( $change_dbline ) {
102             # Switch debugger's magic structures.
103 0         0 my $filekey = '_<' . $filename;
104 0 0       0 *DB::dbline = $main::{ $filekey } if exists $main::{ $filekey };
105             }
106              
107 1         2 my $lineno = $fn_or_lineno;
108 1 50       4 if ($fn_or_lineno =~ /\D/) {
109 0         0 my $junk;
110 0         0 ($filename, $junk, $lineno) = find_subline($fn_or_lineno) ;
111 0 0       0 unless ($lineno) {
112 0 0       0 *DB::dbline = $main::{ '_<' . $DB::filename } if $change_dbline;
113 0         0 return "Subroutine $fn_or_lineno not found"
114             }
115 0         0 $change_dbline = $filename ne $DB::filename;
116 0 0       0 if ( $change_dbline ) {
117             # Switch debugger's magic structures.
118 0         0 my $filekey = '_<' . $filename;
119 0 0       0 *DB::dbline = $main::{ $filekey } if exists $main::{ $filekey };
120             }
121 0         0 $$filename_ref = $filename;
122 0         0 return undef;
123             } else {
124 1 50 33     6 if (!defined($DB::dbline[$lineno]) || $DB::dbline[$lineno] == 0) {
125 1 50       2 *DB::dbline = $main::{ '_<' . $DB::filename } if $change_dbline;
126 1         2 $$filename_ref = $filename;
127 1         5 return "Line $lineno of $filename not known to be a trace line";
128             }
129             }
130 0         0 return undef;
131             }
132              
133             =head2 set_breakpoint
134              
135             # Set a breakpoint, temporary breakpoint, or action.
136              
137             =cut
138             sub set_break {
139 1     1 0 555 my ($s, $filename, $fn_or_lineno, $cond, $id, $type, $enabled, $force) = @_;
140 1 50       3 $filename = $DB::filename unless defined $filename;
141 1         3 my $change_dbline = $filename ne $DB::filename;
142 1 50       10 $type = 'brkpt' unless defined $type;
143 1 50       4 $enabled = 1 unless defined $enabled;
144 1   33     3 $fn_or_lineno ||= $DB::lineno;
145 1   50     3 $cond ||= '1';
146              
147             # If we're not in that file, switch over to it.
148 1 50       3 if ( $change_dbline ) {
149             # Switch debugger's magic structures.
150 0         0 my $filekey = '_<' . $filename;
151 0 0       0 *DB::dbline = $main::{ $filekey } if exists $main::{ $filekey };
152             }
153              
154 1         1 my $lineno = $fn_or_lineno;
155 1         2 my $function = undef;
156 1 50       4 if ($fn_or_lineno =~ /\D/) {
157 0         0 my $junk;
158 0         0 ($filename, $function, $lineno) = find_subline($fn_or_lineno, $filename) ;
159 0 0       0 unless ($lineno) {
160 0         0 $s->warning("Subroutine $fn_or_lineno not found.\n");
161 0 0       0 *DB::dbline = $main::{ '_<' . $DB::filename } if $change_dbline;
162 0         0 return undef;
163             }
164 0         0 $change_dbline = $filename ne $DB::filename;
165 0 0       0 if ( $change_dbline ) {
166             # Switch debugger's magic structures.
167 0         0 my $filekey = '_<' . $filename;
168 0 0       0 *DB::dbline = $main::{ $filekey } if exists $main::{ $filekey };
169             }
170             } else {
171 1 50 33     5 unless (is_breakable($filename, $lineno) || $force) {
172 0         0 $s->warning("Line $lineno of $filename not known to be a trace line.\n");
173              
174 0 0       0 *DB::dbline = $main::{ '_<' . $DB::filename } if $change_dbline;
175 0         0 return undef;
176             }
177             }
178 1 50       3 unless (defined $id) {
179 1 50       3 if ($type eq 'action') {
180 0         0 $id = ++$max_action;
181             } else {
182 1         2 $id = ++$max_bp;
183             }
184             }
185 1         25 my $brkpt = DBBreak->new(
186             type => $type,
187             condition => $cond,
188             id => $id,
189             hits => 0,
190             enabled => $enabled,
191             filename => $filename,
192             line_num => $lineno
193             );
194              
195 1         79 my $ary_ref;
196 1 50       3 if (defined($function)) {
197 0 0       0 $DB::fn_brkpt{$function} = [] unless exists $DB::fn_brkpt{$fn_or_lineno};
198 0         0 $ary_ref = $DB::fn_brkpt{$function};
199             } else {
200 1 50       4 $DB::dbline{$lineno} = [] unless (exists $DB::dbline{$lineno});
201 1         3 $ary_ref = $DB::dbline{$lineno};
202             }
203 1         3 push @$ary_ref, $brkpt;
204 1 50       2 *DB::dbline = $main::{ '_<' . $DB::filename } if $change_dbline;
205 1         3 return $brkpt;
206             }
207              
208             =head2 is_breakable
209              
210             Return 1 if C<$filename> has code associated with C<$lineno>; 0 otherwise.
211              
212             =cut
213             # Return 1 if $lineno is
214             sub is_breakable {
215 1     1 0 3 my($filename, $lineno) = @_;
216 1   33     9 return defined($DB::dbline[$lineno]) && ($DB::dbline[$lineno] != 0)
217             }
218              
219             # Set a temporary breakpoint
220             sub set_tbreak {
221 0     0 0 0 my ($s, $filename, $lineno, $cond, $id) = @_;
222 0         0 return set_break($s, $filename, $lineno, $cond, $id, 'tbrkpt');
223             }
224              
225             sub delete_bp($$) {
226 1     1 0 4 my ($s, $bp) = @_;
227 1         22 my $i = $bp->line_num;
228 1         26 my $file_key = '_<' . $bp->filename;
229 1 50       13 return unless exists $main::{ $file_key };
230 1         3 local *dbline = $main::{ $file_key };
231 1 50       6 if (defined $DB::dbline{$i}) {
232 0           my $brkpts = $DB::dbline{$i};
233 0           my $count = 0;
234 0           my $break_count = scalar @$brkpts;
235 0           for (my $j=0; $j <= $break_count; $j++) {
236 0           $brkpt = $brkpts->[$j];
237 0 0         next unless defined $brkpt;
238 0 0         if ($brkpt eq $bp) {
239 0           undef $brkpts->[$j];
240 0           last;
241             }
242 0           $count++;
243             }
244 0 0         delete $DB::dbline{$i} if $count == 0;
245             }
246             }
247              
248             sub clr_breaks {
249 0     0 0   my $s = shift;
250 0           my $i;
251 0 0         if (@_) {
252 0           while (@_) {
253 0           $i = shift;
254 0 0         $i = _find_subline($i) if ($i =~ /\D/);
255 0 0         $s->output("Subroutine not found.\n") unless $i;
256 0 0         if (defined $DB::dbline{$i}) {
257 0           my $brkpts = $DB::dbline{$i};
258 0           my $j = 0;
259 0           for my $brkpt (@$brkpts) {
260 0 0         if ($brkpt->action ne 'brkpt') {
261 0           $j++;
262 0           next;
263             }
264 0           undef $brkpts->[$j];
265             }
266 0 0         delete $DB::dbline{$i} if $j == 0;
267             }
268             }
269             } else {
270 0           for ($i = 1; $i <= $#DB::dbline ; $i++) {
271 0 0         if (defined $DB::dbline{$i}) {
272 0           clr_breaks($s, $i);
273             }
274             }
275             }
276             }
277              
278             # Set a an action
279             sub set_action {
280 0     0 0   my ($s, $lineno, $filename, $action, $id) = @_;
281 0           return set_break($s, $filename, $lineno, $action, $id, 'action');
282             }
283              
284             # FIXME: combine with clear_breaks
285             sub clr_actions {
286 0     0 0   my $s = shift;
287 0           my $i;
288 0 0         if (@_) {
289 0           while (@_) {
290 0           $i = shift;
291 0 0         $i = _find_subline($i) if ($i =~ /\D/);
292 0 0         $s->output("Subroutine not found.\n") unless $i;
293 0 0         if (defined $DB::dbline{$i}) {
294 0           my $brkpts = $DB::dbline{$i};
295 0           my $j = 0;
296 0           for my $brkpt (@$brkpts) {
297 0 0         if ($brkpt->action ne 'action') {
298 0           $j++;
299 0           next;
300             }
301 0           undef $brkpts->[$j];
302             }
303 0 0         delete $DB::dbline{$i} if $j == 0;
304             }
305             }
306             } else {
307 0           for ($i = 1; $i <= $#DB::dbline ; $i++) {
308 0 0         if (defined $DB::dbline{$i}) {
309 0           clr_breaks($s, $i);
310             }
311             }
312             }
313             }
314              
315             # Demo it.
316             unless (caller) {
317             my $brkpt = DBBreak->new(
318             filename => __FILE__, line_num => __LINE__,
319             type=>'action', condition=>'1', id=>1, hits => 0, enbled => 1,
320             negate => 0
321             );
322             print $brkpt->inspect, "\n";
323             # This line is a comment for below.
324             $DB::filename = __FILE__;
325             my $filename = __FILE__;
326             my $msg = break_invalid(undef, \$filename, __LINE__ - 3);
327             print $msg, "\n";
328             }
329              
330             1;