Perl Debugger - How to step out of a loop - perl

While using perl debugger, is there any way to step out of the current loop?
For example:
line 1
for($i=1;$i<100000:$i++)
{
line2
}
line3
I want the debugger to step out of this for loop and stop at line3

c 5
Demonstration:
>perl -d
Loading DB routines from perl5db.pl version 1.33
Editor support available.
Enter h or `h h' for help, or `perldoc perldebug' for more help.
print "line1\n";
for (1..100000) {
print "line2\n";
}
print "line3\n";
^Z
main::(-:1): print "line1\n";
DB<1> s
line1
main::(-:2): for (1..100000) {
DB<1> s
main::(-:3): print "line2\n";
DB<1> s
line2
main::(-:3): print "line2\n";
DB<1> c 5
line2
line2
line2
...
line2
line2
line2
main::(-:5): print "line3\n";
DB<2> s
line3
Debugged program terminated. Use q to quit or R to restart,

You can just set the loop termination condition:
$i=100000
Elaborate? Just set the variable to the exit condition like so:
DB<5> $i=1
DB<6> print $i
1
DB<7> $i=100000
DB<8> print $i
100000
DB<9> c
Debugged program terminated. Use q to quit or R to restart,

c 3 means continue execution and stop at line 3

There is no step out.
You can either setup a break point on "line 3" and continue "c" to next breakpoint, or explicitly state c <line #> to stop at a particular line.

Related

How can I set a working breakpoint to a constant expression?

I have Perl code that uses a constant with an initializing block like this:
use constant C => map {
...;
} (0..255);
When I try to set a breakpoint at the ...; line, it does not work, meaning: I can set the breakpoint, but the debugger does not stop there.
I tried:
Start the program with the debugger (perl -d program.pl)
Set the breakpoint in the debugger (b 2)
Reload using R, then run (r) the program
But still the debugger did not stop at the line, just as if I had no breakpoint set.
My Perl is not the latest; it's 5.18.2, just in case it matters...
You are trying to put a break point in a use block.
A use block is in effect a BEGIN block with a require in it.
The Perl debugger by default does not stop in compile phase.
However you can force the Perl debugger into single step mode inside a BEGIN block by setting the variable $DB::single to 1
See Debugging Compile-Time Statements in perldoc perldebug
If you change your code to
use constant C => map {
$DB::single = 1;
...;
} (0..255);
The Perl debugger will stop in the use statement.
You can avoid altering your code if you create a simple module like this (concept originated here):
package StopBegin;
BEGIN {
$DB::single=1;
}
1;
Then, run your code as
perl -I./ -MStopBegin -d test.pl
Pertinent Answer (previous, not-so-pertinent answer is below this one)
If test.pl looks like this:
use constant C => {
map {;
"C$_" => $_;
} 0 .. 255
};
here's what the debug interaction looks like:
% perl -I./ -MStopBegin -d test.pl
Loading DB routines from perl5db.pl version 1.53
Editor support available.
Enter h or 'h h' for help, or 'man perldebug' for more help.
StopBegin::CODE(0x55db6287dac0)(StopBegin.pm:8):
8: 1;
DB<1> s
main::CODE(0x55db6287db38)(test.pl:5):
5: };
DB<1> -
1 use constant C => {
2: map {;
3: "C$_" => $_;
4 } 0 .. 255
5==> };
DB<2> b 3
DB<3> c
main::CODE(0x55db6287db38)(test.pl:3):
3: "C$_" => $_;
DB<3>
Note the use of the breakpoint to stop inside the map.
Previous, Not-So-Pertinent Answer
If test.pl looks like this:
my $foo;
BEGIN {
$foo = 1;
};
here's what the debug interaction looks like:
% perl -I./ -MStopBegin -d test.pl
Loading DB routines from perl5db.pl version 1.53
Editor support available.
Enter h or 'h h' for help, or 'man perldebug' for more help.
StopBegin::CODE(0x5567e3d79a80)(StopBegin.pm:8):
8: 1;
DB<1> s
main::CODE(0x5567e40f0db0)(test.pl:4):
4: $foo = 1;
DB<1> s
main::(test.pl:1): my $foo;
DB<1> s
Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.
DB<1>
Note the use of the s command to advance, otherwise it'll skip over the BEGIN block in test.pl

sed insert multiple lines before multiple lines pattern

I have the following very long file:
...
close unit 1
...
...
close unit 1
...
...
close unit 1
stop
I want to insert multiples lines before the last close unit 1 which is before stop. The file contains an undefined number of close unit 1.
I found a lot of other similar questions here and there, but the answers couldn't help me... For example I tried https://stackoverflow.com/a/8635732/1689664 but this didn't work...
Using sed and tac:
$ tac inputfile | sed '/close unit 1/ {s/\(.*\)/\1\nLine3\nLine2\nLine1/; :loop; n; b loop}' | tac
...
close unit 1
...
...
close unit 1
...
...
Line1
Line2
Line3
close unit 1
stop
Note that you'd need to specify the input lines in the reverse order in the sed expression.
Perl solution:
perl -ne ' push #arr, $_;
print shift #arr if #arr > 3;
if ("stop\n" eq $_ and "close unit 1\n" eq $arr[0]) {
print "\n\n"; # Inserted lines
}
}{ print #arr ' long-file > new-file
It keeps a sliding window of last 3 lines, if the last line in the window is stop and the first one is close unit 1, it prints the lines.
Another possibility is to use nl to number the lines, then grep lines containing close unit 1, getting the number of the last such a line and using it in a sed address:
nl -ba long-file \
| grep -F 'close unit 1' \
| tail -n1 \
| ( read line junk
sed -e $line's/^/\n\n/' long-file > new-file
)
This might work for you (GNU sed):
sed '/close unit 1/,$!b;/close unit 1/{x;/./p;x;h;d};H;$!d;x;i\line 1\nline 2\n...' file
Print every line before the first occurrence of close unit 1 as normal. Store collections of lines beginning with close unit 1 in the hold space and print the previous collection before storing the next. At the end of the file, the last collection will still be in the hold space, so insert the lines required and then print the last collection.

perl - push not appending to the end of the array

DB<2> n
main::(/home/repsa/temper.pl:84): my $tttdiskhumber=$myTemprecord[-1];
DB<2> n
main::(/home/repsa/temper.pl:87): push(#myMainrecord,$tttdiskhumber);
DB<2> p #myMainrecord
t2agvio701vhost03t2adsap7011
DB<3> p $tttdiskhumber
hdisk6
DB<4> n
main::(/home/repsa/temper.pl:88): #myTemprecord=();
DB<4> p #myMainrecord
hdisk6o701vhost03t2adsap7011
DB<5>
Why my last push is not appending to the end of the array?
Any help is appreciated....
oh it is. The problem is that you're sending a carriage return to the screen. It's probably trailing the previous element in the array.
$ perl -e'print "abc", "def\r", "ghi", "\n";'
ghidef
You probably read a Windows text file on a non-Windows system without convert the line endings, either in advance (using dos2unix) or when you read the file (by using s/\s+\z//; instead of chomp;).
As jordanm suggested in a comment, the debugger's x command will show you what you have better than p.
$ perl -d
Loading DB routines from perl5db.pl version 1.33
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
my #a = ("abc", "def\r", "ghi");
1;
^D
main::(-:1): my #a = ("abc", "def\r", "ghi");
DB<1> s
main::(-:2): 1;
DB<1> p #a
ghidef
DB<2> x #a
0 'abc'
1 "def\cM"
2 'ghi'
DB<3> q

Reading between the lines (perl or awk)

Hey all, sorry for posting this here I could not find an answer anywhere and my solution is not working. I have a log file being written in the following fashion (don't ask):
=============================
04-12-2011 11:37:10 SOMETHING_GOES_HERE
Variable1
Something Goes Here
=============================
04-12-2011 11:37:20 SOMETHING_GOES_HERE
Variable2
Anything different may be here
=============================
04-12-2011 11:37:30 SOMETHING_GOES_HERE
Variable3
is altogether different here
=============================
What I'd like to do (either in perl or awk as this is an RTOS) is:
Take a look at the file, if Variable1 exists, then start at Variable2 and print everything between the equal signs:
E.g.
=============================
04-12-2011 11:37:10 SOMETHING_GOES_HERE
Variable1
Mary had a little lamb
=============================
04-12-2011 11:37:20 SOMETHING_GOES_HERE
Variable2
The cow jumped over the moon
=============================
awk '/Mary had/{getline;getline;getline;print}'
will only print
04-12-2011 11:37:20 SOMETHING_GOES_HERE
but what I need is everything between the equal signs. I tried butchering up a perl script which isn't working either. Any thoughts?
Alright, this worked (sort of)
#!perl -w
use strict;
my $LOGFILE = "/home/mydir/MyTestFile";
open my $fh, "<$LOGFILE" or die("could not open log file: $!");
my $in = 0;
while(<$fh>)
{
$in = 1 if /Variable1/i;
print if($in);
$in = 0 if /Variable2/i;
}
In the sense that a lot was stripped out. Now another q I have is selective printing a-la awk. Typically, I can get the line before something using:
echo "
test
hello
foo
bar" | awk '/foo/{print x};{x=$0}'
Will print test, however haven't found a way to get the word test (will always be a different word, but the word foo will always remain). Any takers (by the way many thanks in advance)
local $/ = "\n=============================\n";
while (<>) {
chomp;
...
}
Alternative:
my $rec = '';
while (<>) {
if (!/^=============================$/) {
$rec .= $_;
next;
}
...
$rec = '';
}
$/, chomp
I'm not totally clear on if "Variable1/2" etc are constant strings or actually varying quanitites, but does this work:
if [ $(grep Variable1 $file | wc -l) -gt 0 ]; then
sed -n '/Variable2/,/=======/' $file
fi

How do I move the instruction point in the Perl debugger?

I would like to be able to (reasonably) arbitrarily set my execution point in the Perl debugger. E.g., moving immediately prior to an if from the body of the if and setting a variable.
Rummaging around the perldebug(and perldebguts and the perl debugger POD) page suggests that this kind of functionality is either not supported or not documented.
A cumbersome workaround would be to add labels and conditional goto statements throughout your code. But depending on how badly you want to emulate this feature, it might be worth it.
POINT1: $GOTO=""; # $GOTO is our fake variable that we only set from the debugger
($a,$b,$c)=(1,2,3);
POINT2: $GOTO="";
if ($a < $b) {
goto $GOTO if $GOTO;
if ($a > $c) {
goto $GOTO if $GOTO;
print "foo\n";
} else {
goto $GOTO if $GOTO;
print "bar\n";
}
goto $GOTO if $GOTO;
} else {
goto $GOTO if $GOTO;
print "nothing\n";
goto $GOTO if $GOTO;
}
Sample debugging session:
$ perl -d debuggoto.pl
Loading DB routines from perl5db.pl version 1.28
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(debuggoto.pl:1): POINT1: $GOTO=""; # $GOTO is our fake variable that we only set from the debugger
DB<1> n
main::(debuggoto.pl:2): ($a,$b,$c)=(1,2,3);
DB<1>
main::(debuggoto.pl:3): POINT2: $GOTO="";
DB<1>
main::(debuggoto.pl:4): if ($a < $b) {
DB<1>
main::(debuggoto.pl:5): goto $GOTO if $GOTO;
DB<1>
main::(debuggoto.pl:6): if ($a > $c) {
DB<1>
main::(debuggoto.pl:10): goto $GOTO if $GOTO;
DB<1>
main::(debuggoto.pl:11): print "bar\n";
DB<1>
bar
main::(debuggoto.pl:13): goto $GOTO if $GOTO;
DB<1> $GOTO="POINT2"
DB<2> n
main::(debuggoto.pl:3): POINT2: $GOTO="";
DB<2> $c=0
DB<3> n
main::(debuggoto.pl:4): if ($a < $b) {
DB<3>
main::(debuggoto.pl:5): goto $GOTO if $GOTO;
DB<3>
main::(debuggoto.pl:6): if ($a > $c) {
DB<3>
main::(debuggoto.pl:7): goto $GOTO if $GOTO;
DB<3>
main::(debuggoto.pl:8): print "foo\n";
DB<3>
foo
main::(debuggoto.pl:13): goto $GOTO if $GOTO;
DB<3>
Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.
DB<3>
Use `q' to quit or `R' to restart. `h q' for details.
DB<3>
I wonder if it would be possible to build a debugger that uses this idea.
I am still not sure what exactly this would achieve, but I bet a custom runloop that skips ops you don't care about until you start caring again might solve your problem.
A good runloop to cargo-cult is Runops::Switch. Remove the switch statement and write a function that skips ops until you are on the one you want to run; then just call into the normal runloop to actually run that op.
Relevant code: http://cpansearch.perl.org/src/RGARCIA/Runops-Switch-0.04/Switch.xs
This is all handwaving, I have never written a runloop before. The goto idea in another post is also good, but this involves writing less code.
Breakpoints can only be set on the first line of a statement.
This can't be done with the existing debugger.