continue command ignores breakpoints - perl

I'm getting errors using CPAN and, as output is rather unhelpful, resorted to debugging this.
$ perl -d /usr/bin/cpan CPAN
Loading DB routines from perl5db.pl version 1.33
<...>
main::(/usr/bin/cpan:2): eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
main::(/usr/bin/cpan:3): if $running_under_some_shell;
DB<1> b /usr/lib/perl5/5.14/HTTP/Tiny.pm:125
DB<2> L
/usr/bin/cpan:
2: eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
break if (/usr/lib/perl5/5.14/HTTP/Tiny.pm:125)
DB<2> c
After c, the program runs to completion, ignoring the breakpoint.
/usr/lib/perl5/5.14/HTTP/Tiny.pm:125 is the following line, outside any conditional blocks.
my $request = {
method => $method,
scheme => $scheme,
host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
uri => $path_query,
headers => {},
};
Setting a break on line 142 (that's causing errors that I'm debugging) makes no difference.
From the L output, it looks like the breakpoint is set on the current line rather than the one I need. But, perldebug lists b file:line as valid syntax.

That syntax might be a newer addition, I wasn't able to get it working here either. I have 5.8.8 on Unix and 5.10.1 on Windows and both don't seem to work. I get the breakpoint on my current line as you do (it treats the whole string as a condition).
I'd suggest using another syntax as below.
Break on the method:
b HTTP::Tiny::some_method
Select the file, break on the line (it matches on paths, not module namespaces):
f HTTP/Tiny
b 125
If they are loaded at runtime with require or eval, the debugger won't know about the module yet so you can just type use HTTP::Tiny in the debugger first to force it to load.

Related

What is $^C in Perl?

I am trying to understand this code in Perl that I have inherited:
sub myfunc($)
{
my ($path) = #_;
unless ($^C)
{
# manipulate path
}
return $path;
}
But I do not understand the $^C test. What does it do?
Perl's -c switch compiles the source and tells you if the syntax is valid. However, Perl doesn't have a strict separation between compile-time and run-time. You can run some code during the compilation (BEGIN blocks), and you can compile code when you are running (eval()).
This means that you might think that you are safe while syntax checking, and that you would be wrong. The second example actually runs some code:
$ perl -c -le 'print "Hello!"'
-e syntax OK
$ perl -c -le 'BEGIN{print "Hello!"}'
Hello!
-e syntax OK
This is more important in IDEs. Many of these will continually run perl -c on your source so it can highlight problems. However, this also means that your IDE is now potentially a trojan horse for executing that code you just copied and pasted from Stackoverflow.
Thus, the $^C guard (see perlvar):
$ perl -c -le 'BEGIN{ print "Hello!" unless $^C }'
-e syntax OK
I'm guessing that the developer who put that there either had a bet to use all the special variables in one program, or was reacting to unwanted behavior from their tools.

Invoking Perl debugger so that it runs until first breakpoint

When I invoke my Perl debugger with perl -d myscript.pl, the debugger starts, but it does not execute any code until I press n (next) or c (continue).
Is there anyway to invoke the debugger and have it run through the code by default until it hits a breakpoint?
If so, is there any statement that I can use in my code as a breakpoint to have the debugger stop when it hits it?
Update:
Here is what I have in my .perldb file:
print "Reading ~/.perldb options.\n";
push #DB::typeahead, "c";
parse_options("NonStop=1");
Here is my hello_world.pl file:
use strict;
use warnings;
print "Hello world.\n";
$DB::single=1;
print "How are you?";
Here is the debugging session from running: perl -d hello_world.pl:
Reading ~/.perldb options.
Hello world
main::(hello_world.pl:6): print "How are you?";
auto(-1) DB<1> c
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> v
9563 9564
9565 sub at_exit {
9566==> "Debugged program terminated. Use `q' to quit or `R' to restart.";
9567 }
9568
9569 package DB; # Do not trace this 1; below!
DB<1>
In other words, my debugger skips print "How are you?", and instead stops once the program finishes, which is not what I want.
What I want is have the debugger run my code without stopping anywhere (nor at the beginning, nor at the end of my script), unless I explicitly have a $DB::single=1; statement, in which case I would like it to stop before running the next line. Any ways to do this?
For reference, I am using:
$perl --version
This is perl 5, version 14, subversion 1 (v5.14.1) built for x86_64-linux
Put
$DB::single = 1;
before any statement to set a permanent breakpoint in your code.
This works with compile-time code, too, and may be the only good way to set a breakpoint during the compile phase.
To have the debugger automatically start your code, you can manipulate the #DB::typeahead array in either a .perldb file or in a compile-time (BEGIN) block in your code. For example:
# .perldb file
push #DB::typeahead, "c";
or
BEGIN { push #DB::typeahead, "p 'Hello!'", "c" }
...
$DB::single = 1;
$x = want_to_stop_here();
There is also a "NonStop" option that you could set in .perldb or in the PERLDB_OPTS environment variable:
PERLDB_OPTS=NonStop perl -d myprogram.pl
All of this (and much more) is discussed deep in the bowels of perldebug and perl5db.pl
Update:
To address the issues raised in the most recent update. Use the following in ./perldb:
print "Reading ~/.perldb options.\n";
push #DB::typeahead, "c";
parse_options("inhibit_exit=0");
Also check out Enbugger. And while on the topic of debuggers, see Devel::Trepan which also works with Enbugger.

Perl ambiguous command line options, and security implications of eval with -i?

I know this is incorrect. I just want to know how perl parses this.
So, I'm playing around with perl, what I wanted was perl -ne what I typed was perl -ie the behavior was kind of interesting, and I'd like to know what happened.
$ echo 1 | perl -ie'next unless /g/i'
So perl Aborted (core dumped) on that. Reading perl --help I see -i takes an extension for backups.
-i[extension] edit <> files in place (makes backup if extension supplied)
For those that don't know -e is just eval. So I'm thinking one of three things could have happened either it was parsed as
perl -i -e'next unless /g/i' i gets undef, the rest goes as argument to e
perl -ie 'next unless /g/i' i gets the argument e, the rest is hanging like a file name
perl -i"-e'next unless /g/i'" whole thing as an argument to i
When I run
$ echo 1 | perl -i -e'next unless /g/i'
The program doesn't abort. This leads me to believe that 'next unless /g/i' is not being parsed as a literal argument to -e. Unambiguously the above would be parsed that way and it has a different result.
So what is it? Well playing around with a little more, I got
$ echo 1 | perl -ie'foo bar'
Unrecognized switch: -bar (-h will show valid options).
$ echo 1 | perl -ie'foo w w w'
... works fine guess it reads it as `perl -ie'foo' -w -w -w`
Playing around with the above, I try this...
$ echo 1 | perl -ie'foo e eval q[warn "bar"]'
bar at (eval 1) line 1.
Now I'm really confused.. So how is Perl parsing this? Lastly, it seems you can actually get a Perl eval command from within just -i. Does this have security implications?
$ perl -i'foo e eval "warn q[bar]" '
Quick answer
Shell quote-processing is collapsing and concatenating what it thinks is all one argument. Your invocation is equivalent to
$ perl '-ienext unless /g/i'
It aborts immediately because perl parses this argument as containing -u, which triggers a core dump where execution of your code would begin. This is an old feature that was once used for creating pseudo-executables, but it is vestigial in nature these days.
What appears to be a call to eval is the misparse of -e 'ss /g/i'.
First clue
B::Deparse can your friend, provided you happen to be running on a system without dump support.
$ echo 1 | perl -MO=Deparse,-p -ie'next unless /g/i'
dump is not supported.
BEGIN { $^I = "enext"; }
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
chomp($_);
(('ss' / 'g') / 'i');
}
So why does unle disappear? If you’re running Linux, you may not have even gotten as far as I did. The output above is from Perl on Cygwin, and the error about dump being unsupported is a clue.
Next clue
Of note from the perlrun documentation:
-u
This switch causes Perl to dump core after compiling your program. You can then in theory take this core dump and turn it into an executable file by using the undump program (not supplied). This speeds startup at the expense of some disk space (which you can minimize by stripping the executable). (Still, a "hello world" executable comes out to about 200K on my machine.) If you want to execute a portion of your program before dumping, use the dump operator instead. Note: availability of undump is platform specific and may not be available for a specific port of Perl.
Working hypothesis and confirmation
Perl’s argument processing sees the entire chunk as a single cluster of options because it begins with a dash. The -i option consumes the next word (enext), as we can see in the implementation for -i processing.
case 'i':
Safefree(PL_inplace);
[Cygwin-specific code elided -geb]
{
const char * const start = ++s;
while (*s && !isSPACE(*s))
++s;
PL_inplace = savepvn(start, s - start);
}
if (*s) {
++s;
if (*s == '-') /* Additional switches on #! line. */
s++;
}
return s;
For the backup file’s extension, the code above from perl.c consumes up to the first whitespace character or end-of-string, whichever is first. If characters remain, the first must be whitespace, then skip it, and if the next is a dash then skip it also. In Perl, you might write this logic as
if ($$s =~ s/i(\S+)(?:\s-)//) {
my $extension = $1;
return $extension;
}
Then, all of -u, -n, -l, and -e are valid Perl options, so argument processing eats them and leaves the nonsensical
ss /g/i
as the argument to -e, which perl parses as a series of divisions. But before execution can even begin, the archaic -u causes perl to dump core.
Unintended behavior
An even stranger bit is if you put two spaces between next and unless
$ perl -ie'next unless /g/i'
the program attempts to run. Back in the main option-processing loop we see
case '*':
case ' ':
while( *s == ' ' )
++s;
if (s[0] == '-') /* Additional switches on #! line. */
return s+1;
break;
The extra space terminates option parsing for that argument. Witness:
$ perl -ie'next nonsense -garbage --foo' -e die
Died at -e line 1.
but without the extra space we see
$ perl -ie'next nonsense -garbage --foo' -e die
Unrecognized switch: -onsense -garbage --foo (-h will show valid options).
With an extra space and dash, however,
$ perl -ie'next -unless /g/i'
dump is not supported.
Design motivation
As the comments indicate, the logic is there for the sake of harsh shebang (#!) line constraints, which perl does its best to work around.
Interpreter scripts
An interpreter script is a text file that has execute permission enabled and whose first line is of the form:
#! interpreter [optional-arg]
The interpreter must be a valid pathname for an executable which is not itself a script. If the filename argument of execve specifies an interpreter script, then interpreter will be invoked with the following arguments:
interpreter [optional-arg] filename arg...
where arg... is the series of words pointed to by the argv argument of execve.
For portable use, optional-arg should either be absent, or be specified as a single word (i.e., it should not contain white space) …
Three things to know:
'-x y' means -xy to Perl (for some arbitrary options "x" and "y").
-xy, as common for unix tools, is a "bundle" representing -x -y.
-i, like -e absorbs the rest of the argument. Unlike -e, it considers a space to be the end of the argument (as per #1 above).
That means
-ie'next unless /g/i'
which is just a fancy way of writing
'-ienext unless /g/i'
unbundles to
-ienext -u -n -l '-ess /g/i'
^^^^^ ^^^^^^^
---------- ----------
val for -i val for -e
perlrun documents -u as:
This switch causes Perl to dump core after compiling your program. You can then in theory take this core dump and turn it into an executable file by using the undump program (not supplied). This speeds startup at the expense of some disk space (which you can minimize by stripping the executable). (Still, a "hello world" executable comes out to about 200K on my machine.) If you want to execute a portion of your program before dumping, use the dump() operator instead. Note: availability of undump is platform specific and may not be available for a specific port of Perl.

How to set breakpoint on a particular file in Perl program?

My Perl program looks like:
foo/
foo/bar/
for/bar/test.pm
foo.pm
foo/bar.pm
test.pl
and use perl test.pl to start the program.
I want to debug a sub in foo/bar/test.pm. How to set a breakpoint on that sub?
To debug a perl script, use the -d switch to invoke the debugger.
perl -d test.pl
Within the debugger you can use b <line no> to set a breakpoint in the current file. Sometimes it is a hassle to set a breakpoint in a file that hasn't been loaded yet or that was loaded a long time ago, so you can also put the line
$DB::single = 1;
anywhere in any perl program, and the debugger will break immediately after it executes that line. This is also a good way (the only way?) to set a breakpoint in code that will be run at compile time.
Just use the fully qualified name of the sub as the argument to b:
b foo::bar::test::subname
Example:
$ perl -d -e'use CGI; CGI->new'
...
main::(-e:1): use CGI; CGI->new
DB<1> b CGI::new
DB<2> r
CGI::new(.../CGI.pm:337):
337: my($class,#initializer) = #_;
DB<2> q
You can enter f Module.pm to tell the debugger to look at that particular module. Once you've done that, b line_number will stop at that line in Module.pm.
Once the original script has passed use Module, then b subroutine will stop at that subroutine. The only catch here is that you can't make your first two debugger commands f Module.pm; b subroutine because when the script begins, it hasn't passed use Module, so Module.pm hasn't loaded yet, which means that perl can't see that Module.pm has subroutine.

How can I use Perl 5.10 features inside the debugger?

I am unable to evaluate 'modern Perl' code inside the Perl debugger. It works OK when debugging the code in a file, but not from the prompt.
Minimal example:
# Activating 5-10 features with -E (it works)
$ perl -E 'say "x"'
x
# Calling the debugger with -E
# It works for infile code, but for prompt line code...
$ perl -dEbug Loading DB routines from perl5db.pl version 1.33
DB say "x"
String found where operator expected at (eval 16)[/local-perl/lib/5.12.1/perl5db.pl:638] line 2, near "say "x""
at (eval 16)[/local-perl/lib/5.12.1/perl5db.pl:638] line 2
eval '($#, $!, $^E, $,, $/, $\\, $^W) = #saved;package main; $^D = $^D | $DB::db_stop;say "x";
(Note: the same happens with "use feature ':5.10'".)
Am I missing something?
I found a reference to the issue here, but it's about a year old. However, the relevant portion of the Perl source hasn't changed since and can be seen here. Essentially, if you take a look at toke.c in the Perl source, you see the following:
if (PL_perldb) {
/* Generate a string of Perl code to load the debugger.
* If PERL5DB is set, it will return the contents of that,
* otherwise a compile-time require of perl5db.pl. */
const char * const pdb = PerlEnv_getenv("PERL5DB");
...
}
...
if (PL_minus_E)
sv_catpvs(PL_linestr,
"use feature ':5." STRINGIFY(PERL_VERSION) "';");
Basically, the debugger is loaded before the -E flag is processed, so the features aren't yet enabled when the debugger gets loaded. The gist of this is that you can't currently use -E with the -d command. If you want to use say, switch, or any other feature from the debug prompt, you have to do it like this:
DB<1> use feature 'say'; say "x"
x
The closest I've seen to a solution is:
copy perl5db.pl from your PERL5LIB to either somewhere in PERL5LIB or the current directory, with a different name, say myperl5db.pl
2. Edit myperl5db.pl to have use feature ':5.10'; (or just 'state', or just 'say') on the first line.
3. Set the environment variable PERL5DB to "BEGIN { require 'myperl5db.pl' }"
Which I found at PerlMonks.