Tuesday, August 04, 2015

Debugging Perl Debugger: Part 2 - Variable Values Vindicated

In yesterday's post, I described how I got Perl debugging integrated with Notepad++. I had issues with watch variables only showing the variable name and type, not the value. I had source code from both the DBGp plugin and the Komodo Perl debugger. My only real option was to change the Perl debugger source as the plugin was written in a language I'm unfamiliar with and didn't have a compiler for.

The DBGp plugin offers a cool button labeled "DBG" which pops up a window with the raw XML messages sent between the debugger and the plugin. Like a packet capture trace file, this was a great place to start. Adding a watch variable and querying it's state showed the XML exchange and specifically where the value was returned:

...
<property name="$VERSION" fullname="$VERSION" encoding="base64" type="scalar" constant="0" children="0" size="25" >
    <value encoding="base64">
        <![CDATA[MS4wIC0gMjkgSlVMIDIwMTU=]]>
    </value>
</property>
...

So it was getting sent. Why couldn't the plugin read and display it?

I assumed it was a base64 encoding / decoding problem. I checked with base64.exe that the encoded data was correct:

C:\> echo MS4wIC0gMjkgSlVMIDIwMTU=| base64 -d
1.0 - 29 JUL 2015

It was - the Perl debugger was properly encoding and passing the value. Next stop was to find where in the DBGp code base64 was done.

I avoided the temptation to look in the file 'Base64.pas' without first grep-ing:

C:\> grep -il base64 *.pas
Base64.pas
DbgpWinSocket.pas

OK, fine, I looked in 'Base64.pas', but honestly, there wan't much there. I wasn't about to research the Base64 algorithm and how to code it in Delphi, so I assumed it worked correctly and moved on to 'DbgpWinSocket.pas'. Turned out to be a good move.

Searching for "base64" in 'DbgpWinSocket.pas' turned up a comment which looked to document the message it expected to receive:

...
<response xmlns="urn:debugger_protocol_v1" command="property_get" transaction_id="15" >
    <property name="2" fullname="$a[2]" address="-1215298680" type="string" size="3" encoding="base64">
        <![CDATA[ZGRk]]>
    </property>
...

It looked familiar, but where was the "<value ...></value>" tag surrounding "<![CDATA[...]]>" that the Perl debugger was sending? It couldn't be that simple ... could it?

The "command="property_get"" looked promising. I moved to the PerlDebug subdirectory I had created in the Notepad++ plugins directory for the Komodo IDE Perl debugger package. The "property_get" string was found in "perl5db.pl" and called a function "getPropertyInfo" before doing a bunch of error handling.

C:\> grep -lr getPropertyInfo *
DB/DbgrProperties.pm
perl5db.pl

I started with 'DB/DbgrProperties.pm' and searched for "value". Lots of results, but line 657 was the winner:

657:    $res .= sprintf(qq(<value%s><![CDATA[%s]]></value>\n),
658:            $encoding ? qq( encoding="$encoding") : "",
659:            $encVal);

That was the actual XML encoding that created the "<value ...></value>" tag surrounding "<![CDATA[...]]>". I changed those 3 lines to:

    $res .= sprintf(qq(<![CDATA[%s]]>\n), $encVal;

I restarted debugging and ... SUCCESS! I could now see watch variables' values too!

Everything wasn't perfect yet. I had to fix an annoyance about Global versus Local context. Again, the tag sent by "perl5db.pl" wasnt' recognized by the DBGp plugin, so a quick edit in 'DB/DbgrProperties.pm' again:

131:        context_id="%d"

changed to:

131:        context="%d"

did the trick! I experimented with the config options of the DBGp plugin now and the automated refresh for local and global worked much better.

Also, breakpoints worked, but the line number was always 0, so that was fixed with an update to "perl5db.pl":

1525:        $res .= sprintf(' line="%s"',

changed to:

1525:        $res .= sprintf(' lineno="%s"',

fixed the breakpoint line numbers. Additionally, breakpoint types other than 'line' were not being displayed. It looked like an omission in "perl5db.pl":

2897:        while (my ($bType, $bkptID) = each %$val) {
2898:            my $bpInfo = getBreakpointInfoString($bkptID, function => $bFunction);
2899:        }

while less than 10 lines earlier, the call to getBreakpointInfoString() for the 'line' breakpoints actually appends the answer to the return value. Simply inserting the same code here (lines 2899 - 2902 below) between the original lines 2898 and 2899 from above:

2897:        while (my ($bType, $bkptID) = each %$val) {
2898:            my $bpInfo = getBreakpointInfoString($bkptID, function => $bFunction);
2899:            if ($bpInfo) {
2901:                $res .= $bpInfo;
2902:            }
2903:        }

fixed the issue.

I hit a snag now and again on complicated programs, but this is a vast improvement over no integration at all. Ultimately, I proved that changes can be made so if I am sufficiently bothered by bad debug behavior, I can set out to fix it!

Now if I could only automate this from Notepad++. Wait a tick, I have NppExec installed - read tomorrow's post for how I did that.

No comments :

 

Copyright © VinsWorld. All Rights Reserved.