xref: /openbsd/gnu/usr.bin/perl/lib/perl5db.t (revision 898184e3)
1#!./perl
2
3BEGIN {
4    chdir 't' if -d 't';
5    @INC = '../lib';
6    require './test.pl';
7}
8
9use strict;
10use warnings;
11use Config;
12
13BEGIN {
14    if (! -c "/dev/null") {
15        print "1..0 # Skip: no /dev/null\n";
16        exit 0;
17    }
18
19    my $dev_tty = '/dev/tty';
20    $dev_tty = 'TT:' if ($^O eq 'VMS');
21    if (! -c $dev_tty) {
22        print "1..0 # Skip: no $dev_tty\n";
23        exit 0;
24    }
25    if ($ENV{PERL5DB}) {
26        print "1..0 # Skip: \$ENV{PERL5DB} is already set to '$ENV{PERL5DB}'\n";
27        exit 0;
28    }
29}
30
31plan(30);
32
33my $rc_filename = '.perldb';
34
35sub rc {
36    open my $rc_fh, '>', $rc_filename
37        or die $!;
38    print {$rc_fh} @_;
39    close ($rc_fh);
40
41    # overly permissive perms gives "Must not source insecure rcfile"
42    # and hangs at the DB(1> prompt
43    chmod 0644, $rc_filename;
44}
45
46sub _slurp
47{
48    my $filename = shift;
49
50    open my $in, '<', $filename
51        or die "Cannot open '$filename' for slurping - $!";
52
53    local $/;
54    my $contents = <$in>;
55
56    close($in);
57
58    return $contents;
59}
60
61my $out_fn = 'db.out';
62
63sub _out_contents
64{
65    return _slurp($out_fn);
66}
67
68{
69    my $target = '../lib/perl5db/t/eval-line-bug';
70
71    rc(
72        <<"EOF",
73    &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
74
75    sub afterinit {
76        push(\@DB::typeahead,
77            'b 23',
78            'n',
79            'n',
80            'n',
81            'c', # line 23
82            'n',
83            "p \\\@{'main::_<$target'}",
84            'q',
85        );
86    }
87EOF
88    );
89
90    {
91        local $ENV{PERLDB_OPTS} = "ReadLine=0";
92        runperl(switches => [ '-d' ], progfile => $target);
93    }
94}
95
96like(_out_contents(), qr/sub factorial/,
97    'The ${main::_<filename} variable in the debugger was not destroyed'
98);
99
100{
101    my $target = '../lib/perl5db/t/eval-line-bug';
102
103    rc(
104        <<"EOF",
105    &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
106
107    sub afterinit {
108        push(\@DB::typeahead,
109            'b 23',
110            'c',
111            '\$new_var = "Foo"',
112            'x "new_var = <\$new_var>\\n";',
113            'q',
114        );
115    }
116EOF
117    );
118
119    {
120        local $ENV{PERLDB_OPTS} = "ReadLine=0";
121        runperl(switches => [ '-d' ], progfile => $target);
122    }
123}
124
125like(_out_contents(), qr/new_var = <Foo>/,
126    "no strict 'vars' in evaluated lines.",
127);
128
129{
130    local $ENV{PERLDB_OPTS} = "ReadLine=0";
131    my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/lvalue-bug');
132    like($output, qr/foo is defined/, 'lvalue subs work in the debugger');
133}
134
135{
136    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
137    my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/symbol-table-bug');
138    like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table');
139}
140
141SKIP: {
142    if ( $Config{usethreads} ) {
143        skip('This perl has threads, skipping non-threaded debugger tests');
144    } else {
145        my $error = 'This Perl not built to support threads';
146        my $output = runperl( switches => [ '-dt' ], stderr => 1 );
147        like($output, qr/$error/, 'Perl debugger correctly complains that it was not built with threads');
148    }
149
150}
151SKIP: {
152    if ( $Config{usethreads} ) {
153        local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
154        my $output = runperl(switches => [ '-dt' ], progfile => '../lib/perl5db/t/symbol-table-bug');
155        like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table when running with thread support');
156    } else {
157        skip("This perl is not threaded, skipping threaded debugger tests");
158    }
159}
160
161
162# Test [perl #61222]
163{
164    local $ENV{PERLDB_OPTS};
165    rc(
166        <<'EOF',
167        &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
168
169        sub afterinit {
170            push(@DB::typeahead,
171                'm Pie',
172                'q',
173            );
174        }
175EOF
176    );
177
178    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-61222');
179    unlike(_out_contents(), qr/INCORRECT/, "[perl #61222]");
180}
181
182
183
184# Test for Proxy constants
185{
186    rc(
187        <<'EOF',
188
189&parse_options("NonStop=0 ReadLine=0 TTY=db.out LineInfo=db.out");
190
191sub afterinit {
192    push(@DB::typeahead,
193        'm main->s1',
194        'q',
195    );
196}
197
198EOF
199    );
200
201    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/proxy-constants');
202    is($output, "", "proxy constant subroutines");
203}
204
205# [perl #66110] Call a subroutine inside a regex
206{
207    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
208    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-66110');
209    like($output, "All tests successful.", "[perl #66110]");
210}
211
212# [perl 104168] level option for tracing
213{
214    rc(<<'EOF');
215&parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
216
217sub afterinit {
218    push (@DB::typeahead,
219    't 2',
220    'c',
221    'q',
222    );
223
224}
225EOF
226
227    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-104168');
228    my $contents = _out_contents();
229    like($contents, qr/level 2/, "[perl #104168]");
230    unlike($contents, qr/baz/, "[perl #104168]");
231}
232
233# taint tests
234
235{
236    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
237    my $output = runperl(switches => [ '-d', '-T' ], stderr => 1,
238        progfile => '../lib/perl5db/t/taint');
239    chomp $output if $^O eq 'VMS'; # newline guaranteed at EOF
240    is($output, '[$^X][done]', "taint");
241}
242
243package DebugWrap;
244
245sub new {
246    my $class = shift;
247
248    my $self = bless {}, $class;
249
250    $self->_init(@_);
251
252    return $self;
253}
254
255sub _cmds {
256    my $self = shift;
257
258    if (@_) {
259        $self->{_cmds} = shift;
260    }
261
262    return $self->{_cmds};
263}
264
265sub _prog {
266    my $self = shift;
267
268    if (@_) {
269        $self->{_prog} = shift;
270    }
271
272    return $self->{_prog};
273}
274
275sub _output {
276    my $self = shift;
277
278    if (@_) {
279        $self->{_output} = shift;
280    }
281
282    return $self->{_output};
283}
284
285sub _include_t
286{
287    my $self = shift;
288
289    if (@_)
290    {
291        $self->{_include_t} = shift;
292    }
293
294    return $self->{_include_t};
295}
296
297sub _contents
298{
299    my $self = shift;
300
301    if (@_)
302    {
303        $self->{_contents} = shift;
304    }
305
306    return $self->{_contents};
307}
308
309sub _init
310{
311    my ($self, $args) = @_;
312
313    my $cmds = $args->{cmds};
314
315    if (ref($cmds) ne 'ARRAY') {
316        die "cmds must be an array of commands.";
317    }
318
319    $self->_cmds($cmds);
320
321    my $prog = $args->{prog};
322
323    if (ref($prog) ne '' or !defined($prog)) {
324        die "prog should be a path to a program file.";
325    }
326
327    $self->_prog($prog);
328
329    $self->_include_t($args->{include_t} ? 1 : 0);
330
331    $self->_run();
332
333    return;
334}
335
336sub _quote
337{
338    my ($self, $str) = @_;
339
340    $str =~ s/(["\@\$\\])/\\$1/g;
341    $str =~ s/\n/\\n/g;
342    $str =~ s/\r/\\r/g;
343
344    return qq{"$str"};
345}
346
347sub _run {
348    my $self = shift;
349
350    my $rc = qq{&parse_options("NonStop=0 TTY=db.out LineInfo=db.out");\n};
351
352    $rc .= join('',
353        map { "$_\n"}
354        (q#sub afterinit {#,
355         q#push (@DB::typeahead,#,
356         (map { $self->_quote($_) . "," } @{$self->_cmds()}),
357         q#);#,
358         q#}#,
359        )
360    );
361
362    # I guess two objects like that cannot be used at the same time.
363    # Oh well.
364    ::rc($rc);
365
366    my $output =
367        ::runperl(
368            switches =>
369            [
370                '-d',
371                ($self->_include_t ? ('-I', '../lib/perl5db/t') : ())
372            ],
373            stderr => 1,
374            progfile => $self->_prog()
375        );
376
377    $self->_output($output);
378
379    $self->_contents(::_out_contents());
380
381    return;
382}
383
384sub output_like {
385    my ($self, $re, $msg) = @_;
386
387    local $::Level = $::Level + 1;
388    ::like($self->_output(), $re, $msg);
389}
390
391sub contents_like {
392    my ($self, $re, $msg) = @_;
393
394    local $::Level = $::Level + 1;
395    ::like($self->_contents(), $re, $msg);
396}
397
398package main;
399
400# Testing that we can set a line in the middle of the file.
401{
402    my $wrapper = DebugWrap->new(
403        {
404            cmds =>
405            [
406                'b ../lib/perl5db/t/MyModule.pm:12',
407                'c',
408                q/do { use IO::Handle; STDOUT->autoflush(1); print "Var=$var\n"; }/,
409                'c',
410                'q',
411            ],
412            include_t => 1,
413            prog => '../lib/perl5db/t/filename-line-breakpoint'
414        }
415    );
416
417    $wrapper->output_like(qr/
418        ^Var=Bar$
419            .*
420        ^In\ MyModule\.$
421            .*
422        ^In\ Main\ File\.$
423            .*
424        /msx,
425        "Can set breakpoint in a line in the middle of the file.");
426}
427
428# Testing that we can set a breakpoint
429{
430    my $wrapper = DebugWrap->new(
431        {
432            prog => '../lib/perl5db/t/breakpoint-bug',
433            cmds =>
434            [
435                'b 6',
436                'c',
437                q/do { use IO::Handle; STDOUT->autoflush(1); print "X={$x}\n"; }/,
438                'c',
439                'q',
440            ],
441        },
442    );
443
444    $wrapper->output_like(
445        qr/X=\{Two\}/msx,
446        "Can set breakpoint in a line."
447    );
448}
449
450# Testing that we can disable a breakpoint at a numeric line.
451{
452    my $wrapper = DebugWrap->new(
453        {
454            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
455            cmds =>
456            [
457                'b 7',
458                'b 11',
459                'disable 7',
460                'c',
461                q/print "X={$x}\n";/,
462                'c',
463                'q',
464            ],
465        }
466    );
467
468    $wrapper->output_like(qr/X=\{SecondVal\}/ms,
469        "Can set breakpoint in a line.");
470}
471
472# Testing that we can re-enable a breakpoint at a numeric line.
473{
474    my $wrapper = DebugWrap->new(
475        {
476            prog =>  '../lib/perl5db/t/disable-breakpoints-2',
477            cmds =>
478            [
479                'b 8',
480                'b 24',
481                'disable 24',
482                'c',
483                'enable 24',
484                'c',
485                q/print "X={$x}\n";/,
486                'c',
487                'q',
488            ],
489        },
490    );
491
492    $wrapper->output_like(
493        qr/
494        X=\{SecondValOneHundred\}
495        /msx,
496        "Can set breakpoint in a line."
497    );
498}
499# clean up.
500
501# Disable and enable for breakpoints on outer files.
502{
503    my $wrapper = DebugWrap->new(
504        {
505            cmds =>
506            [
507                'b 10',
508                'b ../lib/perl5db/t/EnableModule.pm:14',
509                'disable ../lib/perl5db/t/EnableModule.pm:14',
510                'c',
511                'enable ../lib/perl5db/t/EnableModule.pm:14',
512                'c',
513                q/print "X={$x}\n";/,
514                'c',
515                'q',
516            ],
517            prog =>  '../lib/perl5db/t/disable-breakpoints-3',
518            include_t => 1,
519        }
520    );
521
522    $wrapper->output_like(qr/
523        X=\{SecondValTwoHundred\}
524        /msx,
525        "Can set breakpoint in a line.");
526}
527
528# Testing that the prompt with the information appears.
529{
530    my $wrapper = DebugWrap->new(
531        {
532            cmds => ['q'],
533            prog => '../lib/perl5db/t/disable-breakpoints-1',
534        }
535    );
536
537    $wrapper->contents_like(qr/
538        ^main::\([^\)]*\bdisable-breakpoints-1:2\):\n
539        2:\s+my\ \$x\ =\ "One";\n
540        /msx,
541        "Prompt should display the first line of code.");
542}
543
544# Testing that R (restart) and "B *" work.
545{
546    my $wrapper = DebugWrap->new(
547        {
548            cmds =>
549            [
550                'b 13',
551                'c',
552                'B *',
553                'b 9',
554                'R',
555                'c',
556                q/print "X={$x};dummy={$dummy}\n";/,
557                'q',
558            ],
559            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
560        }
561    );
562
563    $wrapper->output_like(qr/
564        X=\{FirstVal\};dummy=\{1\}
565        /msx,
566        "Restart and delete all breakpoints work properly.");
567}
568
569{
570    my $wrapper = DebugWrap->new(
571        {
572            cmds =>
573            [
574                'c 15',
575                q/print "X={$x}\n";/,
576                'c',
577                'q',
578            ],
579            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
580        }
581    );
582
583    $wrapper->output_like(qr/
584        X=\{ThirdVal\}
585        /msx,
586        "'c line_num' is working properly.");
587}
588
589{
590    my $wrapper = DebugWrap->new(
591        {
592            cmds =>
593            [
594                'n',
595                'n',
596                'b . $exp > 200',
597                'c',
598                q/print "Exp={$exp}\n";/,
599                'q',
600            ],
601            prog => '../lib/perl5db/t/break-on-dot',
602        }
603    );
604
605    $wrapper->output_like(qr/
606        Exp=\{256\}
607        /msx,
608        "'b .' is working correctly.");
609}
610
611# Testing that the prompt with the information appears inside a subroutine call.
612# See https://rt.perl.org/rt3/Ticket/Display.html?id=104820
613{
614    my $wrapper = DebugWrap->new(
615        {
616            cmds =>
617            [
618                'c back',
619                'q',
620            ],
621            prog => '../lib/perl5db/t/with-subroutine',
622        }
623    );
624
625    $wrapper->contents_like(
626        qr/
627        ^main::back\([^\)\n]*\bwith-subroutine:15\):[\ \t]*\n
628        ^15:\s*print\ "hello\ back\\n";
629        /msx,
630        "Prompt should display the line of code inside a subroutine.");
631}
632
633# Checking that the p command works.
634{
635    my $wrapper = DebugWrap->new(
636        {
637            cmds =>
638            [
639                'p "<<<" . (4*6) . ">>>"',
640                'q',
641            ],
642            prog => '../lib/perl5db/t/with-subroutine',
643        }
644    );
645
646    $wrapper->contents_like(
647        qr/<<<24>>>/,
648        "p command works.");
649}
650
651# Tests for x.
652{
653    my $wrapper = DebugWrap->new(
654        {
655            cmds =>
656            [
657                q/x {500 => 600}/,
658                'q',
659            ],
660            prog => '../lib/perl5db/t/with-subroutine',
661        }
662    );
663
664    $wrapper->contents_like(
665        # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
666        qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/ms,
667        "x command test."
668    );
669}
670
671# Tests for "T" (stack trace).
672{
673    my $prog_fn = '../lib/perl5db/t/rt-104168';
674    my $wrapper = DebugWrap->new(
675        {
676            prog => $prog_fn,
677            cmds =>
678            [
679                'c baz',
680                'T',
681                'q',
682            ],
683        }
684    );
685    my $re_text = join('',
686        map {
687        sprintf(
688            "%s = %s\\(\\) called from file " .
689            "'" . quotemeta($prog_fn) . "' line %s\\n",
690            (map { quotemeta($_) } @$_)
691            )
692        }
693        (
694            ['.', 'main::baz', 14,],
695            ['.', 'main::bar', 9,],
696            ['.', 'main::foo', 6]
697        )
698    );
699    $wrapper->contents_like(
700        # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
701        qr/^$re_text/ms,
702        "T command test."
703    );
704}
705
706# Test for s.
707{
708    my $wrapper = DebugWrap->new(
709        {
710            cmds =>
711            [
712                'b 9',
713                'c',
714                's',
715                q/print "X={$x};dummy={$dummy}\n";/,
716                'q',
717            ],
718            prog => '../lib/perl5db/t/disable-breakpoints-1'
719        }
720    );
721
722    $wrapper->output_like(qr/
723        X=\{SecondVal\};dummy=\{1\}
724        /msx,
725        'test for s - single step',
726    );
727}
728
729{
730    my $wrapper = DebugWrap->new(
731        {
732            cmds =>
733            [
734                'n',
735                'n',
736                'b . $exp > 200',
737                'c',
738                q/print "Exp={$exp}\n";/,
739                'q',
740            ],
741            prog => '../lib/perl5db/t/break-on-dot'
742        }
743    );
744
745    $wrapper->output_like(qr/
746        Exp=\{256\}
747        /msx,
748        "'b .' is working correctly.");
749}
750
751{
752    my $prog_fn = '../lib/perl5db/t/rt-104168';
753    my $wrapper = DebugWrap->new(
754        {
755            cmds =>
756            [
757                's',
758                'q',
759            ],
760            prog => $prog_fn,
761        }
762    );
763
764    $wrapper->contents_like(
765        qr/
766        ^main::foo\([^\)\n]*\brt-104168:9\):[\ \t]*\n
767        ^9:\s*bar\(\);
768        /msx,
769        'Test for the s command.',
770    );
771}
772
773{
774    my $wrapper = DebugWrap->new(
775        {
776            cmds =>
777            [
778                's uncalled_subroutine()',
779                'c',
780                'q',
781            ],
782
783            prog => '../lib/perl5db/t/uncalled-subroutine'}
784    );
785
786    $wrapper->output_like(
787        qr/<1,2,3,4,5>\n/,
788        'uncalled_subroutine was called after s EXPR()',
789        );
790}
791
792{
793    my $wrapper = DebugWrap->new(
794        {
795            cmds =>
796            [
797                'n uncalled_subroutine()',
798                'c',
799                'q',
800            ],
801            prog => '../lib/perl5db/t/uncalled-subroutine',
802        }
803    );
804
805    $wrapper->output_like(
806        qr/<1,2,3,4,5>\n/,
807        'uncalled_subroutine was called after n EXPR()',
808        );
809}
810
811END {
812    1 while unlink ($rc_filename, $out_fn);
813}
814