Sample Debug Session Using The Perl Debugger

Catalyst's logging and debug output options usually give enough information to work out what is going wrong with your application. When they don't, you can use the perl debugger on script/myapp_server.pl (or similar) to step through the code manually, inspect the data and see what is happening.

What follows is an example of debugging a problem with Parley 0.07 that turned out to be an issue with DBIx::Class::Loader 0.10.

Start the debugger

# cd /var/www/catalyst/parley
# perl -d script/parley_server.pl -k

-k is needed to make Internet Explorer redirects work.

main::(script/parley_server.pl:14):
14:     my $debug         = 0;

Type 'c'ontinue and press RETURN

  DB<1> c
lots of messages from Catalyst

Browse to http://myserver:3000 and use the application. Try posting on the bulletin board screen and it fails. The debugger shows a full perl call stack on screen and you can see it fails in thread/view. The error message seems to indicate a prior db add failure, so that's where to look.

Add a breakpoint to parley's _add_new_topic() by editing /lib/Parley/Controller/Thread.pm and at line 137 adding

  $DB::single = 1;

just before the eval block where the new thread and post are created.

Now quit out of the debugger with 'q'uit then restart it to pick up the changed code.

<CTRL-C>
  DB<25> q
# perl -d script/parley_server.pl -k

Back to the browser and try posting again on the add bulletin board screen.

This time it breaks out to the debugger in _add_new_topic() and you can use 'n'ext to step over subroutine calls

  DB<5> n

until the $self->_update_last_post() line then 's'tep into the subroutine and walk through the code.

  DB<25> s

That goes through the following:

DBIx::Class::AccessGroup anonymous function
DBIx::Class::Row::get_column()
DBIx::Class::Relationship::Accessor anonymous function
DBIx::Class::Relationship::Base::set_from_related()

I type in 'v'iew to see where I am:

316:      my ($self, $rel, $f_obj) = @_;
  DB<1> v
313     314
315     sub set_from_related {
316==>    my ($self, $rel, $f_obj) = @_;
317:      my $rel_obj = $self->_relationships->{$rel};
318:      $self->throw( "No such relationship ${rel}" ) unless $rel_obj;
319:      my $cond = $rel_obj->{cond};
320:      $self->throw( "set_from_related can only handle a hash condition; the "
321         ."condition for $rel is of type ".(ref $cond ? ref $cond : 'plain scalar'))
322           unless ref $cond eq 'HASH';

I want to see a few more code lines so use 'l'ist:

  DB<2> l
323:      my $f_class = $self->resolve_class($rel_obj->{class});
324:      $self->throw( "Object $f_obj isn't a ".$f_class )
325         unless $f_obj->isa($f_class);
326:      foreach my $key (keys %$cond) {
327:        next if ref $cond->{$key}; # Skip literals and complex conditions
328:        $self->throw("set_from_related can't handle $key as key")
329           unless $key =~ m/^foreign\.([^\.]+)$/;
330:        my $val = $f_obj->get_column($1);
331:        $self->throw("set_from_related can't handle ".$cond->{$key}." as value")
332           unless $cond->{$key} =~ m/^self\.([^\.]+)$/;

Then I look at the params with e'x'amine:

  DB<1> x @_
0  Parley::Model::ParleyDB::Thread=HASH(0xbb9f0ac)
   '_column_data' => HASH(0xb55cde0)
      'active' => 1
      'created' => '2006-01-16 18:50:21.761284+00'
      'creator' => 1
      'forum' => 0
      'last_post' => undef
      'post_count' => 0
      'subject' => 'a2'
      'thread_id' => 20
      'view_count' => 0
   '_in_storage' => 1
   '_relationship_data' => HASH(0xbb9eec0)
      'forum' => Parley::Model::ParleyDB::Forum=HASH(0xbb9efbc)
         '_column_data' => HASH(0xbb9a738)
            'active' => 1
            'description' => 'General off-topic discussion'
            'forum_id' => 0
            'last_post' => 20
            'name' => 'Off-Topic'
            'post_count' => 0
         '_dirty_columns' => HASH(0xbba55b4)
            'last_post' => 1
         '_in_storage' => 1
         '_inflated_column' => HASH(0xbba5470)
              empty hash
1  'last_post'
2  20

And that value of 20 in param 2 is what will cause a throw() at line 325 because it's a scalar and not an object, so $f_obj->isa($f_class) is false and an exception is thrown.

The solution in this case meant contacting the module authors who investigated, found and corrected a bug.

For more information type

# perldoc perldebug

and read this article Using the Perl Debugger by Brian D. Foy.

Peter Edwards <peterdragon@users.sourceforge.net>

Emacs and cperl-mode users should know about cperl-db. In my installation of this (os x), I have two issues. First, I have to manually modify the command that cperl-mode suggests, changing "perl" to "perl -d" (this may be an aquaemacs/os x issue), and I also have to ensure that any paths to files in the application I am debugging are absolute not relative paths. Both issues have work arounds, and are almost certainly easily fixable. - kd

See Also

Testing for information about using test harnesses. The combination of Test Harness, and interactive debugger is very powerful.