You would benefit from converting your script to a modulino.
The problem you are experiencing is that foo() is called directly as soon as Perl enters runtime. But this is not the behavior you desire from a debugging perspective.
The purpose of a modulino is to allow the bulk of the code to be treated as an embedded module, and to allow the startup or kickoff code to only run if the script is run directly, but to not be run if the script is instead loaded as a module. By doing this you're able to write a test suite against the modulino code, leaving only minimal kickoff code outside the reach of unit tests.
One of several writeups on this topic exists here: https://perlmaven.com/modulino-both-script-and-module
For your purposes, the modulino lets you be more selective about what you run with the Perl debugger.
If you structured your code as follows, you would be more successful:
package MyModulino;
use parent 'Exporter';
our @EXPORT = qw(foo bar);
sub foo {
print "Hello world\n";
# enable debugger from this moment with PERLDB_OPTS='NonStop frame=1'
bar();
# disable debugger from this moment
return;
}
sub bar {
print "Just another Perl hacker\n";
return;
}
package main;
if(!caller) {
MyModulino->import();
foo();
}
Now you can do this:
perl -I/some/path/to/script -e 'do "scriptname"; MyModulino::bar()'
In other words, you can now treat scriptname, whatever it is, as a module with a package named MyModulino, and you can call bar() directly rather than relying on foo() to call it. The call to foo() only happens if caller() returns false, which it will do if you run the program directly, but will not do if you invoke it as a module.
By treating scriptname as a module, the foo() call doesn't run implicitly, and your debugger code can invoke bar() from the MyModulino package.
This one liner should drop you into the debugger without having called foo().
perl -d -I/path/to/script -e 'do "scriptname"; MyModulino::bar()'
You can combine this technique along with Test::MockModule to mock the portions of the call stack you don't care about when testing bar() also. But when you start going down that path you really should invest the time to implement these strategies in an automated test script. The 'modulino' advice still applies in that case.