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