The Perl Debugger
There is one more variation of the list code command, l. It is the ability to list the code of a subroutine, by typing l sub, where sub is the subroutine name.
Running the code in Listing 2 returns:
Loading DB routines from perl5db.pl version 1 Emacs support available. Enter h or h h for help. main::(./p2.pl:3): require 5.001; DB<1>
Entering l searchdir allows us to see the text of searchdir, which is the meat of this program.
22 sub searchdir { # takes directory as argument
23: my($dir) = @_;
24: my(@files, @subdirs);
25
26: opendir(DIR,$dir) or die "Can't open \"
27: $dir\" for reading: $!\n";
28
29: while(defined($_ = readdir(DIR))) {
30: /^\./ and next; # if file begins with '.', skip
31
32 ### SUBTLE HINT ###
As you can see, I left a subtle hint. The bug is
that I deleted an important line at this point.
If we were to step through every line of code in a subroutine that is supposed to be recursive, it would take all day. As I mentioned before, the code as in Listing 2 seems only to list the files in the current directory, and it ignores the files in any subdirectories. Since the code only prints the files in the current, initial directory, maybe the recursive calls aren't working. Invoke the Listing 2 code under the debugger.
Now, set a breakpoint. A breakpoint is a way to tell the debugger that we want normal execution of the program until it gets to a specific point in the code. To specify where the debugger should stop, we insert a breakpoint. In the Perl debugger, there there are two basic ways to insert a breakpoint. The first is by line number, with the syntax b linenum. If linenum is omitted, the breakpoint is inserted at the next line about to be executed. However, we can also specify breakpoints by subroutine, by typing b sub, where sub is the subroutine name. Both forms of breakpointing take an optional second argument, a Perl conditional. If when the flow of execution reached the breakpoint the conditional evaluates to true, the debugger will stop at the breakpoint; otherwise, it will continue. This gives greater control of execution.
For now we'll set a break at the searchdir subroutine with b searchdir. Once the breakpoint is set, we'll just execute until we hit the subroutine. To do this, enter c (for continue).
Looking at the code in Listing 2, we can see that the first call to searchdir comes in the main code. This seems to works fine, or else nothing would be printed out. Press c again to continue to the next invocation of searchdir, which occurs in the searchdir routine.
We wish to know what is in the $dir variable, which represents the directory that will be searched for files and subdirectories. Specifically, we want to know the contents of this variable each time we cycle through the code. We can do this by setting an action. By looking at the program listing, we see that by line 25, the variable $dir has been assigned. So, set an action at line 25 in this way:
a 25 print "dir is $dir\n"
Now, whenever line 25 comes around, the print command will be executed. Note that for the a command, the line number is optional and defaults to the next line to be executed.
Pressing c will execute the code until we come across a breakpoint, executing action points that are set along the way. In our example, pressing c continuously will yield the following:
main::(../p2.pl:3): require 5.001; DB<1> b searchdir DB<2> a 25 print "dir is $dir\n" DB<3> c main::searchdir(../p2.pl:23): my($dir) = @_; DB<3> c dir is . main::searchdir(../p2.pl:23): my($dir) = @_; DB<3> c dir is dir1.0 main::searchdir(../p2.pl:23): my($dir) = @_; DB<3> c dir is dir2.0 main::searchdir(../p2.pl:23): my($dir) = @_; DB<3> c dir is dir3.0 file1 file1 file1 file1 DB::fake::(/usr/lib/perl5/perl5db.pl:2043): 2043: "Debugged program terminated. Use `q' to quit or `R' to restart."; DB<3>
Note that older versions of the debugger don't output the last line as listed here, but instead exit the debugger. This newer version is nice because when the program has finished it still lets you have control so that you can restart the program.
It still seems that we aren't getting into any subdirectories. Enter D and A to clear all breakpoints and actions, respectively, and enter R to restart. Or, in older debugger versions, simply restart the program to begin again.
We now know that the searchdir subroutine isn't being called for any subdirectories except the first level ones. Looking back at the text of the program, notice in lines 44 through 46 that the only time the searchdir subroutine is called recursively is when there is something in the @subdirs list. Put an action at line 42 that will print the $dir and @subdirs variables by entering:
a 42 print "in $dir is @subdirs \n"
Now, put a breakpoint at line 12 to prevent the program from outputting to our screen (b 12), then enter c. This will tell us all the subdirectories that our program thinks are in the directory.
main::(../p2.pl:3): require 5.001;
DB<1> a 42 print "in $dir is @subdirs \n"
DB<2> b 12
DB<3> c
in . is dir1.0 dir2.0 dir3.0
in dir1.0 is
in dir2.0 is
in dir3.0 is
main::(../p2.pl:12): foreach (@files) {
DB<3>
This program sees that there are directories in “.”, but not in
any of the subdirectories within “.”. Since we are printing out
the value of @subdirs at line 42, we know that
@subdirs has no elements in it. (Notice that
when listing line 42, there is the letter “a” after the line
number and a colon. This tells us that there is an action point
here.) So, nothing is being assigned to @subdirs
in line 37, but should be if the current (as
held in $_) file is a directory. If it is, it
should be pushed into the @subdirs list. This is
not happening.
One error I've committed (intentionally, of course) is on line 38. There is no catch-all “else” statement. I should probably put an error statement here. Instead of doing this, let's put in another action point. Reinitialize the program so that all points are cleared and enter the following:
a 34 if( ! -f $_ and ! -d $_ ) { print "in $dir: $_ is
weird!\n" }
b 12"
c
which reveals:
main::(../p2.pl:3): require 5.001;
DB<1> a 34 if( ! -f $_ and ! -d $_ ) { print "in $dir:
$_ is weird!\n" }
DB<2> b 12
DB<3> c
in dir1.0: dir1.1 is weird!
in dir1.0: dir2.1 is weird!
in dir1.0: file2 is weird!
in dir1.0: file3 is weird!
in dir2.0: dir2.1 is weird!
in dir2.0: dir1.1 is weird!
in dir2.0: file2 is weird!
in dir2.0: file3 is weird!
main::(../p2.pl:12): foreach (@files) {
DB<3>
While the program can read (through the readdir
call on line 29) that dir1.1 is a file of some type in dir1.0, the
file test (the -f construct) on dir1.1 says that
it is not.
It would be nice to halt the execution at a point (line 34) where we have a problem. We can use the conditional breakpoint that I mentioned earlier to do this. Reinitialize or restart the debugger, and enter:
b 34 ( ! -f $_ and ! -d $_ ) c p p $dir
You'll get output that looks like this:
main::(../p2.pl:3): require 5.001;
DB<1> b 34 ( ! -f $_ and ! -d $_ )
DB<2> c
main::searchdir(../p2.pl:34): if( -f $_) { # if its a file...
DB<2> p
dir1.1
DB<2> p $dir
dir1.0
DB<3>
The first line sets the breakpoint, the next c
executes the program until the break point stops it. The
p prints the contents of the variable
$_ and the last command, p
$dir prints out $dir. So, dir1.1 is a
file in dir1.0, but the file tests (-d and
-f) don't admit that it exists, and therefore
dir1.1 is not being inserted into @subdirs (if
it's a directory) or into @files (if it's a
file).
Now that we are back at a prompt, we could inspect all sorts of variables, subroutines or any other Perl construct. To save you from banging your heads against your monitors, and thus saving both your heads and your monitors, I'll tell you what is wrong.
All programs have something known as the current working directory (CWD). By default, the CWD is the directory where the program starts. Any and all file accesses (such as file tests or file and directory openings) are made in reference from the CWD. At no time does our program change its CWD. But the values returned by the readdir call on line 29 are simply file names relative to the directory that readdir is reading (which is in $dir). So, when we do the readdir, $_ gets assigned a string representing a file (or directory) within the directory in $dir (which is why it's called a subdirectory). But when running the -f and -d file tests, they look for $_ in the context of the CWD. But it isn't in the CWD, it's in the directory represented by $dir. The moral of the story is that we should be working with $dir/$_, not just $_. So the string
###SUBTLE HINT###
should be replaced by
$_ = "$dir/$_"; # make all path names absoluteThat sums it up. Our problem was we were dealing with relative paths, not absolute (from the CWD) paths.
Putting it back into our example, we need to check dir1.0/dir1.1, not dir1.1. To check to make sure that this is what we want, we can put in another action point. Try typing:
a 34 $_ = "$dir/$_" c
In effect this temporarily places the corrective measure into our code. Action points are the first item on the line to be evaluated. You should now see the proper results of the execution of the program:
DB<1> a 34 $_ = "$dir/$_" DB<2> c ./file1 ./dir1.0/file1 ./dir1.0/file2 ./dir1.0/file3 ./dir1.0/dir1.1/file1 ./dir1.0/dir1.1/file2 ./dir1.0/dir1.1/file3 ./dir2.0/file1 ./dir2.0/file2 ./dir2.0/file3 ./dir2.0/dir2.1/file1 ./dir2.0/dir2.1/file2 ./dir3.0/file1 DB::fake::(/usr/lib/perl5/perl5db.pl:2043): 2043: "Debugged program terminated. Use `q' to quit or `R' to restart."; DB<2>
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Speed Up Your Web Site with Varnish | Jun 19, 2013 |
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
- Speed Up Your Web Site with Varnish
- Containers—Not Virtual Machines—Are the Future Cloud
- Linux Systems Administrator
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Non-Linux FOSS: libnotify, OS X Style
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- Web & UI Developer (JavaScript & j Query)
- RSS Feeds
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




1 hour 10 min ago
5 hours 10 min ago
6 hours 26 min ago
9 hours 57 min ago
12 hours 51 min ago
13 hours 17 min ago
15 hours 45 min ago
16 hours 18 min ago
16 hours 19 min ago
16 hours 20 min ago