Data::Dump::Streamer + $DB::single

schwern on 2008-01-19T00:28:31

I'm trying to track down a nasty problem involving the Class::DBI compatibility wrapper around DBIx::Class and Class::Trigger. I've got closures being created, and then stored somewhere, and then way later in the code those closures run. Oh, and all this is a bug that only happens near the end of a very specific, big wad of code.

Many, many layers, action at a long distance, lots of code to wade through until we hit the problem and those damned opaque closures.

I can't examine what's in the trigger queue because it's all closures, all the debugger or Data::Dumper will show me is "CODE(0x189fc3c)" and maybe line which generated it if I'm lucky. Getting in there with the debugger is a royal pain because every time I have to restart the process, set up the breakpoints... very time consuming and I have to do it over and over again as I slowly zero in on the problem.

There's a little known way to hard code breakpoints into the code. It's done by setting $DB::single. Usually you see it as "$DB::single = 1" but it's even less widely realized that it doesn't have to be 1. You can set it to any true value or expression! "$DB::single = $foo == 42" is "break when $foo is 42". Handy. After a lot of work I finally figured out the particular closure that was misfiring, it was being passed in the wrong arguments. I had to catch the point where that particular closure was being called to see how the arguments were being made. There's hundreds of them, so I can't just go through by hand. How do you identify a particular closure?

Enter Data::Dump::Streamer. By Yves Orton, this is Data::Dumper on steroids. Not satisfied with the limitations of Data::Dumper, Yves improved on it with a module that can serialize most everything you can throw at it. The edge cases most of us consider impossible, like closures. :)

use DDS;  # an alias for Data::Dump::Streamer

my $thing = 42; my $code = sub { print $thing };

print Dump $code;


my ($thing);
$thing = 42;
$CODE1 = sub {
           print $thing;
         };


Well now, let's combine the two. $DB::single and DDS.

use DDS;
$DB::single = grep /\Q$thing = 42\E/, Dump $code;


Now the code stops right before it runs just the trigger I want.