Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Neat Debugger tricks

by osunderdog (Deacon)
on Nov 16, 2005 at 17:18 UTC ( [id://509118]=perlmeditation: print w/replies, xml ) Need Help??

Rightly or wrongly, I use the debugger. I use it all the time to step through code that I have inherited. Along the way I have run across a few debugger idioms that have been invaluable to my code archiology.

Please respond and share your debugger idioms!

  • Write intermediate data to a file
  • In some cases, while debugging, it is helpful to capture an intermediate data structure for reference. I find this particularly useful for dumping out xml content that is generated.

    DB<3> open FH, ">/tmp/foo.txt"; DB<4> print FH "THIS IS AN EXAMPLE"; DB<5> print FH $user_id; DB<6> close FH
  • Step into an expression
  • You can evaluate any valid perl expression with x while in the debugger. However you can also step into that expression using s. For example:

    DB<6> s $profile->to_xml() main::((eval 35)[/usr/lib/perl5/5.6.1/perl5db.pl:1521]:3): 3: $profile->to_xml(); DB<<7>>

    At this point, you will be stepping through the expression that was entered on the previous line.

  • break the debugger from code
  • Put the following expression somewhere in your code, run it in the debugger and the debugger will stop on the line immediately following it.

    $DB::single = 1;

There are many more debugger idioms out there, what are yours?

Hazah! I'm Employed! (But still looking...)

Replies are listed 'Best First'.
Re: Neat Debugger tricks (using the debugger to write tests)
by Ovid (Cardinal) on Nov 16, 2005 at 19:53 UTC

    If you really want to learn some awesome debugging tricks, check out this lazy testing presentation. The author explains a bit about how the debugger works internally and then shows how you run tests inside of the debugger and have a new test script written when you're done. It's very impressive.

    You can also script a debugging session. From perldebug:

    You can mock TTY input to debugger by adding arbitrary commands to @DB::typeahead. For example, your .perldb file might contain

      sub afterinit { push @DB::typeahead, "b 4", "b 6"; }

    Which would attempt to set breakpoints on lines 4 and 6 immediately after debugger initilization. Note that @DB::typeahead is not a supported interface and is subject to change in future releases.

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Neat Debugger tricks
by VSarkiss (Monsignor) on Nov 16, 2005 at 20:42 UTC

    Not so much a trick in the debugger, but I like to use it as an "interactive Perl shell", using a command like perl -de 1. Of course, any expression other than -e 1 will work, I'm just used to that one.

    For example, if you're wondering whether a particular regex will match, or whether your split will work as you intended, but you don't want to mess around with shell quotes, just:

    $ perl -de 1 Loading DB routines from perl5db.pl version 1.25 Editor support available. Enter h or `h h' for help, or `perldoc perldebug' for more help. main::(-e:1): 1 DB<1> $s = '12345' DB<2> x split(/(?<=\d)/, $s) 0 1 1 2 2 3 3 4 4 5 DB<3> q # yup, it works
    I believe I first read about this in Effective Perl Programming.

      I've always used "perl -de 0", but the too-clever version used to impress or confuse people is "perl -demo" :)

        I like perl -deal. Or, since we’re on PerlMonks, maybe perl -deacon?

        (Another classic, though it has nothing to do with the debugger, is perl -please as a fun if expensive replacement for cat.)

        Makeshifts last the longest.

Re: Neat Debugger tricks
by johnnywang (Priest) on Nov 16, 2005 at 20:09 UTC
    I've also been using the debugger quite often recently because of inherited code. The ones I use most:
    1. break at a line number: b 234
    2. break at a function : b foo
    3. simply change the value of a variable, e.g, the script is processing all files a directory, so it got a list at the beginning, I only want to look at a particular file, I can simply break after gettting the list, then change the list to contain that particular file.
    4. dump a data structure: use Data::Dumper; p Dumper($foo)
      4. dump a data structure: use Data::Dumper; p Dumper($foo)
      Unless you're just sweet on D::D, I prefer
      x $foo
      For hashes, this works better:
      x \%foo

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

        If the output from x (or p) is too long for your screen then prepend the pipe. The output will be piped through less (or whatever 'o pager' is set to) and you can scroll back and forth at your pleasure.
        harleypig@harleypig ~ $ perl -de 0 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::(-e:1): 0 DB<1> o pager? pager = '|/usr/bin/less' DB<2> |x \%::
        and the output is the same as 'x \%::' but in a less session.
        Harley J Pig

      Why use Data::Dumper? Do you know about the "x" command? Of course, for me, it isn't just the 1 character versus a few dozen; it is that the default format that Data::Dumper produces is just horribly awful and the format the "x" command uses is quite nice.

      I was about to hack into the debugger the ability to control how deep complex structures are dumped. Unfortunately, "hashDepth" already existed and has nothing to do with "depth", but instead controls the length of dump produced. Not everyone "gets" English. (: Luckily I see that someone solved this problem with "dumpDepth".</ramble>

      - tye        

        I'm betting he uses Data::Dumper instead of 'x' for the same reason I do: inertia. I've become so used to Dumper output that I can just glance at it and see what's going on. The output from 'x' is always a mental speedbump for me. I'm not saying it's bad, it's just not what I'm used to and I don't use the debugger often enough for this to be an issue.

        Cheers,
        Ovid

        New address of my CGI Course.

Re: Neat Debugger tricks
by graff (Chancellor) on Nov 17, 2005 at 01:50 UTC
    When I set a break point inside a loop and I want to track the value of one or more variables at each iteration, I find it saves a lot of time/tedium to use "< expression" -- like so:
    DB<1> b 123 DB<2> < print "## $var1 ## $var2 ## $whatever ##\n" DB<3> c 123: some_line_of_code(); ## var1_value ## thisisvar2 ## and so on ## DB<4>
    Naturally, you could put "print Dumper($whatever)" as well, or any other operation that might speed up the debugging inside a loop.
Re: Neat Debugger tricks
by friedo (Prior) on Nov 16, 2005 at 18:01 UTC
    Nice node, osunderdog++. Personally, I never use the debugger. But I must admit that part of the reason is I've always found the interface to be a real PITA. This is a good starting point and I may try it out.
Re: Neat Debugger tricks
by gaal (Parson) on Nov 17, 2005 at 08:00 UTC
    Something that's missing in the debugger is "set next statement", something that some c and Java debuggers have. And differential compilation, but that's probably even more difficult. Eg.

    # ... 107 sub foo { 108 my $x; 109 $x = 43; 110 => die "invalid x!" unless $x == 42; 111 print "Yay!" 112 # ...

    At this point, I notice I have a bug so I edit line 109 (the source code on disk changes):

    109 $x = 43; # When I hit "save", foo is recompiled. If line numbers # have changed, breakpoints are updated.

    And set the next instruction to it again. Then I hit s/n/c/whatever and keep going.

      I understand what you are trying to do...identify a bug, modify the code, and continue debugging with the modified code. That would be very nice.

      I think I can point out that you could at least change it for this instance and continue debugging. For example, usin g your code:

      # ... 107 sub foo { 108 my $x; 109 $x = 43; 110 => die "invalid x!" unless $x == 42; 111 print "Yay!" 112 # ...

      At this point you could set the value of $x before the unless expression is evaluated:

      DB<6> $x = 43;

      Of course, you would have to remember to make the same modification in code...

      As an interesting alternative, you could redefine the function on the fly... This wouldn't work in your example because it dies on a fail case. However after a first run through the function, cut the function from an editor and paste it into the debugger with the change you desire. Subsequent runs restarts through the debugger would have the modified version of the function.

      For example:

      use strict; foo(); foo(); sub foo { my $x; $x = 43; if($x==42){print "DANGER Will Robinson!\n" +}else{print "Yay!\n";}}

      Then I run this in the debugger... After the first execution of foo Cut and paste the modified subroutine in to the debugger and execute it. This redefines the foo function.

      main::(perldebugexample2.pl:3): foo(); DB<1> n Yay! main::(perldebugexample2.pl:4): foo(); <$x = 42; if($x==42){print "DANGER Will Robinson!\n"}else{print "Yay!\ +n";}} DB<2> n DANGER Will Robinson!

      I'm not sure if this is practicle, but it is interesting... :)

      Hazah! I'm Employed!

        I understand what you are trying to do...identify a bug, modify the code, and continue debugging with the modified code. That would be very nice.

        Yes, yes, yes, and yes :-)

        Microsoft Visual Studio does this, at least for c, and Eclipse does it for Java. These are languages where you'd expect these things to be waaaay harder to get away with than Perl. (Well, except for "set next instruction", which in c is relatively easy.)

Re: Neat Debugger tricks
by salva (Canon) on Nov 17, 2005 at 10:17 UTC
    the new assertion subs, available on blead perl and on 5.10 in the future, act as conditional breakpoints under the debugger.

    For example, this script uses an assertion to test that the argument passed to my_sqrt is a positive number:

    #!/usr/local/bin/perl5.9.3 use assertions '1'; # assrt. are always active use strict; use warnings; sub assert (&@) :assertion { my $sub = shift; &{$sub}() or die "assertion failed: @_" } sub my_sqrt { my $n = shift; assert { $n >=0 } "argument has to be a positive number"; return sqrt($n); } for my $n (0.3, 0, -4, 7, 9) { printf "sqrt(%f) = %f\n", $n, my_sqrt($n); }
    then running it under the debugger:
    toledo:~# perl5.9.3 -d /tmp/as.pl main::(/tmp/as.pl:19): for my $n (0.3, 0, -4, 7, 9) { DB<1> c sqrt(0.300000) = 0.547723 sqrt(0.000000) = 0.000000 assertion failed: argument has to be a positive number at /tmp/as.pl l +ine 10. main::my_sqrt(/tmp/as.pl:16): return sqrt($n); DB<1> l 16==> return sqrt($n); 17 } 18 19: for my $n (0.3, 0, -4, 7, 9) { 20: printf "sqrt(%f) = %f\n", $n, my_sqrt($n); 21 } 22 DB<1> p $n -4
Re: Neat Debugger tricks
by leriksen (Curate) on Nov 16, 2005 at 23:54 UTC
    One I use is to use Data::Dumper to display complex data structures whilst in the dbgr

    DB<6>use Data::Dumper DB<7>print Dumper($complexity)

    I think used in conjunction with the first point (opening a file handle in the dbgr) this could a neat way to capture an object or structure to a file at a point in time

    ...reality must take precedence over public relations, for nature cannot be fooled. - R P Feynmann

      Or, if your development environment supports dotty (the interactive dot environment from graphviz), you can install Devel::Command::Viz and get pictures of your data structures drawn with dot by GraphViz::Data::Structure.

      Disclaimer: I wrote both of these and for the two or three times I've really needed them, they really helped.

      Sometime I should tell the story of how wanting to do this got me into completely recommenting the debugger...

Re: Neat Debugger tricks
by bsb (Priest) on Nov 17, 2005 at 09:42 UTC
    I often have a dbg script for a project that sets up the environment and a few objects, saving me from doing that manually each time I want to try something out.
    #!/usr/bin/perl -d # Make the debugger run through to $DB::single=1 BEGIN { DB::parse_options("NonStop=1"); } # Setup environment to play around in use My::Module; $some_thing = My::Module->new(); $DB::single=1; $ending = "You're about to start destruction (press w)"; __END__

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://509118]
Approved by herveus
Front-paged by friedo
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2024-03-19 11:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found