File Coverage

blib/lib/Devel/Camelcadedb.pm
Criterion Covered Total %
statement 141 789 17.8
branch 22 430 5.1
condition 3 196 1.5
subroutine 28 93 30.1
pod 0 10 0.0
total 194 1518 12.7


line stmt bran cond sub pod time code
1             package Devel::Camelcadedb;
2             # must be quoted to work correctly with JSON protocol
3             our $VERSION = "v2023.1"; # DO NOT REMOVE FUCKING v, IT KEEPS PROPER VERSIONING
4              
5             # to ensure protocol compatibility between the IDE and the debugger, we will use $API_VERSION variable, to be able
6             # to bump debugger version without necessity to update IDE part.
7             my $API_VERSION = "2019.1";
8              
9             # http://perldoc.perl.org/DB.html
10             # http://perldoc.perl.org/perldebug.html
11             # http://perldoc.perl.org/perldebtut.html
12             # http://perldoc.perl.org/perldebguts.html
13             package DB;
14 1     1   2306 use 5.008;
  1         4  
15 1     1   7 use strict;
  1         2  
  1         20  
16 1     1   5 use warnings;
  1         2  
  1         23  
17 1     1   540 use IO::Socket::INET;
  1         20616  
  1         9  
18 1     1   1001 use IO::Select;
  1         1670  
  1         50  
19 1     1   472 use PadWalker qw/peek_my peek_our/;
  1         631  
  1         62  
20 1     1   7 use Scalar::Util;
  1         3  
  1         34  
21 1     1   697 use Encode;
  1         10001  
  1         72  
22 1     1   6 use overload;
  1         3  
  1         10  
23 1     1   42 use PerlIO;
  1         2  
  1         8  
24 1     1   519 use Hash::StoredIterator;
  1         1461  
  1         59  
25             #use Carp;
26              
27             #sub FLAG_REPORT_GOTO() {0x80;}
28              
29             use constant {
30 1         91 STEP_CONTINUE => 0,
31             STEP_INTO => 1,
32             STEP_OVER => 2,
33 1     1   7 };
  1         2  
34              
35             use constant {
36             # see PERLDBf_* constants in perl.h
37 1         100 DEBUG_ALL => 0x7ff,
38             DEBUG_SINGLE_STEP_ON => 0x20,
39             DEBUG_USE_SUB_ADDRESS => 0x40,
40             DEBUG_REPORT_GOTO => 0x80,
41 1     1   7 };
  1         14  
42             use constant {
43             # debugger enabled
44 1         1604 DEBUG_DEFAULT_FLAGS => DEBUG_ALL & ~(DEBUG_USE_SUB_ADDRESS|DEBUG_REPORT_GOTO), # 0x73f
45             # instrument code, but don't call DB::DB (see sub disable for DB::sub)
46             DEBUG_PREPARE_FLAGS => DEBUG_ALL & ~(DEBUG_USE_SUB_ADDRESS|DEBUG_REPORT_GOTO|DEBUG_SINGLE_STEP_ON), # 0x73c
47 1     1   6 };
  1         2  
48              
49              
50             # Each array @{"::_<$filename"} holds the lines of $filename for a file compiled by Perl. The same is also true for evaled
51             # strings that contain subroutines, or which are currently being executed. The $filename for evaled strings looks like
52             # (eval 34) .
53             # Values in this array are magical in numeric context: they compare equal to zero only if the line is not breakable.
54             #
55             # # @DB::dbline is an alias for @{"::_
56             # Perl), either explicitly chosen with the debugger's f command, or implicitly by flow of execution.
57             #
58             our @dbline = (); # list of lines in currently loaded file
59              
60             # Each hash %{"::_<$filename"} contains breakpoints and actions keyed by line number. Individual entries (as opposed to
61             # the whole hash) are settable. Perl only cares about Boolean true here, although the values used by perl5db.pl have the
62             # form "$break_condition\0$action" .
63             #
64             # The same holds for evaluated strings that contain subroutines, or which are currently being executed. The $filename
65             # for evaled strings looks like (eval 34) .
66             #
67             # %DB::dbline is an alias for %{"::_
68             # the currently-selected file, either explicitly chosen with the debugger's f command, or implicitly by flow of execution.
69             # As previously noted, individual entries (as opposed to the whole hash) are settable. Perl only cares about Boolean
70             # true here, although the values used by perl5db.pl have the form "$break_condition\0$action" .
71             #
72             # Actions in current file (keys are line numbers). The values are strings that have the sprintf(3) format
73             # ("%s\000%s", breakcondition, actioncode) .
74             our %dbline = (); # actions in current file (keyed by line number)
75              
76             # Each scalar ${"::_<$filename"} contains "::_<$filename" . This is also the case for evaluated strings that contain
77             # subroutines, or which are currently being executed. The $filename for evaled strings looks like (eval 34) .
78             #
79             our $dbline;
80              
81             # DB::dump_trace(skip[,count]) skips the specified number of frames and returns a list containing information about the
82             # calling frames (all of them, if count is missing). Each entry is reference to a hash with keys context (either ., $ ,
83             # or @ ), sub (subroutine name, or info about eval), args (undef or a reference to an array), file , and line .
84             #
85              
86             # these are hardcoded in perl source (some are magical)
87              
88             # When execution of the program reaches a subroutine call, a call to &DB::sub (args) is made instead, with $DB::sub
89             # holding the name of the called subroutine. (This doesn't happen if the subroutine was compiled in the DB package.)
90             our $sub = ''; # Name of current executing subroutine.
91              
92             # A hash %DB::sub is maintained, whose keys are subroutine names and whose values have the form
93             # filename:startline-endline . filename has the form (eval 34) for subroutines defined inside evals.
94             #
95             # The keys of this hash are the names of all the known subroutines. Each value is an encoded string that has the
96             # sprintf(3) format ("%s:%d-%d", filename, fromline, toline) .
97             our %sub = (); # "filename:fromline-toline" for every known sub
98              
99             # If you set $DB::single to 2, it's equivalent to having just typed the step over command, whereas a value of 1
100             # means the step into command.
101             our $single = 0; # single-step flag (set it to 1 to enable stops in BEGIN/use) 1 -into, 2 - over
102              
103             # Signal flag. Will be set to a true value if a signal was caught. Clients may check for this flag to abort
104             # time-consuming operations.
105             our $signal = 0;
106              
107             # The $DB::trace variable should be set to 1 to simulate having typed the t command.
108             # This flag is set to true if the API is tracing through subroutine calls.
109             our $trace = 0; # are we tracing through subroutine calls?
110              
111             # For example, whenever you call Perl's built-in caller function from the package DB , the arguments that the
112             # corresponding stack frame was called with are copied to the @DB::args array. These mechanisms are enabled by calling
113             # Perl with the -d switch. Specifically, the following additional features are enabled (cf. $^P in perlvar):
114             our @args = (); # arguments of current subroutine or @ARGV array
115              
116             our @ret = (); # return value of last sub executed in list context
117             our $ret = ''; # return value of last sub executed in scalar context
118              
119             # custom values renderers. Consists of two items arrayrefs, with class as a first field, and code text as second field
120             # every blessed item is checked with this list and if it matches with some renderer - it is used as value in ide.
121             # e.g. ['Foo::Bar', '$it->as_string`] meaning that any 'Foo::Bar' object will be represented with as_string value
122             my @renderers = ();
123              
124             my %_perl_file_id_to_path_map = (); # map of perl file ids without _< => real path detected on loading
125             my %_paths_to_perl_file_id_map = (); # maps real paths to _
126              
127             my %_loaded_breakpoints = (); # map of loaded breakpoints, set and not in form: path => line => object
128             my %_queued_breakpoints_files = (); # map of files with loaded and not set breakpoints
129             my %_references_cache = (); # cache of soft references from peek_my
130              
131             my %_source_been_sent = (); # flags that source been sent
132             my %_file_name_sent = (); # flags that idea been notfied about this file loading
133             my %_evals_to_templates_map = (); # map of evals to templates or something (see template_handler). Structure: eval => target file
134             my %_templates_to_evals_map = (); # map of templates to evals or something (see template_handler). Structure: template => [eval1, eval2, ...]
135              
136             my @glob_slots = qw/SCALAR ARRAY HASH CODE IO FORMAT/;
137             my $glob_slots = join '|', @glob_slots;
138              
139             my $_dev_mode = $ENV{CAMELCADEDB_DEV_MODE}; # enable this to get verbose STDERR output from process
140             my $_debug_log_fh = *STDERR; # debug log fh. If omited, file will be created
141             my $_debug_log_filename = 'current_debug.log';
142             my $_debug_sub_handler = 1; # debug entering/leaving subs, works in dev mode
143             my $_debug_load_handler = 0; # debug modules loading
144             my $_debug_breakpoints = 0; # debug breakpoints setting
145              
146             my $_script_charset = 'utf8'; # all sources and strings without utf flag will be encoded from this encoding to the utf
147              
148             my $_skip_run_stop = 0; # flag for skipping forced stop on run phase
149              
150             # this enables pausing and breakpoints setting while script is running, gives moderate overhead
151             my $_enable_noninteractive_mode = 0;
152              
153             # this enables attemp to set a breakpoint on each leaving/entering sub, gives large overhead, proportional number of breakpoints
154             my $_enable_compile_time_breakpoints = 0;
155              
156             my $_debug_socket;
157             my $_debug_packed_address;
158             my IO::Select $_debug_socket_select;
159              
160             my $coder; # JSON::XS coder
161             my $deparser; # B::Deparse deparser
162              
163             my $frame_prefix_step = " ";
164             my $frame_prefix = '';
165              
166             my $_internal_process = 0;
167              
168             my @saved; # saved runtime environment
169              
170             my $current_package;
171             my $current_file_id;
172             my $current_line;
173              
174             my $trace_code_stack_and_frames = 0; # report traces on entering code
175             my $trace_real_path = 0; # trasing real path transition
176              
177             my $ready_to_go = 0; # set after debugger been initialized
178              
179             my $_stack_frames = [ ]; # stack frames
180              
181             sub _report($;@)
182             {
183 0 0   0   0 return unless $_dev_mode;
184 0         0 my ($message, @sprintf_args) = @_;
185 0         0 chomp $message;
186              
187 0 0       0 unless ($_debug_log_fh)
188             {
189 0 0       0 open $_debug_log_fh, ">", $_debug_log_filename or die "Unable to open debug log $_debug_log_filename $!";
190 0         0 $_debug_log_fh->autoflush( 1 );
191             }
192              
193 0   0     0 printf $_debug_log_fh "$frame_prefix$message\n", map {$_ // 'undef'} @sprintf_args;
  0         0  
194             }
195              
196             sub _format_caller
197             {
198 0     0   0 my (@caller) = @_;
199 0 0 0     0 return sprintf "%s %s%s%s from %s::, %s line %s; %s %s %s %s",
    0 0        
    0          
    0          
    0          
200             map $_ // 'undef',
201             defined $caller[5] ? $caller[5] ? 'array' : 'scalar' : 'void', # wantarray
202             $caller[3], # target sub
203             $caller[4] ? '(@_)' : '', # has args
204             $caller[7] ? ' [require '.$caller[6].']' : '', # is_require and evaltext
205             $caller[0], # package
206             $caller[1], # filename
207             $caller[2], # line
208             $caller[7] ? '' : $caller[6] // '', # evaltext if no isrequire
209             $caller[8], # strcit
210             $caller[9], # warnings
211             $caller[10], # hinthash
212             ;
213             }
214              
215             sub _get_loaded_files_map
216             {
217 0     0   0 my %result = ();
218 0         0 foreach my $key (keys %::)
219             {
220 0         0 my $glob = $::{$key};
221 0 0       0 next unless $key =~ s/^_
222 0 0 0     0 next unless *$glob{ARRAY} && scalar @{*$glob{ARRAY}};
  0         0  
223 0         0 $result{$key} = ${*$glob};
  0         0  
224             }
225 0         0 return \%result;
226             }
227              
228             sub _get_file_descriptor_by_id
229             {
230 0     0   0 my ($file_id) = @_;
231              
232 0         0 my $real_path = _get_real_path_by_normalized_perl_file_id( $file_id );
233 0         0 my $presentable_name;
234              
235 0 0       0 if ($real_path =~ /^\(eval \d+\)/)
236             {
237 0         0 my $eval_map_entry = $_evals_to_templates_map{$real_path};
238 0 0 0     0 if ($eval_map_entry && $eval_map_entry->{path})
239             {
240 0         0 $presentable_name = $eval_map_entry->{path};
241             }
242             # else
243             # {
244             # $presentable_name = $real_path;
245             # $presentable_name =~ s/^(\(eval \d+\)).+$/$1/;
246             # }
247             }
248              
249             return {
250 0         0 path => $real_path,
251             name => $presentable_name,
252             };
253             }
254              
255             sub _send_loaded_files_names
256             {
257 0     0   0 my $loaded_files_map = _get_loaded_files_map();
258 0         0 my @files_to_add = ();
259 0         0 my @files_to_remove = ();
260              
261 0         0 foreach my $file_id (keys %$loaded_files_map)
262             {
263 0 0 0     0 next if index( $file_id, 'Camelcadedb.pm' ) != -1 || exists $_file_name_sent{$file_id};
264 0         0 $_file_name_sent{$file_id} = 1;
265 0         0 push @files_to_add, _get_file_descriptor_by_id( $file_id );
266             }
267              
268 0         0 foreach my $file_id (keys %_file_name_sent)
269             {
270 0 0       0 next if exists $loaded_files_map->{$file_id};
271 0         0 delete $_file_name_sent{$file_id};
272 0         0 push @files_to_remove, _get_file_descriptor_by_id( $file_id );
273             }
274              
275 0 0       0 if (scalar @files_to_add + scalar @files_to_remove)
276             {
277 0         0 _send_event( "LOADED_FILES_DELTA", {
278             add => \@files_to_add,
279             remove => \@files_to_remove
280             } );
281             }
282             }
283              
284              
285             sub _send_breakpoint_reached_event
286             {
287 0     0   0 my ($breakpoint) = @_;
288              
289             my $event_data = {
290             path => $breakpoint->{path},
291             line => $breakpoint->{line} - 1,
292             logmessage => $breakpoint->{action_result},
293 0         0 };
294              
295 0 0       0 if ($breakpoint->{suspend})
296             {
297 0         0 $event_data->{suspend} = \1;
298 0         0 $event_data->{frames} = _calc_stack_frames();
299             }
300             else
301             {
302 0         0 $event_data->{suspend} = \0;
303 0         0 $event_data->{frames} = [ ];
304             }
305              
306 0         0 _send_event( 'BREAKPOINT_REACHED', $event_data );
307             }
308              
309             sub _send_event
310             {
311 0     0   0 my ($name, $data) = @_;
312              
313 0         0 _send_data_to_debugger( +{
314             event => $name,
315             data => $data
316             } );
317             }
318              
319             sub _dump_stack
320             {
321 0     0   0 my $depth = 0;
322 0 0       0 _report "Stack trace:\n" if $_dev_mode;
323 0         0 while()
324             {
325 0         0 my @caller = caller( $depth );
326 0 0       0 last unless defined $caller[2];
327 0 0       0 _report $frame_prefix_step."%s: %s\n", $depth++, _format_caller( @caller ) if $_dev_mode;
328             }
329 0         0 1;
330             }
331              
332             sub _dump_frames
333             {
334 0     0   0 my $depth = 0;
335 0 0       0 _report "Frames trace:\n" if $_dev_mode;
336 0         0 foreach my $frame (@$_stack_frames)
337             {
338             _report $frame_prefix_step."%s: %s\n", $depth++,
339             join ', ', map $_ // 'undef', @$frame{qw/subname file current_line single/},
340 0 0 0     0 $frame->{is_use_block} ? '(use block)' : ''
    0          
341             if $_dev_mode;
342             }
343 0         0 1;
344             }
345              
346             sub _deparse_code
347             {
348 0     0   0 my ($code) = @_;
349 0   0     0 $deparser ||= B::Deparse->new();
350 0         0 return $deparser->coderef2text( $code );
351             }
352              
353             sub _send_transaction_response
354             {
355 0     0   0 my ($transaction_id, $data) = @_;
356              
357 0         0 _send_data_to_debugger( +{
358             event => 'RESPONSE',
359             transactionId => $transaction_id,
360             data => $data,
361             }
362             );
363             }
364              
365             sub _get_file_source_by_file_id
366             {
367 0     0   0 my ($file_id) = @_;
368 0         0 $_source_been_sent{$file_id} = 1;
369             {
370 1     1   9 no strict 'refs';
  1         2  
  1         368  
  0         0  
371 0 0       0 _report "Getting source of main::_<$file_id" if $_dev_mode;
372 0         0 my @lines = @{"main::_<$file_id"};
  0         0  
373 0         0 shift @lines;
374 0         0 return _to_utf8( join '', @lines );
375             }
376             }
377              
378             sub _get_file_source_once_by_file_id
379             {
380 0     0   0 my ($file_id) = @_;
381 0 0       0 return if $_source_been_sent{$file_id};
382 0         0 return _get_file_source_by_file_id( $file_id );
383             }
384              
385             sub _get_file_source_handler
386             {
387 0     0   0 my ($request_serialized_object) = @_;
388 0         0 my $transaction_wrapper = _deserialize( $request_serialized_object );
389 0         0 my ($transaction_id, $request_object) = @$transaction_wrapper{qw/id data/};
390              
391 0         0 my $file_id = _get_perl_file_id_by_real_path( $request_object->{path} );
392              
393 0 0       0 _report "Fetching source for $file_id $request_object->{path}" if $_dev_mode;
394              
395 0   0     0 _send_transaction_response(
396             $transaction_id,
397             _get_file_source_once_by_file_id( $file_id ) // '# No source found for '.$file_id
398             );
399             }
400              
401             sub _get_reference_subelements
402             {
403 0     0   0 my ($request_serialized_object) = @_;
404 0         0 my $transaction_wrapper = _deserialize( $request_serialized_object );
405 0         0 my ($transaction_id, $request_object) = @$transaction_wrapper{qw/id data/};
406 0         0 my ($offset, $size, $key) = @$request_object{qw/offset limit key/};
407 0         0 my $data = [ ];
408              
409 0         0 my $source_data;
410              
411 0 0       0 if ($key =~ /^\*(.+?)(?:\{($glob_slots)\})?$/) # hack for globs by names
412             {
413 1     1   7 no strict 'refs';
  1         2  
  1         372  
414 0         0 my ( $name, $slot) = ($1, $2);
415              
416 0 0       0 if ($slot)
417             {
418 0         0 $source_data = *{$name}{$slot};
  0         0  
419             }
420             else
421             {
422 0         0 $source_data = \*{$name};
  0         0  
423             }
424              
425 0 0       0 _report "Got glob ref $key => $source_data" if $_dev_mode;
426             }
427             else
428             {
429 0         0 $source_data = $_references_cache{$key};
430             }
431 0 0       0 if ($source_data)
432             {
433 0         0 my $reftype = Scalar::Util::reftype( $source_data );
434              
435 0 0 0     0 if ($reftype eq 'ARRAY' && $#$source_data >= $offset)
    0          
    0          
    0          
436             {
437 0         0 my $last_index = $offset + $size;
438              
439 0   0     0 for (my $item_number = $offset; $item_number < $last_index && $item_number < @$source_data; $item_number++)
440             {
441 0         0 push @$data, _get_reference_descriptor( "[$item_number]", \$source_data->[$item_number] );
442             }
443             }
444             elsif ($reftype eq 'HASH')
445             {
446 0         0 my $hash_iterator = Hash::StoredIterator::hash_get_iterator( $source_data );
447 0         0 my @keys = sort keys %$source_data;
448 0         0 Hash::StoredIterator::hash_set_iterator( $source_data, $hash_iterator );
449              
450 0 0       0 if ($#keys >= $offset)
451             {
452 0         0 my $last_index = $offset + $size;
453              
454 0   0     0 for (my $item_number = $offset; $item_number < $last_index && $item_number < @keys; $item_number++)
455             {
456 0         0 my $hash_key = $keys[$item_number];
457 0         0 push @$data, _get_reference_descriptor( "'$hash_key'", \$source_data->{$hash_key} );
458             }
459             }
460             }
461             elsif ($reftype eq 'REF')
462             {
463 0         0 push @$data, _get_reference_descriptor($source_data, $$source_data);
464             }
465             elsif ($reftype eq 'GLOB')
466             {
467 1     1   8 no strict 'refs';
  1         2  
  1         835  
468              
469 0         0 foreach my $glob_slot (@glob_slots)
470             {
471 0         0 my $reference = *$source_data{$glob_slot};
472 0 0       0 next unless $reference;
473 0         0 my $desciptor = _get_reference_descriptor( $glob_slot, \$reference );
474              
475             # hack for DB namespace, see https://github.com/hurricup/Perl5-IDEA/issues/1151
476 0 0 0     0 if ($glob_slot eq 'HASH' && $key =~ /^\*(::)*(main::)*(::)*DB(::)?$/)
477             {
478 0         0 $desciptor->{expandable} = \0;
479 0         0 $desciptor->{size} = 0;
480             }
481              
482 0         0 push @$data, $desciptor;
483             }
484              
485             }
486             else
487             {
488 0 0       0 _report "Dont know how to iterate $reftype" if $_dev_mode;
489             }
490              
491             }
492             else
493             {
494 0 0       0 _report "No source data for $key\n" if $_dev_mode;
495             }
496              
497 0         0 _send_transaction_response( $transaction_id, $data );
498             }
499              
500             sub _format_variables_hash
501             {
502 0     0   0 my ($vars_hash) = @_;
503              
504 0         0 my $result = [ ];
505              
506 0         0 foreach my $variable (sort keys %$vars_hash)
507             {
508 0         0 my $value = $vars_hash->{$variable};
509 0         0 push @$result, _get_reference_descriptor( $variable, $value );
510             }
511              
512 0         0 return $result;
513             }
514              
515             sub _to_utf8
516             {
517 2     2   5 my ($value) = @_;
518              
519 2 50       5 return $value unless $value;
520              
521 2 50       8 if (utf8::is_utf8( $value )) # if values is marked as utf8
    50          
522             {
523 0         0 utf8::encode( $value ); # we just making octets from it
524             }
525             elsif ($value =~ /[\x80-\xFF]/) # otherwise, if we've got non-ascii symbols, we suppose it's in configured encoding
526             {
527 0         0 Encode::from_to( $value, $_script_charset, 'utf8' );
528             }
529              
530 2         19 return $value;
531             }
532              
533             sub _from_utf8
534             {
535 0     0   0 my ($value) = @_;
536              
537 0 0       0 return $value unless $value;
538              
539 0 0       0 if ($_script_charset ne 'utf8') # if script uses non-utf encoding, just encode data to the script encoding
540             {
541 0         0 Encode::from_to( $value, 'utf8', $_script_charset );
542             }
543             else # otherwise, decode octets to characters
544             {
545 0         0 utf8::decode( $value );
546             }
547              
548 0         0 return $value;
549             }
550              
551             sub _get_reference_descriptor
552             {
553 1     1   14 my ($name, $value) = @_;
554              
555 1         14 my $key = $value;
556 1         5 my $reftype = Scalar::Util::reftype( $value );
557 1         2 my $ref = ref $value;
558              
559 1         2 my $size = 0;
560 1         4 my $type = overload::StrVal( $value );
561              
562 1         21 my $expandable = \0;
563 1 50 33     10 my $is_blessed = $ref && Scalar::Util::blessed( $value ) ? \1 : \0;
564 1         1 my $ref_depth = 0;
565 1         2 my $is_utf = \0;
566 1         2 my $layers = undef;
567 1         1 my $fileno = undef;
568 1         2 my $rendered = undef;
569 1         2 my $rendered_error = \0;
570 1         1 my $tied;
571              
572 1 50       5 if (!$reftype)
    50          
    50          
    0          
    0          
    0          
573             {
574 0         0 $type = "SCALAR";
575 0         0 $tied = tied $value;
576 0 0 0     0 $is_utf = defined $value && utf8::is_utf8( $value ) ? \1 : \0;
577 0 0       0 $value = defined $value ? "\"$value\"" : 'undef'; #_escape_scalar(
578 0   0     0 $key //= 'undef';
579             }
580             elsif ($reftype eq 'SCALAR')
581             {
582 0         0 $tied = tied $$value;
583 0 0 0     0 $is_utf = defined $$value && utf8::is_utf8( $$value ) ? \1 : \0;
584 0 0       0 $value = defined $$value ? "\"$$value\"" : 'undef'; #_escape_scalar(
585             }
586             elsif ($reftype eq 'REF') {
587 1   50     4 $type = overload::StrVal($$value) || 'unknown';
588 1         5 $tied = tied $value;
589 1         2 $size = 1;
590 1         2 $expandable = \1;
591 1         1 $ref_depth = 1;
592 1         2 $ref = undef; # to prevent rendering data
593             }
594             elsif ($reftype eq 'ARRAY')
595             {
596 0         0 $size = scalar @$value;
597 0         0 $tied = tied @$value;
598 0         0 $value = sprintf "size = %s", $size;
599 0 0       0 $expandable = $size ? \1 : \0;
600             }
601             elsif ($reftype eq 'HASH')
602             {
603 0         0 $tied = tied %$value;
604 0         0 my $hash_iterator = Hash::StoredIterator::hash_get_iterator( $value );
605 0         0 $size = scalar keys %$value;
606 0         0 Hash::StoredIterator::hash_set_iterator( $value, $hash_iterator );
607              
608 0         0 $value = sprintf "size = %s", $size;
609 0 0       0 $expandable = $size ? \1 : \0;
610             }
611             elsif ($reftype eq 'GLOB')
612             {
613 1     1   9 no strict 'refs';
  1         2  
  1         3201  
614 0         0 $tied = tied *$value;
615 0         0 $size = scalar grep *$value{$_}, @glob_slots;
616 0         0 $value = "*".*$value{PACKAGE}."::".*$value{NAME};
617 0         0 $layers = _get_layers($key);
618 0         0 $fileno = fileno($key);
619 0 0       0 $expandable = $size ? \1 : \0;
620             }
621              
622 1         2 my $char_code;
623 1         2 my $stringified_key = "$key";
624 1         6 $stringified_key =~ s{(.)}{
625 14         21 $char_code = ord( $1 );
626 14 50       38 $char_code < 32 ? '^'.chr( $char_code + 0x40 ) : $1
627             }gsex;
628              
629 1 50       3 if ($reftype)
630             {
631 1         3 $_references_cache{$stringified_key} = $key;
632             }
633              
634 1 50       3 if ($ref) {
635 0         0 my $got_renderer = 0;
636 0         0 for my $renderer (@renderers) {
637 0 0       0 if (UNIVERSAL::isa($key, $renderer->[0])) {
638 0         0 $got_renderer = 1;
639 0         0 $DB::Sandbox::it = $key;
640 0         0 $rendered = eval 'package DB::Sandbox;our $it;' . $renderer->[1];
641 0 0       0 if ($@) {
642 0         0 $rendered = $@;
643 0         0 $rendered_error = \1;
644             }
645 0         0 last;
646             }
647             }
648 0 0       0 unless ($got_renderer) {
649 0         0 $rendered = "$key";
650 0 0       0 if ($rendered eq $type) {
651 0         0 $rendered = undef;
652             }
653             }
654             }
655              
656 1         2 $name = "$name";
657 1         1 $value = "$value";
658              
659 1         4 $name =~ s{(.)}{
660 8         11 $char_code = ord( $1 );
661 8 50       22 $char_code < 32 ? '^'.chr( $char_code + 0x40 ) : $1
662             }gsex;
663 1         4 $value =~ s{([^\n\r\f\t])}{
664 14         19 $char_code = ord($1);
665 14 50       37 $char_code < 32 ? '^' . chr($char_code + 0x40) : $1
666             }gsex;
667              
668             # handling encoding
669              
670 1         4 my $result = {
671             name => _to_utf8("$name"),
672             value => _to_utf8("$value"),
673             type => "$type",
674             expandable => $expandable,
675             key => $stringified_key,
676             size => $size,
677             blessed => $is_blessed,
678             ref_depth => $ref_depth,
679             is_utf => $is_utf
680             };
681              
682 1 50       5 if (defined $rendered) {
683 0         0 $rendered =~ s{([^\n\r\f\t])}{
684 0         0 $char_code = ord($1);
685 0 0       0 $char_code < 32 ? '^' . chr($char_code + 0x40) : $1
686             }gsex;
687              
688 0         0 $result->{rendered} = _to_utf8($rendered);
689 0         0 $result->{rendered_error} = $rendered_error;
690             }
691 1 50       3 $result->{layers} = $layers if $layers;
692 1 50       2 $result->{fileno} = "".$fileno if defined $fileno;
693 1 50       3 $result->{tied_with} = _get_reference_descriptor(object => $tied) if $tied;
694              
695 1         6 return $result;
696             }
697              
698             sub _get_layers{
699 0     0   0 my $glob = shift;
700              
701 0         0 my %result = ();
702 0         0 my $input_layers = _pack_layers(PerlIO::get_layers($glob, details => 1));
703 0 0 0     0 $result{input} = $input_layers if $input_layers && @$input_layers;
704              
705 0         0 my $output_layers = _pack_layers(PerlIO::get_layers($glob, details => 1, output=>1));
706 0 0 0     0 $result{output} = $output_layers if $output_layers && @$output_layers;
707              
708 0 0       0 return scalar keys %result ? \%result: undef;
709             }
710              
711             sub _pack_layers{
712 0     0   0 my @result = ();
713 0         0 push @result, { name => shift, param => shift, flags => shift} while @_;
714 0         0 return \@result;
715             }
716              
717             #
718             # Making scalar control elements visible, \n\r for now, need cool conception
719             #
720             my %map = (
721             "\n" => '\n',
722             "\r" => '\r',
723             "\f" => '\f',
724             "\t" => '\t',
725             );
726              
727             sub _escape_scalar
728             {
729 0     0   0 my ($scalar) = @_;
730 0         0 $scalar =~ s{\\(?=[rnft])}{\\\\}sg;
731 0         0 $scalar =~ s/([\r\n\f\t])/$map{$1}/seg;
  0         0  
732 0         0 return $scalar;
733             }
734              
735             sub _get_current_stack_frame
736             {
737 0     0   0 return $_stack_frames->[-1];
738             }
739              
740             sub _send_data_to_debugger
741             {
742 0     0   0 my ($event) = @_;
743 0         0 _send_string_to_debugger( _serialize( $event ) );
744             }
745              
746             sub _send_string_to_debugger
747             {
748 0     0   0 my ($string) = @_;
749 0         0 $string .= "\n";
750 0         0 print $_debug_socket $string;
751 0 0       0 _report "Sent to debugger: %s", $string if $_dev_mode;
752             }
753              
754             sub _get_adjusted_line_number
755             {
756 0     0   0 my ($line_number) = @_;
757 0         0 return $line_number - 1;
758             }
759              
760             #@returns JSON::XS
761             sub _get_seraizlier
762             {
763 0 0   0   0 unless ($coder)
764             {
765 0         0 $coder = JSON::XS->new();
766 0         0 $coder->latin1();
767             }
768 0         0 return $coder;
769             }
770              
771             sub _serialize
772             {
773 0     0   0 my ($data) = @_;
774 0         0 return _get_seraizlier->encode( $data );
775             }
776              
777             sub _deserialize
778             {
779 0     0   0 my ($json_string) = @_;
780 0         0 return _get_seraizlier->decode( $json_string );
781             }
782              
783             sub _calc_stack_frames
784             {
785 0     0   0 my $frames = [ ];
786 0         0 my $depth = 0;
787 0         0 %_references_cache = ();
788              
789 0         0 while ()
790             {
791 0         0 my ($package, $filename, $line, $subroutine, $hasargs,
792             $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = caller( $depth );
793              
794 0         0 my $cnt = 0;
795 0         0 my @frame_args = map _get_reference_descriptor( '$_['.$cnt++.']', $_ ), @DB::args;
796              
797 0 0       0 last unless defined $filename;
798              
799 0 0 0     0 if ($package && $package ne 'DB')
800             {
801 0 0 0     0 if (@$frames && $subroutine ne '(eval)')
802             {
803 0         0 $frames->[-1]->{file}->{name} = $subroutine;
804             }
805              
806 0         0 my $global_variables = [ ];
807 0         0 my $global_variables_hash = eval {peek_our( $depth + 1 )};
  0         0  
808 0 0       0 unless ($@)
809             {
810 0         0 $global_variables = _format_variables_hash( $global_variables_hash );
811             }
812              
813 0         0 my $lexical_variables = [ ];
814 0         0 my $variables_hash = eval {peek_my( $depth + 1 )};
  0         0  
815 0 0       0 unless ($@)
816             {
817 0         0 $lexical_variables = _format_variables_hash( $variables_hash );
818             }
819              
820 0 0       0 $frames->[-1]->{args} = \@frame_args if scalar @$frames;
821              
822 0         0 my $descriptor = _get_file_descriptor_by_id( $filename );
823              
824 0         0 push @$frames, {
825             file => $descriptor,
826             line => $line - 1,
827             lexicals => $lexical_variables,
828             globals => $global_variables,
829             main_size => scalar keys %::,
830             args => [ ],
831             };
832             }
833              
834 0         0 $depth++;
835             }
836              
837 0         0 return $frames;
838             }
839              
840             sub _is_use_frame
841             {
842 0     0   0 my $stack_frame = shift;
843 0         0 my $is_use_block = 0;
844              
845 0 0       0 if (ref $stack_frame->{subname})
846             {
847 0         0 my $deparsed_block = _deparse_code( $stack_frame->{subname} );
848 0         0 $is_use_block = $deparsed_block =~ /require\s+[\w\:]+\s*;\s*do/si;
849             }
850              
851 0         0 return $is_use_block;
852             }
853              
854             sub _set_frames_single
855             {
856 0     0   0 my ($new_value) = @_;
857 0         0 foreach my $frame (@{$_stack_frames})
  0         0  
858             {
859 0         0 $frame->{single} = $new_value;
860             }
861 0         0 $DB::single = $new_value;
862             }
863              
864             sub _hold_the_line
865             {
866 0     0   0 _set_frames_single( STEP_INTO );
867             }
868              
869             sub _release_the_hounds
870             {
871 0     0   0 _set_frames_single( STEP_CONTINUE );
872             }
873              
874             sub _process_command
875             {
876 0     0   0 my ($command) = @_;
877 0 0       0 _report "============> Got command: '%s'\n", $command if $_dev_mode;
878              
879 0 0       0 if ($command eq 'q')
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
880             {
881 0 0       0 _report "Exiting" if $_dev_mode;
882 0         0 exit;
883             }
884             elsif ($command =~ /^e\s+(.+)$/) # eval expresion
885             {
886 0         0 my $data = $1;
887 0         0 my $transaction_data = _deserialize( $data );
888              
889 0         0 my ($transaction_id, $request_object) = @$transaction_data{qw/id data/};
890              
891 0   0     0 my $result = _eval_expression( _from_utf8( $request_object->{expression} // '' ) );
892 0         0 $result->{result} = _get_reference_descriptor( result => $result->{result} );
893              
894 0         0 _send_transaction_response( $transaction_id, $result );
895              
896 0 0       0 _report "Result is $result\n" if $_dev_mode;
897             }
898             elsif ($command eq 'pause')
899             {
900 0         0 _hold_the_line;
901             }
902             elsif ($command eq 'g')
903             {
904 0         0 _release_the_hounds();
905 0         0 return;
906             }
907             elsif ($command =~ /^b (.+)$/) # set breakpoints from proto
908             {
909 0         0 _process_new_breakpoints( $1 );
910             }
911             elsif ($command =~ /^p (.+)$/) # Run to cursor
912             {
913 0         0 _set_run_to_cursor_breakpoint( $1 );
914 0         0 _release_the_hounds();
915 0         0 return;
916             }
917             elsif ($command eq 'o') # over,
918             {
919 0         0 my $current_frame = _get_current_stack_frame;
920 0 0       0 if (_is_use_frame( $current_frame ))
921             {
922 0         0 $current_frame->{single} = STEP_INTO;
923 0         0 $DB::single = STEP_CONTINUE;
924             }
925             else
926             {
927 0         0 $DB::single = STEP_OVER;
928             }
929 0         0 return;
930             }
931             elsif ($command =~ /^getchildren (.+)$/) # expand,
932             {
933 0         0 _get_reference_subelements( $1 );
934             }
935             elsif ($command =~ /^get_source (.+)$/) # get eval/file source
936             {
937 0         0 _get_file_source_handler( $1 );
938             }
939             elsif ($command eq 'u') # step out
940             {
941 0         0 my $current_frame = _get_current_stack_frame;
942 0 0       0 if (_is_use_frame( $current_frame ))
943             {
944 0         0 $current_frame->{single} = STEP_CONTINUE;
945             }
946 0         0 $DB::single = STEP_CONTINUE;
947              
948 0         0 return;
949             }
950             else
951             {
952 0         0 $DB::single = STEP_INTO;
953 0         0 return;
954             }
955 0         0 return 1;
956             }
957              
958             my $input_buffer = '';
959              
960             sub _get_next_command
961             {
962 0     0   0 my $read_bytes;
963 0         0 my $new_line_index = index $input_buffer, "\n";
964              
965 0 0       0 if ($new_line_index == -1)
966             {
967 0         0 while( $read_bytes = sysread( $_debug_socket, $input_buffer, 10240, length( $input_buffer ) ))
968             {
969 0 0       0 last if ($new_line_index = index $input_buffer, "\n") > -1;
970             }
971 0 0       0 unless (defined $read_bytes)
972             {
973 0         0 die 'Debugging socket disconnected';
974             }
975 0 0       0 unless ($new_line_index > -1)
976             {
977 0 0       0 if (scalar @saved) {
978 0         0 ($@, $!, $^E, $,, $/, $\, $^W) = @saved;
979 0         0 @saved = ();
980             }
981 0         0 print STDERR "Buffer $input_buffer has no newlines in it and nothing is in the socket\n";
982 0         0 exit -1;
983             }
984             }
985 0         0 my $command = substr $input_buffer, 0, $new_line_index + 1, '';
986 0         0 $command =~ s/[\r\n]+$//;
987             # printf STDERR "Got command: %s and left in buffer %s\n%s\n\%s\n", length $command, length $input_buffer ,$command, $input_buffer;
988 0         0 return $command;
989             }
990              
991             sub _can_read
992             {
993 0   0 0   0 return length( $input_buffer ) > 0 || ($_debug_socket && $_debug_socket_select && scalar $_debug_socket_select->can_read( 0 ));
994             }
995              
996             sub _event_handler
997             {
998 0     0   0 my ($breakpoint) = @_;
999 0         0 _send_loaded_files_names();
1000              
1001 0 0 0     0 if ($breakpoint && !$breakpoint->{run_to_cursor})
1002             {
1003 0         0 _send_breakpoint_reached_event( $breakpoint );
1004             }
1005             else
1006             {
1007 0         0 _send_event( "STOP", _calc_stack_frames() );
1008             }
1009              
1010 0         0 while()
1011             {
1012 0 0       0 _report "Waiting for input\n" if $_dev_mode;
1013 0 0       0 _process_command( _get_next_command ) || return;
1014             }
1015             }
1016              
1017              
1018             sub _enter_frame
1019             {
1020 0     0   0 my ($old_db_single, $wantarray) = @_;
1021              
1022 0 0 0     0 _report "Entering frame %s: %s %s-%s-%s, %s",
    0 0        
    0 0        
      0        
1023             scalar @$_stack_frames + 1,
1024             $DB::sub,
1025             $DB::trace // 'undef',
1026             $DB::signal // 'undef',
1027             $old_db_single // 'undef',
1028             $wantarray ? 'ARRAY' : defined $wantarray ? 'SCALAR' : 'VOID'
1029             if $_debug_sub_handler && $_dev_mode;
1030              
1031 0         0 $frame_prefix = $frame_prefix_step x (scalar @$_stack_frames + 1);
1032              
1033 0         0 my $new_stack_frame = {
1034             subname => $DB::sub,
1035             single => $old_db_single,
1036             };
1037 0         0 push @{$_stack_frames}, $new_stack_frame;
  0         0  
1038 0 0 0     0 _set_break_points_for_files() if $_enable_compile_time_breakpoints && $ready_to_go;
1039 0         0 return $new_stack_frame;
1040             }
1041              
1042              
1043             sub _exit_frame
1044             {
1045 0     0   0 $_internal_process = 1;
1046 0         0 my $frame = pop @$_stack_frames;
1047 0         0 $frame_prefix = $frame_prefix_step x (scalar @$_stack_frames);
1048             _report "Leaving frame %s, setting single to %s", (scalar @$_stack_frames + 1),
1049 0 0 0     0 $frame->{single} if $_debug_sub_handler && $_dev_mode;
1050 0         0 $DB::single = $frame->{single};
1051 0         0 $_internal_process = 0;
1052             }
1053              
1054             sub _get_normalized_perl_file_id
1055             {
1056 0     0   0 my ($perl_file_id) = @_;
1057 0 0       0 if ($perl_file_id =~ /_<(.+)$/)
1058             {
1059 0         0 return $1;
1060             }
1061             else
1062             {
1063 0         0 die "PANIC: Incorrect perl file id $perl_file_id";
1064             }
1065              
1066             }
1067              
1068             sub _get_perl_file_id_by_real_path
1069             {
1070 0     0   0 my ($path) = @_;
1071              
1072 0 0       0 return $path if $path =~ /^\(eval \d+\)/;
1073 0 0       0 return exists $_paths_to_perl_file_id_map{$path} ? $_paths_to_perl_file_id_map{$path} : undef;
1074             }
1075              
1076              
1077              
1078             sub _get_real_path_by_normalized_perl_file_id
1079             {
1080 143     143   311 my $perl_file_id = shift;
1081              
1082 143 50       259 unless ($perl_file_id)
1083             {
1084 0 0 0     0 _dump_stack && _dump_frames && die "Perl normalized file id undefined";
1085             }
1086              
1087 143 50       269 if (!exists $_perl_file_id_to_path_map{$perl_file_id})
1088             {
1089 1     1   10 no strict 'refs';
  1         9  
  1         2340  
1090 143         243 my $path = ${*{"::_<$perl_file_id"}};
  143         186  
  143         595  
1091 143 50       335 return '' unless defined $path; # some subs created via XS
1092 143         234 my $real_path = _calc_real_path( $path, $perl_file_id );
1093              
1094 143         495 $_perl_file_id_to_path_map{$perl_file_id} = $real_path;
1095 143         377 $_paths_to_perl_file_id_map{$real_path} = $perl_file_id;
1096             }
1097 143         304 return $_perl_file_id_to_path_map{$perl_file_id};
1098             }
1099              
1100             sub _get_real_path_by_perl_file_id
1101             {
1102 0     0   0 my ($perl_file_id) = @_;
1103 0         0 return _get_real_path_by_normalized_perl_file_id( _get_normalized_perl_file_id( $perl_file_id ) );
1104             }
1105              
1106             sub _get_loaded_breakpoints_by_real_path
1107             {
1108 0     0   0 my ($real_path) = @_;
1109              
1110 0         0 my $result = { };
1111              
1112 0 0       0 if ($_loaded_breakpoints{$real_path})
1113             {
1114 0 0 0     0 _report "Found real breakpoints" if $_dev_mode && $_debug_breakpoints;
1115 0         0 %$result = %{$_loaded_breakpoints{$real_path}};
  0         0  
1116             }
1117              
1118             # append breakpoints from templates
1119 0 0       0 if (my $substituted_file_descriptor = $_evals_to_templates_map{$real_path})
1120             {
1121 0         0 my ($template_path, $lines_map) = @$substituted_file_descriptor{qw/path lines_map/};
1122 0 0 0     0 _report "Found template file %s", $template_path if $_dev_mode && $_debug_breakpoints;
1123 0 0       0 if (my $template_breakpoints = $_loaded_breakpoints{$template_path})
1124             {
1125 0 0 0     0 _report "Found template breakpoints" if $_dev_mode && $_debug_breakpoints;
1126 0         0 foreach my $line (keys %$template_breakpoints)
1127             {
1128 0 0       0 if (my $mapped_line = $lines_map->{$line})
1129             {
1130 0 0 0     0 _report "Got mapped breakpoint %s => %s", $line, $mapped_line if $_dev_mode && $_debug_breakpoints;
1131 0   0     0 $result->{$mapped_line} //= $template_breakpoints->{$line};
1132             }
1133             }
1134             }
1135             }
1136              
1137 0 0       0 return scalar keys %$result ? $result : undef;
1138             }
1139              
1140             sub _get_current_breakpoint
1141             {
1142 0 0 0 0   0 return if $DB::single || $DB::signal;
1143 0         0 my $current_breakpoint = $DB::dbline{$current_line};
1144 0 0       0 return unless $current_breakpoint;
1145 0 0       0 if ($current_breakpoint->{run_to_cursor})
1146             {
1147 0         0 $current_breakpoint->{remove} = 1;
1148 0         0 $current_breakpoint->{line}--; # fixme find out why works without it
1149 0         0 _process_breakpoints_descriptors( [ $current_breakpoint ] );
1150             }
1151 0         0 return $current_breakpoint;
1152             }
1153              
1154             sub _eval_expression
1155             {
1156 0     0   0 my ($expression ) = @_;
1157              
1158 0         0 my $expr = "no strict; package $current_package;".'( $@, $!, $^E, $,, $/, $\, $^W ) = @DB::saved;'."$expression";
1159 0 0       0 _report "Running %s\n", $expr if $_dev_mode;
1160              
1161 0         0 my $result;
1162              
1163             {
1164 0     0   0 local $SIG{__WARN__} = sub {};
  0         0  
1165 0         0 $result = eval $expr;
1166             }
1167              
1168 0 0       0 if (my $e = $@)
1169             {
1170             # fixme handle object exceptions
1171 0 0       0 unless (ref $e) # message, change it
1172             {
1173 0         0 $e = join "; ", map {s/ at \(eval \d+.+$//;
  0         0  
1174 0         0 $_ } grep $_, split /[\r\n]+/, $e;
1175             }
1176             $result = {
1177 0         0 error => \1,
1178             result => $e
1179             };
1180             }
1181             else
1182             {
1183 0         0 $result = {
1184             error => \0,
1185             result => $result
1186             };
1187             }
1188              
1189 0         0 return $result;
1190             }
1191              
1192             sub _reset_breakpoint
1193             {
1194 0     0   0 my ($breakpoint_descriptor, $real_line, $perl_breakpoints_map) = @_;
1195              
1196 0         0 my $real_path = $breakpoint_descriptor->{path};
1197              
1198 0 0 0     0 if (exists $_loaded_breakpoints{$real_path} && exists $_loaded_breakpoints{$real_path}->{$real_line})
1199             {
1200 0         0 delete $_loaded_breakpoints{$real_path}->{$real_line};
1201             }
1202              
1203 0 0       0 if ($perl_breakpoints_map)
1204             {
1205 0         0 $perl_breakpoints_map->{$real_line} = 0;
1206             }
1207             }
1208              
1209             sub _set_run_to_cursor_breakpoint
1210             {
1211 0     0   0 my ($serialized_descriptor) = @_;
1212 0         0 my $descriptor = _deserialize( $serialized_descriptor );
1213 0         0 @$descriptor{qw/run_to_cursor condition remove suspend/} = (1, undef, undef, \1);
1214 0         0 _process_breakpoints_descriptors( [ $descriptor ] );
1215             }
1216              
1217             sub _set_breakpoint
1218             {
1219 0     0   0 my ($breakpoint_descriptor, $real_line, $perl_breakpoints_map, $perl_source_lines) = @_;
1220              
1221             my $event_data = {
1222             path => $breakpoint_descriptor->{path},
1223 0         0 line => $breakpoint_descriptor->{line} - 1,
1224             };
1225              
1226 0 0 0     0 _report 'Setting breakpoint to %s, real line %s, %s', $breakpoint_descriptor->{path}, $real_line,
1227             $perl_source_lines->[$real_line] if $_dev_mode && $_debug_breakpoints;
1228              
1229 0         0 $breakpoint_descriptor->{_processed} = 1;
1230              
1231 0 0 0     0 if (!defined $perl_source_lines->[$real_line] || $perl_source_lines->[$real_line] == 0) {
1232 0         0 _send_event("BREAKPOINT_DENIED", $event_data);
1233             }
1234             else {
1235 0         0 $perl_breakpoints_map->{$real_line} = $breakpoint_descriptor;
1236 0 0       0 _send_event("BREAKPOINT_SET", $event_data) unless $breakpoint_descriptor->{run_to_cursor};
1237             }
1238             }
1239              
1240             sub _set_up_debugger
1241             {
1242 0     0   0 my ($json_data) = @_;
1243 0 0       0 _report 'Setting up debugger: %s', $json_data if $_dev_mode;
1244 0         0 my $set_up_data = _deserialize( $json_data );
1245 0         0 $_script_charset = $set_up_data->{charset};
1246 0         0 _process_breakpoints_descriptors( $set_up_data->{breakpoints} );
1247              
1248 0 0       0 $_enable_compile_time_breakpoints = 1 if $set_up_data->{enableCompileTimeBreakpoints};
1249 0 0       0 $_enable_noninteractive_mode = 1 if $set_up_data->{enableNonInteractiveMode};
1250              
1251 0 0       0 if (ref $set_up_data->{renderers} eq 'ARRAY') {
1252 0         0 for my $entry (@{$set_up_data->{renderers}}) {
  0         0  
1253 0 0       0 if (ref $entry ne 'HASH') {
1254 0         0 next;
1255             }
1256 0         0 my ($package, $code) = @$entry{qw/packageName renderExpression/};
1257 0 0 0     0 if (!$package || !$code) {
1258 0         0 next;
1259             }
1260 0         0 push @renderers, [ $package, $code ];
1261             }
1262             }
1263              
1264 0         0 my $start_mode = $set_up_data->{startMode};
1265              
1266 0 0       0 if ($set_up_data->{initCode})
1267             {
1268 0         0 eval $set_up_data->{initCode};
1269 0 0       0 die "*** Debugger init code error:\n$@" if $@;
1270             }
1271              
1272 0 0       0 if ($start_mode eq 'RUN')
    0          
1273             {
1274 0         0 return STEP_CONTINUE;
1275             }
1276             elsif ($start_mode eq 'COMPILE')
1277             {
1278 0         0 return STEP_INTO;
1279             }
1280             else # here we should have a RUN_TO_BREAKPOINT
1281             {
1282 0         0 $_skip_run_stop = 1;
1283 0         0 return STEP_CONTINUE;
1284             }
1285             }
1286              
1287             sub _set_up_after_connect
1288             {
1289 0     0   0 my ($allow_fail) = @_;
1290              
1291 0         0 $_debug_socket->autoflush( 1 );
1292 0         0 $_debug_socket_select = IO::Select->new();
1293 0         0 $_debug_socket_select->add( $_debug_socket );
1294              
1295 0         0 _send_data_to_debugger( +{
1296             event => 'READY',
1297             version => $API_VERSION,
1298             } );
1299 0 0       0 _report "Waiting for set up data..." if $_dev_mode;
1300 0         0 my $set_up_data = <$_debug_socket>;
1301 0 0 0     0 return if !defined $set_up_data && $allow_fail;
1302 0 0       0 die "Connection closed" unless defined $set_up_data;
1303              
1304 0         0 $ready_to_go = 1;
1305 0         0 $DB::single = _set_up_debugger( $set_up_data );
1306             }
1307              
1308             sub _process_new_breakpoints
1309             {
1310 0     0   0 my ($json_data) = @_;
1311 0 0       0 _report "Processing breakpoints: %s", $json_data if $_dev_mode;
1312 0         0 return _process_breakpoints_descriptors( _deserialize( $json_data ) );
1313             }
1314              
1315             sub _process_breakpoints_descriptors
1316             {
1317 0     0   0 my ($descriptors) = @_;
1318              
1319 0         0 foreach my $descriptor (@$descriptors)
1320             {
1321 0         0 $descriptor->{line}++;
1322 0         0 $descriptor->{condition} = _from_utf8( $descriptor->{condition} );
1323 0         0 $descriptor->{action} = _from_utf8( $descriptor->{action} );
1324              
1325             _report "Processing descriptor: %s %s %s", $descriptor->{path}, $descriptor->{line},
1326 0 0       0 $descriptor->{remove} ? 'remove' : 'set' if $_dev_mode;
    0          
1327              
1328 0         0 my ($real_path, $line) = @$descriptor{qw/path line/};
1329 0   0     0 $_loaded_breakpoints{$real_path} //= { };
1330 0         0 $_loaded_breakpoints{$real_path}->{$line} = $descriptor;
1331 0         0 $_queued_breakpoints_files{$real_path} = 1;
1332             }
1333 0 0       0 _set_break_points_for_files() if $ready_to_go;
1334             }
1335              
1336              
1337             sub _set_break_points_for_files
1338             {
1339 0 0   0   0 return unless $ready_to_go;
1340              
1341 0         0 my $paths_array = [ keys %_queued_breakpoints_files ];
1342 0 0       0 return unless @{$paths_array};
  0         0  
1343              
1344 0         0 my $default_context = undef;
1345              
1346 0         0 foreach my $real_path (@{$paths_array})
  0         0  
1347             {
1348 0 0       0 _report "Setting breakpoints for %s", $real_path if $_dev_mode;
1349              
1350             # mapping real path to file id
1351             my $perl_file_id = $real_path =~ /^\(eval \d+\)/
1352             ? $real_path
1353 0 0       0 : exists $_paths_to_perl_file_id_map{$real_path} ? $_paths_to_perl_file_id_map{$real_path} : next;
    0          
1354              
1355             # getting perl source lines and breakpoints
1356 0         0 my $glob = $::{"_<$perl_file_id"};
1357 0 0 0     0 next unless $glob && *{$glob}{ARRAY} && scalar @{*{$glob}{ARRAY}};
  0   0     0  
  0         0  
  0         0  
1358 0         0 my $perl_source_lines = *{$glob}{ARRAY};
  0         0  
1359 0         0 my $perl_breakpoints_map = *{$glob}{HASH};
  0         0  
1360              
1361             # getting breakpoints passed from the IDE
1362 0 0       0 my $loaded_breakpoints_descriptors = _get_loaded_breakpoints_by_real_path( $real_path ) or next;
1363              
1364             # switching context
1365 0         0 my $old_context = _switch_context( $perl_file_id );
1366 0   0     0 $default_context //= $old_context;
1367              
1368 0         0 my @lines = keys %{$loaded_breakpoints_descriptors};
  0         0  
1369 0         0 my $breakpoints_left = scalar @lines;
1370              
1371 0         0 foreach my $real_line (@lines) {
1372 0         0 my $breakpoint_descriptor = $loaded_breakpoints_descriptors->{$real_line};
1373              
1374 0 0       0 if (exists $breakpoint_descriptor->{_processed}) {
1375 0         0 $breakpoints_left--;
1376             _report "Breakpoint is already set: %s, %s, %s",
1377 0 0       0 @{$breakpoint_descriptor}{qw/path line remove/} if $_dev_mode;
  0         0  
1378 0         0 next;
1379             }
1380              
1381 0 0       0 if ($real_line > $#$perl_source_lines) {
1382             _report "Skip breakpoint setting, file seems not completely compiled. Breakpoint: %s, %s, %s, script lines: %s",
1383 0 0       0 @{$breakpoint_descriptor}{qw/path line remove/}, $#$perl_source_lines if $_dev_mode;
  0         0  
1384 0         0 next;
1385             }
1386 0 0       0 _report "Processing descriptor %s, %s, %s", @{$breakpoint_descriptor}{qw/path line remove/} if $_dev_mode;
  0         0  
1387              
1388 0 0       0 if ($breakpoint_descriptor->{remove}) {
1389 0         0 _reset_breakpoint($breakpoint_descriptor, $real_line, $perl_breakpoints_map);
1390             }
1391             else {
1392 0         0 _set_breakpoint($breakpoint_descriptor, $real_line, $perl_breakpoints_map, $perl_source_lines);
1393             }
1394 0         0 $breakpoints_left--;
1395             }
1396              
1397 0 0       0 delete $_queued_breakpoints_files{$real_path} unless $breakpoints_left;
1398             }
1399 0         0 _switch_context( $default_context );
1400             }
1401              
1402             #sub mydie
1403             #{
1404             # my ($msg) = @_;
1405             # print "$msg\n";
1406             # print Carp::longmess;
1407             # foreach my $key (sort keys %::)
1408             # {
1409             # print $key."\n" if $key =~ /^_
1410             # }
1411             # exit -1;
1412             #}
1413              
1414             sub _calc_real_path
1415             {
1416 143     143   198 my $path = shift;
1417 143         184 my $new_filename = shift;
1418              
1419 143         171 my $real_path;
1420 143 50       278 if ($path =~ /^\(eval (\d+)/)
1421             {
1422 0         0 $real_path = $path;
1423             }
1424             else
1425             {
1426 143         190 $real_path = eval {Cwd::realpath( $path )};
  143         7737  
1427 143 50       603 unless ($real_path)
1428             {
1429 0 0       0 _report 'Unable to find real path for %s use as it is', $path if $_dev_mode;
1430 0         0 $real_path = $path;
1431             }
1432 143         348 $real_path =~ s{\\}{/}g;
1433             }
1434              
1435 143 50 33     273 _report "$new_filename real path is $real_path\n" if $trace_real_path && $_dev_mode;
1436 143         375 return $real_path;
1437             }
1438              
1439             sub _switch_context
1440             {
1441 0     0     my ($context_key) = @_;
1442 0 0         return unless $context_key;
1443 0           $context_key =~ s/^_
1444 1     1   9 no strict 'refs';
  1         2  
  1         1059  
1445 0           my $current_context = $DB::dbline;
1446 0           *DB::dbline = *{"::_<$context_key"};
  0            
1447 0           return $current_context;
1448             }
1449              
1450              
1451             # When the execution of your program reaches a point that can hold a breakpoint, the DB::DB() subroutine is called if
1452             # any of the variables $DB::trace , $DB::single , or $DB::signal is true. These variables are not localizable. This
1453             # feature is disabled when executing inside DB::DB() , including functions called from it unless $^D & (1<<30) is true.
1454             sub step_handler
1455             {
1456 0 0 0 0 0   return if $_internal_process || !$ready_to_go;
1457 0           $_internal_process = 1;
1458              
1459             # Save eval failure, command failure, extended OS error, output field
1460             # separator, input record separator, output record separator and
1461             # the warning setting.
1462 0           @saved = ( $@, $!, $^E, $,, $/, $\, $^W );
1463              
1464 0           $, = ""; # output field separator is null string
1465 0           $/ = "\n"; # input record separator is newline
1466 0           $\ = ""; # output record separator is null string
1467 0           $^W = 0; # warnings are off
1468              
1469             # set breakpoints for evals if any appeared
1470 0 0         _set_break_points_for_files() if $ready_to_go;
1471              
1472             # updating current position
1473 0           my @caller = caller();
1474 0           ($current_package, $current_file_id, $current_line) = @caller[0, 1, 2];
1475              
1476 0 0         if (defined $current_file_id)
1477             {
1478 0 0 0       _report( <<'EOM',
1479             Calling %s %s
1480             EOM
1481             _format_caller( @caller ),
1482             ${^GLOBAL_PHASE} // 'unknown',
1483             ) if $_dev_mode;
1484 0           _switch_context( $current_file_id );
1485             }
1486             else
1487             {
1488 0 0 0       _dump_stack && _dump_frames && warn "CAN'T FIND CALLER;\n";
1489             }
1490              
1491 0           my $skip_event_handler = 0;
1492 0           my $breakpoint;
1493 0 0 0       if ($breakpoint = _get_current_breakpoint)
    0          
    0          
1494             {
1495 0           my $condition = $breakpoint->{condition};
1496              
1497 0 0 0       if ($condition && !_eval_expression( $condition )->{result})
1498             {
1499 0           ($@, $!, $^E, $,, $/, $\, $^W) = @saved;
1500 0           @saved = ();
1501 0           $_internal_process = 0;
1502 0           return;
1503             }
1504              
1505 0 0         if (my $action = $breakpoint->{action})
1506             {
1507 0           $breakpoint->{action_result} = _to_utf8( _eval_expression( $action )->{result} );
1508             }
1509              
1510 0 0         if (!$breakpoint->{suspend})
1511             {
1512 0           _send_breakpoint_reached_event( $breakpoint );
1513 0           ($@, $!, $^E, $,, $/, $\, $^W) = @saved;
1514 0           @saved = ();
1515 0           $_internal_process = 0;
1516 0           return;
1517             }
1518              
1519 0           foreach my $frame (@{$_stack_frames})
  0            
1520             {
1521 0           $frame->{single} = STEP_INTO;
1522             }
1523 0           $DB::single = STEP_INTO;
1524             }
1525             elsif ($DB::single && $_skip_run_stop)
1526             {
1527 0           $_skip_run_stop = 0;
1528 0           $skip_event_handler = 1;
1529 0           $DB::single = STEP_CONTINUE;
1530             }
1531             elsif ($_skip_run_stop)
1532             {
1533 0           $_skip_run_stop = 0;
1534             }
1535              
1536 0           my $old_db_single = $DB::single;
1537 0           $DB::single = STEP_CONTINUE;
1538              
1539 0 0 0       _report "Step with %s %s %s, %s-%s-%s %s",
      0        
      0        
      0        
      0        
      0        
1540             $current_package // 'undef',
1541             $current_file_id // 'undef',
1542             $current_line // 'undef',
1543             $DB::trace // 'undef',
1544             $DB::signal // 'undef',
1545             $old_db_single // 'undef',
1546             ${^GLOBAL_PHASE}
1547             if $_dev_mode;
1548              
1549 0 0         _report $DB::dbline[$current_line] if $_dev_mode;
1550 0 0         _event_handler( $breakpoint ) unless $skip_event_handler;
1551              
1552 0           $_internal_process = 0;
1553 0           ($@, $!, $^E, $,, $/, $\, $^W) = @saved;
1554 0           @saved = ();
1555 0           ();
1556             }
1557              
1558             #
1559             # This is a hook for templating engines working using perl evals.
1560             # This hook should be invoked after evaluation of compiled template with template path
1561             # and map of lines template_line => compiled_source_line
1562             #
1563             # {
1564             # no strict 'refs';
1565             # my $glob = *{'::DB::template_handler'};
1566             #
1567             # if ($glob && *{$glob}{CODE})
1568             # {
1569             # *{$glob}{CODE}->($filepath, $lines_map);
1570             # }
1571             # }
1572             #
1573             #
1574             sub template_handler
1575             {
1576 0     0 0   my ($real_path, $lines_map) = @_;
1577              
1578 0           my $last_eval_id = 0;
1579 0           my $eval_target;
1580 0           foreach my $main_key (keys %::)
1581             {
1582 0 0         if ($main_key =~ /^_<(\(eval (\d+)\).+?)$/)
1583             {
1584 0 0         if ($last_eval_id < $2)
1585             {
1586 0           $last_eval_id = $2;
1587 0           $eval_target = $1;
1588             }
1589             }
1590             }
1591              
1592 0 0         if ($last_eval_id)
1593             {
1594 0           $real_path = Cwd::realpath( $real_path );
1595              
1596 0           $_evals_to_templates_map{$eval_target} = {
1597             path => $real_path,
1598             lines_map => $lines_map
1599             };
1600 0   0       $_templates_to_evals_map{$real_path} //= {
1601             lines_map => $lines_map,
1602             evals => [ ]
1603             };
1604 0           push @{$_templates_to_evals_map{$real_path}->{evals}}, $eval_target;
  0            
1605              
1606 0           delete $_file_name_sent{$eval_target}; # forces re-sending file descriptor
1607 0 0         _report "Mapped template: %s to eval %s", $real_path, $eval_target if $_dev_mode;
1608              
1609 0           $_queued_breakpoints_files{$eval_target} = 1;
1610 0 0         _set_break_points_for_files() if $ready_to_go;
1611             }
1612             else
1613             {
1614 0 0         _report "Unable to locate top level eval for %s", $real_path if $_dev_mode;
1615             }
1616             }
1617              
1618              
1619             # this pass-through flag handles quotation overload loop
1620             sub sub_handler
1621             {
1622 0     0 0   my $stack_frame = undef;
1623              
1624 0           my $old_db_single = $DB::single;
1625 0           my $wantarray = wantarray;
1626              
1627 0 0         if (!$_internal_process)
1628             {
1629 0           $_internal_process = 1;
1630              
1631 0   0       _process_command( _get_next_command ) while $_enable_noninteractive_mode && _can_read;
1632 0           $old_db_single = $DB::single; # might be overriden in commands
1633              
1634 0           $DB::single = STEP_CONTINUE;
1635              
1636 0           $stack_frame = _enter_frame( $old_db_single, $wantarray );
1637              
1638 0 0 0       if ($current_package && $current_package eq 'DB')
1639             {
1640 0 0         _report "PANIC: Catched internal call" if $_dev_mode;
1641 0 0         _dump_stack && _dump_frames();
1642 0           die;
1643             }
1644              
1645 0           $DB::single = $old_db_single;
1646 0           $_internal_process = 0;
1647             }
1648              
1649 0           my $stack_pointer = $#$_stack_frames;
1650              
1651 0 0         if ($DB::single == STEP_OVER)
1652             {
1653             _report "Disabling step in in subcalls, will restore %s\n",
1654             $_stack_frames->[-1]->{single}
1655 0 0 0       if $_debug_sub_handler && $_dev_mode;
1656 0           $DB::single = STEP_CONTINUE;
1657              
1658             # my $die_handler = $SIG{__DIE__};
1659             # local $SIG{__DIE__} = sub{
1660             # _hold_the_line;
1661             # goto &$die_handler if $die_handler;
1662             # };
1663             }
1664             else
1665             {
1666 0 0 0       _report "Keeping step as %s\n", $old_db_single if $stack_frame && $_debug_sub_handler && $_dev_mode;
      0        
1667             }
1668              
1669 0 0 0       if ($DB::sub eq 'DESTROY' or substr( $DB::sub, -9 ) eq '::DESTROY' or !defined $wantarray)
    0 0        
1670             {
1671 1     1   8 no strict 'refs';
  1         2  
  1         109  
1672 0           &$DB::sub;
1673 0           $#$_stack_frames = $stack_pointer;
1674 0 0         if ($stack_frame)
1675             {
1676 0           _exit_frame();
1677             }
1678             else
1679             {
1680 0           $DB::single = $old_db_single;
1681             }
1682              
1683 0           $DB::ret = undef; # return value
1684             }
1685             elsif ($wantarray)
1686             {
1687 1     1   7 no strict 'refs';
  1         2  
  1         105  
1688 0           my @result = &$DB::sub;
1689 0           $#$_stack_frames = $stack_pointer;
1690 0 0         if ($stack_frame)
1691             {
1692 0           _exit_frame();
1693             }
1694             else
1695             {
1696 0           $DB::single = $old_db_single;
1697             }
1698 0           @DB::ret = @result; # return value
1699             }
1700             else
1701             {
1702 1     1   9 no strict 'refs';
  1         2  
  1         777  
1703 0           my $result = &$DB::sub;
1704 0           $#$_stack_frames = $stack_pointer;
1705 0 0         if ($stack_frame)
1706             {
1707 0           _exit_frame();
1708             }
1709             else
1710             {
1711 0           $DB::single = $old_db_single;
1712             }
1713 0           $DB::ret = $result; # return value
1714             }
1715             }
1716              
1717             # If the call is to an lvalue subroutine, and &DB::lsub is defined &DB::lsub (args) is called instead, otherwise
1718             # falling back to &DB::sub (args).
1719             #sub lsub_handler: lvalue
1720             #{
1721             # my $stack_frame = undef;
1722             #
1723             # my $old_db_single = $DB::single;
1724             # if (!$_internal_process)
1725             # {
1726             # $_internal_process = 1;
1727             #
1728             # $DB::single = STEP_CONTINUE;
1729             # $stack_frame = _enter_frame( $old_db_single );
1730             #
1731             # $DB::single = $old_db_single;
1732             # $_internal_process = 0;
1733             # }
1734             #
1735             # if ($DB::single == STEP_OVER)
1736             # {
1737             # _report "Disabling step in in subcalls\n" if $_dev_mode;
1738             # $DB::single = STEP_CONTINUE;
1739             # }
1740             # else
1741             # {
1742             # _report "Keeping step as %s\n", $old_db_single if $stack_frame if $_dev_mode;
1743             # }
1744             #
1745             # {
1746             # no strict 'refs';
1747             # my $result = &$DB::sub;
1748             # if ($stack_frame)
1749             # {
1750             # _exit_frame();
1751             # }
1752             # else
1753             # {
1754             # $DB::single = $old_db_single;
1755             # }
1756             # return $DB::ret = $result;
1757             # }
1758             #}
1759              
1760              
1761             # After each required file is compiled, but before it is executed, DB::postponed(*{"::_<$filename"}) is called if the
1762             # subroutine DB::postponed exists. Here, the $filename is the expanded name of the required file, as found in the values
1763             # of %INC.
1764             #
1765             # After each subroutine subname is compiled, the existence of $DB::postponed{subname} is checked. If this key exists,
1766             # DB::postponed(subname) is called if the DB::postponed subroutine also exists.
1767             sub load_handler
1768             {
1769 0     0 0   my $old_db_single = $DB::single;
1770 0           $DB::single = STEP_CONTINUE;
1771              
1772 0           my $old_internal_process = $_internal_process;
1773 0           $_internal_process = 1;
1774              
1775 0           my $perl_file_id = $_[0];
1776 0           my $real_path = _get_real_path_by_perl_file_id( $perl_file_id );
1777              
1778 0 0 0       _report "Loading module: %s => %s %s-%s-%s",
      0        
      0        
      0        
1779             $perl_file_id,
1780             $real_path,
1781             $DB::trace // 'undef',
1782             $DB::signal // 'undef',
1783             $old_db_single // 'undef',
1784             if $_debug_load_handler && $_dev_mode
1785             ;
1786              
1787 0 0         _set_break_points_for_files() if $ready_to_go;
1788              
1789 0           $_internal_process = $old_internal_process;
1790              
1791 0           $DB::single = $old_db_single;
1792             }
1793             # When execution of the program uses goto to enter a non-XS subroutine and the 0x80 bit is set in $^P , a call to
1794             # &DB::goto is made, with $DB::sub holding the name of the subroutine being entered.
1795             #$^P |= FLAG_REPORT_GOTO;
1796             #sub goto_handler
1797             #{
1798             # return if $_internal_process;
1799             # $_internal_process = 1;
1800             #
1801             # my $old_db_single = $DB::single;
1802             # $DB::single = STEP_CONTINUE;
1803             #
1804             # _report "Goto called%s from %s-%s-%s-%s",
1805             # scalar @_ ? ' with '.(join ',', @_) : '',
1806             # $DB::trace // 'undef',
1807             # $DB::signal // 'undef',
1808             # $old_db_single // 'undef',
1809             # ${^GLOBAL_PHASE} // 'unknown',
1810             # if $_dev_mode;
1811             # $DB::single = $old_db_single;
1812             # $_internal_process = 0;
1813             #}
1814              
1815             unless ($ENV{PERL5_DEBUG_ROLE} && $ENV{PERL5_DEBUG_HOST} && $ENV{PERL5_DEBUG_PORT})
1816             {
1817             printf STDERR <<'EOM', map $_ // 'undefined', @ENV{qw/PERL5_DEBUG_ROLE PERL5_DEBUG_HOST PERL5_DEBUG_PORT/};
1818             Can't start debugging session. In order to make it work, you should set up environment variables:
1819              
1820             PERL5_DEBUG_ROLE - set this to 'server' if you want to make Perl process act as a server, and to 'client' to make it connect to IDEA.
1821             PERL5_DEBUG_HOST - host to bind or connect, depending on role.
1822             PERL5_DEBUG_PORT - host to listen or connect, depending on role.
1823              
1824             Atm we've got:
1825             PERL5_DEBUG_ROLE=%s
1826             PERL5_DEBUG_HOST=%s
1827             PERL5_DEBUG_PORT=%s
1828             EOM
1829              
1830             exit;
1831             }
1832              
1833             my $_connect_at_start = exists $ENV{PERL5_DEBUG_AUTOSTART} ? $ENV{PERL5_DEBUG_AUTOSTART} : 1;
1834              
1835             sub is_connected
1836             {
1837 0     0 0   return !!$_debug_socket;
1838             }
1839              
1840             sub _connect
1841             {
1842 0     0     my $_perl5_debug_host = $ENV{PERL5_DEBUG_HOST};
1843 0           my $_perl5_debug_port = $ENV{PERL5_DEBUG_PORT};
1844              
1845             # ${^TAINT} will be truthy if taint mode is on.
1846 0 0         if (${^TAINT})
1847             {
1848             # The debugger will fail with "Insecure dependency in connect..."
1849             # if we do not untaint the host and port variables, so we do so here.
1850 0           ($_perl5_debug_host) = $_perl5_debug_host =~ /(.*)/;
1851 0           ($_perl5_debug_port) = $_perl5_debug_port =~ /(.*)/;
1852             }
1853              
1854 0           my ($attempts, $allow_fail) = @_;
1855             # http://perldoc.perl.org/perlipc.html#Sockets%3a-Client%2fServer-Communication
1856 0 0         if ($ENV{PERL5_DEBUG_ROLE} eq 'server')
1857             {
1858 0           printf STDERR "Listening for the IDE connection at %s:%s...\n", $_perl5_debug_host, $_perl5_debug_port;
1859 0   0       my $_server_socket = IO::Socket::INET->new(
1860             Listen => 1,
1861             LocalAddr => $_perl5_debug_host,
1862             LocalPort => $_perl5_debug_port,
1863             ReuseAddr => 1,
1864             Proto => 'tcp',
1865             ) || die "Error binding to ${_perl5_debug_host}:${_perl5_debug_port}";
1866 0           $_debug_packed_address = accept( $_debug_socket, $_server_socket );
1867             }
1868             else
1869             {
1870 0           foreach my $attempt (1 .. $attempts)
1871             {
1872 0           printf STDERR "($attempt)Connecting to the IDE from process %s at %s:%s...\n", $$, ${_perl5_debug_host},
1873             ${_perl5_debug_port};
1874 0           $_debug_socket = IO::Socket::INET->new(
1875             PeerAddr => $_perl5_debug_host,
1876             PeerPort => $_perl5_debug_port,
1877             ReuseAddr => 1,
1878             Proto => 'tcp',
1879             );
1880 0 0 0       last if $_debug_socket || $attempt == $attempts;
1881 0           sleep( 1 ); # this is kinda hacky
1882             }
1883 0 0 0       die "Error connecting to ${_perl5_debug_host}:${_perl5_debug_port}" if !$_debug_socket && !$allow_fail;
1884             }
1885 0 0         _set_up_after_connect( $allow_fail ) if $_debug_socket;
1886             }
1887              
1888             sub connect
1889             {
1890 0     0 0   _connect( 1, 1 );
1891             }
1892              
1893             sub disconnect
1894             {
1895 0 0   0 0   return unless is_connected();
1896 0           $_debug_socket->close();
1897 0           undef $_debug_socket_select;
1898 0           undef $_debug_socket;
1899 0           $ready_to_go = 0;
1900             }
1901              
1902             sub connect_or_reconnect
1903             {
1904 0 0   0 0   disconnect() if is_connected();
1905 0           _connect( 1, 1 );
1906             }
1907              
1908             # we want disable() to completely bypass the debugger (except for the parts
1909             # which are required for bookkeeping, like DB::postponed)
1910             #
1911             # setting %^P can disable DB::DB, but the only way to disable DB::sub is to
1912             # make sure *DB::sub{CODE} is undef, while keeping %DB::sub and $DB::sub
1913             # intact; the only way to do that is to save the glob slots we want to
1914             # preserve, undef the glob and then restore the slots
1915             #
1916             # DB::lsub and DB::goto are easier, because we don't need to preserve the
1917             # corresponding scalar/array/hash variables
1918             my (%_orig_db_sub, %_disabled_db_sub, $_orig_db_lsub);
1919              
1920             BEGIN
1921             {
1922             %_orig_db_sub = %_disabled_db_sub = map {
1923 1     1   5 ($_ => *DB::sub{$_}) x !!*DB::sub{$_}
  3         13  
1924             } qw(SCALAR ARRAY HASH);
1925 1         4 $_orig_db_sub{CODE} = \&sub_handler;
1926 1         371 $_orig_db_lsub = undef; # \&lsub_handler
1927             }
1928              
1929             sub enable
1930             {
1931 0     0 0   $^P = DEBUG_DEFAULT_FLAGS;
1932 0           undef *DB::sub;
1933 0           *DB::sub = $_orig_db_sub{$_} for keys %_orig_db_sub;
1934             #*DB::lsub = $_orig_db_lsub;
1935             #*DB::goto = \&goto_handler;
1936             }
1937              
1938             sub disable
1939             {
1940 0     0 0   $DB::single = 0;
1941 0           $^P = DEBUG_PREPARE_FLAGS;
1942 0           undef *DB::sub;
1943 0           undef *DB::lsub;
1944 0           undef *DB::goto;
1945 0           *DB::sub = $_disabled_db_sub{$_} for keys %_disabled_db_sub;
1946             }
1947              
1948             push @$_stack_frames, {
1949             subname => 'SCRIPT',
1950             file => $current_file_id,
1951             current_line => $current_line,
1952             single => STEP_INTO,
1953             };
1954              
1955             _dump_stack && _dump_frames if $trace_code_stack_and_frames;
1956              
1957             $_internal_process = 1;
1958              
1959             require Cwd;
1960             Cwd::getcwd();
1961             require B::Deparse;
1962             require JSON::XS;
1963              
1964             $frame_prefix = $frame_prefix_step;
1965              
1966             foreach my $main_key (keys %::)
1967             {
1968             if ($main_key =~ /_<(.+)/)
1969             {
1970             _get_real_path_by_normalized_perl_file_id( $1 );
1971             }
1972             }
1973              
1974             *DB::DB = \&step_handler;
1975             *DB::postponed = \&load_handler;
1976              
1977             if ($_connect_at_start)
1978             {
1979             _connect( 10, 0 );
1980             enable();
1981             }
1982              
1983             $_internal_process = 0;
1984              
1985             1; # End of Devel::Camelcadedb