File Coverage

blib/lib/Devel/Chitin.pm
Criterion Covered Total %
statement 125 425 29.4
branch 14 138 10.1
condition 2 76 2.6
subroutine 34 92 36.9
pod 36 51 70.5
total 211 782 26.9


line stmt bran cond sub pod time code
1 34     34   238964 use 5.008009;
  34         330  
2 34     34   186 use warnings;
  34         58  
  34         869  
3 34     34   158 use strict;
  34         61  
  34         1791  
4              
5             package Devel::Chitin;
6              
7             our $VERSION = '0.15';
8              
9 34     34   243 use Scalar::Util;
  34         71  
  34         2581  
10 34     34   15835 use IO::File;
  34         291400  
  34         4277  
11 34     34   305 use B;
  34         72  
  34         1517  
12              
13 34     34   14905 use Devel::Chitin::Actionable; # Breakpoints and Actions
  34         96  
  34         1041  
14 34     34   12372 use Devel::Chitin::Eval;
  34         120  
  34         1202  
15 34     34   13848 use Devel::Chitin::Stack;
  34         96  
  34         1172  
16 34     34   13410 use Devel::Chitin::Location;
  34         92  
  34         10667  
17 34     34   19430 use Devel::Chitin::SubroutineLocation;
  34         95  
  34         1167  
18 34     34   13490 use Devel::Chitin::Exception;
  34         81  
  34         889  
19 34     34   15423 use Devel::Chitin::OpTree;
  34         131  
  34         1483  
20              
21 34     34   224 use base 'Exporter';
  34         63  
  34         21278  
22             our @EXPORT_OK = qw( $VERSION );
23              
24             # lexicals shared between the interface package and the DB package
25             my(%attached_clients,
26             @attached_clients,
27             %trace_clients,
28             $is_initialized,
29             @pending_eval,
30             $current_location,
31             $previous_location,
32             @new_watch_exprs,
33             @watch_exprs,
34             );
35             sub attach {
36 4     4 1 852 my $self = shift;
37              
38 4 100       23 unless ($attached_clients{$self}) {
39 3         12 $attached_clients{$self} = $self;
40 3         11 push @attached_clients, $self;
41              
42 3 50       13 if ($is_initialized) {
43 0         0 $self->init();
44             }
45             }
46 4         22 return $self;
47             }
48              
49             sub _turn_off_trace_if_not_needed {
50 6   33 6   41 $DB::trace = %trace_clients || @watch_exprs;
51             }
52              
53             sub detach {
54 6     6 1 21 my $self = shift;
55 6         22 my $deleted = delete $attached_clients{$self};
56 6         16 delete $trace_clients{$self};
57 6         23 _turn_off_trace_if_not_needed();
58 6 100       19 if ($deleted) {
59 3         14 for (my $i = 0; $i < @attached_clients; $i++) {
60 5 100       33 my $same = ref($self)
61             ? Scalar::Util::refaddr($self) == Scalar::Util::refaddr($attached_clients[$i])
62             : $self eq $attached_clients[$i];
63 5 100       53 if ($same) {
64 3         17 splice(@attached_clients, $i, 1);
65             }
66             }
67             }
68 6         41 return $deleted;
69             }
70              
71              
72             sub _clients {
73 14     14   107 return @attached_clients;
74             }
75              
76             ## Methods callable from client code
77              
78             sub step {
79 0     0 1 0 $DB::single=1;
80             }
81              
82             sub stepover {
83 0     0 1 0 local $DB::in_debugger = 1;
84 0         0 $DB::single=1;
85 0         0 $DB::step_over_depth = $DB::stack_depth;
86 0         0 return 1;
87             }
88              
89             sub stepout {
90 0     0 1 0 $DB::single=0;
91 0         0 $DB::step_over_depth = $DB::stack_depth - 1;
92 0         0 return 1;
93             }
94              
95             # Should support running to a subname, or file+line
96             sub continue {
97 0     0 1 0 $DB::single=0;
98 0         0 return 1;
99             }
100              
101             sub trace {
102 0     0 1 0 local $DB::in_debugger = 1;
103 0         0 my $class = shift;
104 0         0 my $rv;
105 0 0       0 if (@_) {
106 0         0 my $new_val = shift;
107 0 0       0 if ($new_val) {
108             # turning trace on
109 0         0 $trace_clients{$class} = $class;
110 0         0 $DB::trace = 1;
111 0         0 $rv = 1;
112             } else {
113             # turning it off
114 0         0 delete $trace_clients{$class};
115 0         0 _turn_off_trace_if_not_needed();
116 0         0 $rv = 0;
117             }
118              
119             } else {
120             # Checking value
121 0         0 $rv = exists $trace_clients{$class};
122             }
123 0         0 return $rv;
124             }
125              
126              
127              
128             sub eval {
129 0     0 1 0 my($class, $eval_string, $wantarray, $cb) = @_;
130 0         0 push @pending_eval, [ $eval_string, $wantarray, $cb ];
131             }
132              
133              
134             sub eval_at {
135 0     0 1 0 my($class, $eval_string, $level) = @_;
136              
137 34     34   251 { no warnings 'numeric';
  34         66  
  34         14332  
  0         0  
138 0 0       0 $level = 0 if ($level < 1);
139             }
140              
141             }
142              
143             sub stack {
144 0     0 1 0 return Devel::Chitin::Stack->new();
145             }
146              
147             sub current_location {
148 0     0 1 0 return $current_location;
149             }
150              
151             sub disable_debugger {
152             # Setting $^P disables single stepping and subrouting entry
153             # but if the program sets $DB::single explicitly, it'll still enter DB()
154 0     0 1 0 $^P = 0; # Stops single-stepping
155 0         0 $DB::debugger_disabled = 1;
156             }
157              
158             sub is_loaded {
159 0     0 1 0 my($self, $filename) = @_;
160             #no strict 'refs';
161 0         0 return $main::{'_<' . $filename};
162             }
163              
164             sub loaded_files {
165 0     0 1 0 my @files = grep /^_
166 0         0 return map { substr($_,2) } @files; # remove the <_
  0         0  
167             }
168              
169             sub add_watchexpr {
170 0     0 0 0 my($class, $expr) = @_;
171 0         0 $DB::trace = 1;
172 0         0 push @new_watch_exprs, { expr => $expr, client => $class, value => undef };
173             }
174              
175             sub remove_watchexpr {
176 0     0 0 0 my($class, $expr) = @_;
177 0         0 my $deleted;
178              
179             SEARCH:
180 0         0 foreach my $store ( \@watch_exprs, \@new_watch_exprs) {
181 0         0 for (my $i = 0; $i < @$store; $i++) {
182 0 0 0     0 if ($store->[$i]->{client} eq $class
183             and
184             $store->[$i]->{expr} eq $expr
185             ) {
186 0         0 $deleted = splice(@$store, $i, 1);
187 0         0 last SEARCH;
188             }
189             }
190             }
191              
192 0         0 _turn_off_trace_if_not_needed();
193              
194 0         0 return $deleted;
195             }
196              
197             sub is_breakable {
198 0     0 1 0 my($class, $filename, $line) = @_;
199              
200 34     34   282 use vars qw(@dbline);
  34         66  
  34         33993  
201 0         0 local(*dbline) = $main::{'_<' . $filename};
202 0         0 return $dbline[$line] + 0; # FIXME change to == 0
203             }
204              
205             sub add_break {
206 0     0 0 0 my $self = shift;
207 0         0 Devel::Chitin::Breakpoint->new(@_);
208             }
209              
210             sub get_breaks {
211 0     0 0 0 my $self = shift;
212 0         0 my %params = @_;
213 0 0       0 if (defined $params{file}) {
214 0         0 return Devel::Chitin::Breakpoint->get(@_);
215             } else {
216 0         0 return map { Devel::Chitin::Breakpoint->get(@_, file => $_) }
  0         0  
217             $self->loaded_files;
218             }
219             }
220              
221             sub remove_break {
222 0     0 0 0 my $self = shift;
223 0 0       0 if (ref $_[0]) {
224             # given a breakpoint object
225 0         0 shift->delete();
226             } else {
227             # given breakpoint params
228 0         0 Devel::Chitin::Breakpoint->delete(@_);
229             }
230             }
231              
232             sub add_action {
233 0     0 0 0 my $self = shift;
234 0         0 Devel::Chitin::Action->new(@_);
235             }
236              
237             sub remove_action {
238 0     0 0 0 my $self = shift;
239 0 0       0 if (ref $_[0]) {
240             # given an action object
241 0         0 shift->delete();
242             } else {
243             # given breakpoint params
244 0         0 Devel::Chitin::Action->delete(@_);
245             }
246             }
247              
248             sub get_actions {
249 0     0 0 0 my $self = shift;
250 0         0 my %params = @_;
251 0 0       0 if (defined $params{file}) {
252 0         0 Devel::Chitin::Action->get(@_);
253             } else {
254 0         0 return map { Devel::Chitin::Action->get(@_, file => $_) }
  0         0  
255             $self->loaded_files;
256             }
257             }
258              
259             sub get_var_at_level {
260 0     0 1 0 my($class, $varname, $level) = @_;
261              
262 0         0 require Devel::Chitin::GetVarAtLevel;
263 0         0 return Devel::Chitin::GetVarAtLevel::get_var_at_level($varname, $level);
264             }
265              
266              
267             sub subroutine_location {
268 0     0 1 0 my $class = shift;
269 0         0 my $subname = shift;
270 0         0 return Devel::Chitin::SubroutineLocation->new_from_db_sub($subname);
271             }
272              
273             # NOTE: This postpones until a named file is loaded.
274             # Have another interface for postponing until a module is loaded
275             sub postpone {
276 0     0 1 0 my($class, $filename, $sub) = @_;
277              
278 0 0       0 if ($class->is_loaded($filename)) {
279             # already loaded, run immediately
280 0         0 $sub->($filename);
281             } else {
282 0   0     0 $DB::postpone_until_loaded{$filename} ||= [];
283 0         0 push @{ $DB::postpone_until_loaded{$filename} }, $sub;
  0         0  
284             }
285             }
286              
287             sub user_requested_exit {
288 0     0 1 0 $DB::user_requested_exit = 1;
289             }
290              
291             sub file_source {
292 0     0 1 0 my($class, $file) = @_;
293              
294 0         0 my $glob = $main::{'_<' . $file};
295 0 0       0 return unless $glob;
296 0         0 return *{$glob}{ARRAY};
  0         0  
297             }
298              
299             my %optrees;
300             our $current_sub;
301             sub _get_optree_for_current_sub {
302 0     0   0 my $loc = current_location;
303              
304 0 0       0 my $optree_cache_key = ref($current_sub) ? "$current_sub" : $loc->subroutine;
305 0 0 0     0 my $optree = $optrees{$optree_cache_key} ||= Devel::Chitin::OpTree->build_from_location(ref($current_sub) ? $current_sub : $loc);
306             }
307              
308             # Some OPs don't deparse to anything useful on their own
309             my %fragment_transforms = (
310             enterloop => sub { shift->sibling->children->[0]->children->[0] }, # deparse the conditional
311             leaveloop => sub { shift->children->[0]->sibling->children->[0]->children->[0] }, # deparse the conditional
312             pushmark => sub {
313             # deparse either the list or entersub
314             my $parent = shift->parent;
315             my $grandparent = $parent->parent;
316             $grandparent->op->name eq 'entersub'
317             ? $grandparent
318             : $parent;
319             },
320             padrange => sub {
321             # deparse either the list or entersub
322             my $parent = shift->parent;
323             my $grandparent = $parent->parent;
324             $grandparent->op->name eq 'entersub'
325             ? $grandparent
326             : $parent;
327             },
328             );
329              
330             sub next_statement {
331 0     0 1 0 my $class = shift;
332              
333 0         0 my $optree = _get_optree_for_current_sub();
334 0         0 my $loc = $class->current_location();
335 0         0 $loc = $class->_fixup_location_inside_eval($loc);
336              
337 0         0 my $callsite = $loc->callsite;
338 0         0 my($last_cop, $current_op);
339             BREAKOUT:
340 0         0 for(1) {
341             $optree->walk_inorder(sub {
342 0     0   0 my $op = shift;
343 0 0       0 $last_cop = $op if ($op->isa('Devel::Chitin::OpTree::COP'));
344 0 0       0 if (${$op->op} == $callsite) {
  0         0  
345 0         0 $current_op = $op;
346 34     34   289 no warnings 'exiting';
  34         85  
  34         29855  
347 0         0 last BREAKOUT;
348             }
349 0         0 });
350             }
351              
352 0 0       0 my $op_to_deparse = $last_cop ? $last_cop->sibling : $current_op;
353              
354 0 0 0     0 if (my $xform = $fragment_transforms{$op_to_deparse->op->name}) {
    0 0        
    0 0        
      0        
      0        
355 0         0 local $@;
356 0   0     0 $op_to_deparse = eval { $xform->($op_to_deparse) } || $op_to_deparse;
357              
358             } elsif ($op_to_deparse->is_null
359             and $op_to_deparse->children
360             and $op_to_deparse->children->[0]->is_if_statement
361             ) {
362 0         0 $op_to_deparse = $op_to_deparse->children->[0]->children->[0]; # deparse the if-condition, not the whole block
363              
364             # !!! special deparsing for landing on a block-map/grep...
365             # return just the list we're mapping/grepping over
366             } elsif ($op_to_deparse->op->name eq 'mapwhile' or $op_to_deparse->op->name eq 'grepwhile'
367             and ( $op_to_deparse->first->children->[1]->first->is_scopelike
368             or
369             ( $op_to_deparse->first->children->[1]->first->is_null
370             and
371             $op_to_deparse->first->children->[1]->first->first->is_scopelike
372             )
373             )
374             ) {
375             # This list contains a pushmark, the block, then all the args
376 0         0 my $map_args = $op_to_deparse->first->children;
377 0         0 my @maplist = @$map_args[2 .. $#$map_args];
378 0         0 return join(', ', map { $_->deparse } @maplist);
  0         0  
379             }
380              
381 0 0       0 if ($op_to_deparse) {
382 0         0 local $@;
383 0         0 my $deparsed = eval { $op_to_deparse->deparse };
  0         0  
384 0 0       0 if ($@) {
385 0         0 warn "failed to deparse: $@";
386 0         0 $optree->print_as_tree($callsite);
387             }
388 0         0 return $deparsed;
389             } else {
390 0         0 Carp::carp("Cannot find current opcode at $callsite in ".$loc->subroutine);
391 0         0 return '';
392             }
393             }
394              
395             sub next_fragment {
396 0     0 1 0 my($class, $parents) = @_;
397              
398 0         0 my $optree = _get_optree_for_current_sub();
399 0         0 my $loc = $class->current_location();
400 0         0 $loc = $class->_fixup_location_inside_eval($loc);
401              
402 0         0 my $callsite = $loc->callsite;
403 0         0 my $current_op = Devel::Chitin::OpTree->_obj_for_op(\$callsite);
404              
405 0 0       0 if (defined $parents) {
    0          
406 0   0     0 while($current_op && $parents--) {
407 0         0 my $parent = $current_op->parent;
408 0 0       0 $current_op = $parent if $parent;
409             }
410             } elsif (my $xform = $fragment_transforms{$current_op->op->name}) {
411 0         0 local $@;
412 0         0 $current_op = eval { $xform->($current_op) };
  0         0  
413             }
414              
415 0 0       0 if ($current_op) {
416 0         0 local $@;
417 0         0 my $deparsed = eval { $current_op->deparse };
  0         0  
418 0 0       0 if ($@) {
419 0         0 warn "failed to deparse: $@\ncurrent op name ",$current_op->op->name,"\n";
420 0         0 $optree->print_as_tree($callsite);
421             }
422 0         0 return $deparsed;
423             } else {
424 0         0 Carp::carp("Cannot find current opcode at $callsite in ".$loc->subroutine);
425 0         0 return '';
426             }
427             }
428              
429             sub _fixup_location_inside_eval {
430 0     0   0 my($class, $loc) = @_;
431              
432 0 0       0 if ($loc->subroutine eq '(eval)') {
433 0         0 my $stack = $class->stack->iterator;
434 0         0 my $frame;
435 0         0 for($frame = $stack->(); $frame; $frame = $stack->()) {
436 0 0       0 last if $frame->subroutine ne '(eval)';
437             }
438 0 0       0 if ($frame) {
439             return Devel::Chitin::Location->new(
440 0         0 (map { $_ => $frame->$_ } qw(package filename line subroutine)),
  0         0  
441             callsite => $loc->callsite
442             );
443             }
444             }
445 0         0 return $loc;
446             }
447              
448             ## Methods called by the DB core - override in clients
449              
450       0 1   sub init {}
451       0 1   sub poll {}
452 0     0 1 0 sub idle { 1;}
453       0 0   sub cleanup {}
454       0 1   sub notify_stopped {}
455       0 1   sub notify_resumed {}
456       0 1   sub notify_trace {}
457       0 1   sub notify_trace_resumed {}
458       0 1   sub notify_fork_parent {}
459       0 1   sub notify_fork_child {}
460       0 1   sub notify_program_terminated {}
461       0 1   sub notify_program_exit {}
462       0 1   sub notify_uncaught_exception {}
463       0 1   sub notify_watch_expr {}
464              
465             sub _do_each_client {
466 66     66   787 my($method, @args) = @_;
467              
468 66         886 $_->$method(@args) foreach @attached_clients;
469             }
470              
471             package DB;
472              
473 34     34   280 use vars qw( %dbline @dbline );
  34         84  
  34         6600  
474              
475             our($stack_depth,
476             $single,
477             $signal,
478             $trace,
479             $debugger_disabled,
480             $no_stopping,
481             $step_over_depth,
482             $ready,
483             @saved,
484             $usercontext,
485             $in_debugger,
486             $finished,
487             $user_requested_exit,
488             @AUTOLOAD_names,
489             $sub,
490             $uncaught_exception,
491             %postpone_until_loaded,
492             );
493              
494             BEGIN {
495 34     34   120 $stack_depth = 0;
496 34         91 $single = 0;
497 34         84 $trace = 0;
498 34         108 $debugger_disabled = 0;
499 34         51 $no_stopping = 0;
500 34         65 $step_over_depth = undef;
501 34         70 $ready = 0;
502 34         71 @saved = ();
503 34         66 $usercontext = '';
504 34         71 $in_debugger = 0;
505              
506             # Controlling program end of life
507 34         68 $finished = 0;
508 34         87 $user_requested_exit = 0;
509              
510             # Remember AUTOLOAD sub names
511 34         7580 @AUTOLOAD_names = ();
512             }
513              
514             sub save {
515             # Save eval failure, command failure, extended OS error, output field
516             # separator, input record separator, output record separator and
517             # the warning setting.
518 0     0 0 0 @saved = ( $@, $!, $^E, $,, $/, $\, $^W );
519              
520 0         0 $, = ""; # output field separator is null string
521 0         0 $/ = "\n"; # input record separator is newline
522 0         0 $\ = ""; # output record separator is null string
523 0         0 $^W = 0; # warnings are off
524             }
525              
526             sub restore {
527 0     0 0 0 ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
528             }
529              
530             sub _evaluate_watch_exprs {
531             EXPR:
532 0     0   0 foreach my $details ( @watch_exprs ) {
533 0         0 my($current_value) = _eval_in_program_context($details->{expr}, 1);
534 0         0 my $old_value = $details->{value};
535              
536 0 0       0 if (@$current_value != @$old_value) {
537 0         0 $details->{client}->notify_watch_expr($previous_location, $details->{expr}, $old_value, $current_value);
538 0         0 $details->{value} = $current_value;
539 0         0 next EXPR;
540             }
541              
542 0         0 for (my $i = 0; $i < @$current_value; $i++) {
543 34     34   234 no warnings 'uninitialized';
  34         88  
  34         18080  
544 0 0 0     0 if ((defined($current_value->[$i]) xor defined($old_value->[$i]))
      0        
545             or
546             $current_value->[$i] ne $old_value->[$i]
547             ) {
548 0         0 $details->{client}->notify_watch_expr($previous_location, $details->{expr}, $old_value, $current_value);
549 0         0 $details->{value} = $current_value;
550 0         0 next EXPR;
551             }
552             }
553             }
554             }
555              
556             sub is_breakpoint {
557 0     0 0 0 my($package, $filename, $line) = @_;
558              
559 0 0 0     0 if ($single and defined($step_over_depth) and $step_over_depth < $stack_depth) {
      0        
560             # This is from a step-over
561 0         0 $single = 0;
562 0         0 return 0;
563             }
564              
565 0 0 0     0 if ($single || $signal) {
566 0         0 $single = $signal = 0;
567 0         0 return 1;
568             }
569              
570 0         0 local(*dbline)= $main::{'_<' . $filename};
571              
572 0         0 my $should_break = 0;
573 0         0 my $breakpoint_key = Devel::Chitin::Breakpoint->type;
574 0 0 0     0 if ($dbline{$line} && $dbline{$line}->{$breakpoint_key}) {
575 0         0 my @delete;
576 0         0 foreach my $condition ( @{ $dbline{$line}->{$breakpoint_key} }) {
  0         0  
577 0 0       0 next if $condition->inactive;
578 0         0 my $code = $condition->code;
579 0 0       0 if ($code eq '1') {
580 0         0 $should_break = 1;
581             } else {
582 0         0 ($should_break) = _eval_in_program_context($condition->code, 0);
583             }
584 0 0       0 push @delete, $condition if $condition->once;
585             }
586 0         0 $_->delete for @delete;
587             }
588              
589 0 0       0 if ($should_break) {
590 0         0 $single = $signal = 0;
591             }
592 0         0 return $should_break;
593             }
594              
595              
596             sub _parent_stack_location {
597 32     32   3143 my($package, $filename, $line) = caller(1);
598 32         1302 my(undef, undef, undef, $subname) = caller(2);
599 32         978 my $callsite = Devel::Chitin::Location::get_callsite(2);
600 32   50     771 $subname ||= 'MAIN';
601 32         568 return ($package, $filename, $line, $subname, $callsite);
602             }
603              
604             BEGIN {
605             # Code to get control when the debugged process forks
606             *CORE::GLOBAL::fork = sub {
607 32     32   32905 my $pid = CORE::fork();
608 32 50       2972 return $pid unless $ready;
609              
610 32         2222 my($package, $filename, $line, $subname, $callsite) = _parent_stack_location();
611 32         2296 my $location = Devel::Chitin::Location->new(
612             'package' => $package,
613             line => $line,
614             filename => $filename,
615             subroutine => $subname,
616             callsite => $callsite,
617             );
618              
619 32 50       813 my $notify = $pid ? 'notify_fork_parent' : 'notify_fork_child';
620 32         757 Devel::Chitin::_do_each_client($notify, $location, $pid);
621 32         1655 return $pid;
622 34     34   26297 };
623             };
624              
625             # Reporting uncaught exceptions back to the debugger clients
626             # inside the handler, note the value for $^S:
627             # undef - died while parsing something
628             # 1 - died while executing an eval
629             # 0 - Died not inside an eval
630             # We could re-throw the die if $^S is 1
631             $SIG{__DIE__} = sub {
632             if (defined($^S) && $^S == 0) {
633             $in_debugger = 1;
634             my $exception = $_[0];
635             # It's interesting to note that if we pass an arg to caller() to
636             # find out the offending subroutine name, then the line reported
637             # changes. Instead of reporting the line the exception occurred
638             # (which it correctly does with no args), it returns the line which
639             # called the function which threw the exception.
640             # We'll work around it by calling it twice
641             my($package, $filename, $line, $subname, $callsite) = _parent_stack_location();
642              
643             $uncaught_exception = Devel::Chitin::Exception->new(
644             'package' => $package,
645             line => $line,
646             filename => $filename,
647             exception => $exception,
648             subroutine => $subname,
649             callsite => $callsite,
650             );
651             # After we fall off the end, the interpreter will try and exit,
652             # triggering the END block that calls DB::fake::at_exit()
653             }
654             };
655              
656              
657             sub _execute_actions {
658 0     0   0 my($filename, $line) = @_;
659 0         0 local(*dbline) = $main::{'_<' . $filename};
660 0 0 0     0 if ($dbline{$line} && $dbline{$line}->{action}) {
661 0         0 my @delete;
662 0         0 foreach my $action ( @{ $dbline{$line}->{action}} ) {
  0         0  
663 0 0       0 next if $action->inactive;
664 0         0 _eval_in_program_context($action->code, undef);
665 0 0       0 push @delete, $action if $action->once;
666             }
667 0         0 $_->delete for @delete;
668             }
669             }
670              
671             sub fill_in_values_for_new_watch_exprs {
672 0     0 0 0 foreach my $detail ( @new_watch_exprs ) {
673 0         0 my($value) = _eval_in_program_context($detail->{expr}, 1);
674 0         0 $detail->{value} = $value;
675 0         0 push @watch_exprs, $detail;
676             }
677 0         0 @new_watch_exprs = ();
678             }
679              
680             sub DB {
681 0 0 0 0 0 0 return if (!$ready or $debugger_disabled or $in_debugger);
      0        
682              
683 0         0 local($in_debugger) = 1;
684              
685 0         0 my($package, $filename, $line) = caller;
686 0         0 my(undef, undef, undef, $subroutine) = caller(1);
687 0 0       0 if ($package eq 'DB::fake') {
688 0         0 $package = 'main';
689             }
690 0   0     0 $subroutine ||= 'MAIN';
691              
692 0 0       0 unless ($is_initialized) {
693 0         0 $is_initialized = 1;
694 0         0 Devel::Chitin::_do_each_client('init');
695             }
696              
697             # set up the context for DB::eval, so it can properly execute
698             # code on behalf of the user. We add the package in so that the
699             # code is eval'ed in the proper package (not in the debugger!).
700 0         0 save();
701 0         0 local $usercontext =
702             'no strict; no warnings; ($@, $!, $^E, $,, $/, $\, $^W) = @DB::saved;' . "package $package;";
703              
704 0         0 $current_location = Devel::Chitin::Location->new(
705             'package' => $package,
706             filename => $filename,
707             line => $line,
708             subroutine => $subroutine,
709             callsite => scalar Devel::Chitin::Location::get_callsite(),
710             );
711              
712 0         0 $_->notify_trace($current_location) foreach values(%trace_clients);
713              
714 0         0 _execute_actions($filename, $line);
715              
716 0 0       0 goto RETURN_TO_DEBUGGED_PROGRAM if $no_stopping;
717              
718 0         0 _evaluate_watch_exprs();
719              
720 0 0       0 if (! is_breakpoint($package, $filename, $line)) {
721 0         0 goto RETURN_TO_DEBUGGED_PROGRAM;
722             }
723 0         0 $step_over_depth = undef;
724              
725 0         0 Devel::Chitin::_do_each_client('notify_stopped', $current_location);
726              
727             STOPPED_LOOP:
728 0         0 foreach (1) {
729              
730 0         0 while (my $e = shift @pending_eval) {
731 0         0 _eval_in_program_context(@$e);
732             }
733              
734 0         0 my $should_continue = 0;
735 0         0 until ($should_continue) {
736 0         0 my @ready_clients = grep { $_->poll($current_location) } @attached_clients;
  0         0  
737 0 0       0 last STOPPED_LOOP unless (@ready_clients);
738 0         0 do { $should_continue |= $_->idle($current_location) } foreach @ready_clients;
  0         0  
739             }
740              
741 0 0 0     0 redo if ($finished || @pending_eval);
742             }
743              
744 0         0 fill_in_values_for_new_watch_exprs();
745              
746 0         0 Devel::Chitin::_do_each_client('notify_resumed', $current_location);
747              
748             RETURN_TO_DEBUGGED_PROGRAM:
749              
750 0         0 $_->notify_trace_resumed($current_location) foreach values(%trace_clients);
751              
752 0         0 $previous_location = $current_location;
753 0         0 undef $current_location;
754 0         0 Devel::Chitin::Stack::invalidate();
755 0         0 restore();
756             }
757              
758 0         0 BEGIN {
759 34     34   138 my $sub_serial = 1;
760 34         131 @Devel::Chitin::stack_serial = ( [ 'main::MAIN', $sub_serial++ ] );
761 34         1728 %Devel::Chitin::eval_serial = ();
762              
763             sub _allocate_sub_serial {
764 0     0   0 $sub_serial++;
765             }
766             }
767              
768              
769             # When using Class::Autouse, the B::* objects created below to determine if an
770             # anon sub has a name (such as via Sub::Name) trigger calls to its UNIVERSAL
771             # DESTROY as the B::* objects go out of scope as you step in to a call to
772             # that named sub. This hack gives those classes a DESTROY method to avoid that
773             foreach my $class ( qw(B::HV B::GV B::CV) ) {
774             next if $class->can('DESTROY');
775             my $destroy = $class . '::DESTROY';
776 34     34   258 no strict 'refs';
  34         79  
  34         2252  
777       0     *$destroy = sub {};
778             }
779              
780             sub sub {
781 34     34   220 no strict 'refs';
  34         64  
  34         21907  
782 0 0 0 0 1 0 goto &$sub if (! $ready or index($sub, 'Devel::Chitin::StackTracker') == 0 or $debugger_disabled);
      0        
783             #goto &$sub if (! $ready or $in_debugger or index($sub, 'Devel::Chitin::StackTracker') == 0 or $debugger_disabled);
784              
785 0 0       0 local $Devel::Chitin::current_sub = $sub unless $in_debugger;
786              
787 0         0 local @AUTOLOAD_names = @AUTOLOAD_names;
788 0 0       0 if (index($sub, '::AUTOLOAD', -10) >= 0) {
789 0         0 my $caller_pkg = substr($sub, 0, length($sub)-8);
790 0         0 my $caller_AUTOLOAD = ${ $caller_pkg . 'AUTOLOAD'};
  0         0  
791 0         0 unshift @AUTOLOAD_names, $caller_AUTOLOAD;
792             }
793 0         0 my $stack_tracker;
794 0         0 local @Devel::Chitin::stack_serial = @Devel::Chitin::stack_serial;
795 0 0       0 unless ($in_debugger) {
796 0         0 $stack_depth++;
797 0         0 $stack_tracker = _new_stack_tracker(_allocate_sub_serial());
798              
799 0         0 my $subname = $sub;
800 0 0       0 if (ref $sub) {
801 0         0 my $cv = B::svref_2object($sub);
802 0         0 my $gv = $cv->GV;
803 0 0       0 if (my $name = $gv->NAME) {
804 0         0 my $package = $gv->STASH->NAME;
805 0         0 $subname = join('::', $package, $name);
806             }
807             }
808              
809 0         0 push(@Devel::Chitin::stack_serial, [ $subname, $$stack_tracker]);
810             }
811              
812 0         0 my @rv;
813 0 0       0 if (wantarray) {
    0          
814 0         0 @rv = &$sub;
815             } elsif (defined wantarray) {
816 0         0 $rv[0] = &$sub;
817             } else {
818 0         0 &$sub;
819             }
820              
821 0 0       0 delete $Devel::Chitin::eval_serial{$$stack_tracker} if $stack_tracker;
822              
823 0 0       0 return wantarray ? @rv : $rv[0];
824             }
825              
826             sub _new_stack_tracker {
827 0     0   0 my $token = shift;
828 0         0 my $self = bless \$token, 'Devel::Chitin::StackTracker';
829             }
830              
831             sub Devel::Chitin::StackTracker::DESTROY {
832 0     0   0 $stack_depth--;
833 0 0 0     0 $single = 1 if (defined($step_over_depth) and $step_over_depth >= $stack_depth);
834             }
835              
836              
837              
838             # This gets called after a require'd file is compiled, but before it's executed
839             # it's called as DB::postponed(*{"_<$filename"})
840             # We can use this to break on module load, for example.
841             # If $DB::postponed{$subname} exists, then this is called as
842             # DB::postponed($subname)
843             sub postponed {
844 0     0 0 0 my($filename) = ($_[0] =~ m/_\<(.*)$/);
845              
846 0 0       0 if (my $actions = delete $postpone_until_loaded{$filename}) {
847 0         0 $_->($filename) foreach @$actions;
848             }
849             }
850              
851             END {
852 34     34   6370331 $trace = 0;
853              
854 34 50       528 return if $debugger_disabled;
855              
856 34         400 $single=0;
857 34         356 $in_debugger = 1;
858              
859 34         196 eval {
860 34 50       446 Devel::Chitin::_do_each_client('notify_uncaught_exception', $uncaught_exception) if $uncaught_exception;
861              
862 34 50       450 if ($user_requested_exit) {
863 0         0 Devel::Chitin::_do_each_client('notify_program_exit');
864             } else {
865 34         387 Devel::Chitin::_do_each_client('notify_program_terminated', $?);
866 34         178 $finished = 1;
867             # These two will trigger DB::DB and the event loop
868 34         179 $in_debugger = 0;
869 34         199 $single=1;
870 34         323 Devel::Chitin::exiting::at_exit();
871             }
872             }
873             }
874              
875             package Devel::Chitin::exiting;
876             sub at_exit {
877 34     34   53 1;
878             }
879              
880             package DB;
881 34     34   1533 BEGIN { $DB::ready = 1; }
882              
883             1;
884              
885             __END__