Happy New Year, all!

My current "spare-time" Perl divertisement has been the creation of a puzzle program in Perl/Tk, which mimics a real-life wooden puzzle shown to me by a coworker. Ultimately, the goal is to have the program "solve" the puzzle using recursion, but the first step has been achieved; namely being able to move the various pieces around the board.  (If I'm pleased with it, when it's finished I'll post it at Perlmonks).

Recently, while trying to solve the puzzle myself (and failing, thus far), it became clear after a large number of moves that the program had a memory leak somewhere -- it would become less and less responsive, and my initial guess was that one of the Tk Canvas objects was being incorrectly deleted.

That is to say, although the program attempted to $canvas->delete() every object created ($canvas->createLine(), $canvas->createOval(), etc.), there was very likely a location where the delete was not happening, so as to give the sluggish performance one sees when Perl/Tk is overloaded.

To aid me in the search for the offending code, I created the following block:

BEGIN { my $p_id_hash = { }; sub my_create { my ($w, $thing, $pparams) = @_; my $id = $w->$thing(@$pparams); my $pinfo = caller_info(); $p_id_hash->{$id} = [ $thing, @$pinfo ]; return $id; } sub caller_info { my $ln = (caller(1))[2]; my $sub = (caller(2))[3]; return [ $ln, $sub ]; } sub my_delete { my ($w, $id) = @_; my $pinfo = $p_id_hash->{$id} || 0; if (!$pinfo) { error_msg("Attempt to delete ID '$id' twice!"); } else { $w->delete($id); delete $p_id_hash->{$id}; return $id; } } sub show_id_hash { my @keys = sort keys %$p_id_hash; my $total = @keys; print "=" x 79, "\n"; for (my $i = 0; $i < $total; $i++) { my $key = $keys[$i]; my $pval = $p_id_hash->{$key}; my ($thing, $ln, $sub) = @$pval; printf "%3d. %16.16s -- %s[%4d]\n", $i+1, $thing, $sub, $ +ln; } print "Total keys = $total\n"; print "=" x 79, "\n"; } }

The subroutine my_create() gets called in place of normal canvas "create" subroutines, so instead of creating (and later deleting) a rectangle with:

my $id = $canvas->createRectangle(@opts); ... $canvas->delete($id);

the following code does the same thing:

my $id = my_create($canvas, "createRectangle", [ @opts ]); ... my_delete($canvas, $id);

but also keeps track of all objects created in the Canvas.   Additionally, an extra button labelled "Show ID Hash" calls show_id_hash() to dump out the table of currently allocated objects, to see whether if it's growing as suspected.

Perl provides the splendid caller functionality so useful for conjuring up stack traces, and, in this case, providing helpful context as to where my_create() and my_delete were called from.   Sure enough, each time a move is made, an extra 20 IDs are in the ID hash:

... 32. createText -- main::draw_coordinates[ 936] 33. createText -- main::draw_coordinates[ 936] 34. createText -- main::draw_coordinates[ 936] 35. createText -- main::draw_coordinates[ 936] 36. createText -- main::draw_coordinates[ 936] 37. createText -- main::draw_coordinates[ 936] 38. createText -- main::draw_coordinates[ 936] 39. createText -- main::draw_coordinates[ 936] 40. createText -- main::draw_coordinates[ 936] 41. createText -- main::draw_coordinates[ 936] 42. createText -- main::draw_coordinates[ 936] ...

And this makes sense, because in this particular puzzle, there are 20 squares which, when "coordinate mode" is on, each have a string written to them containing the (x,y) coordinate pair (mostly for debugging).   And -- no surprise -- the subroutine which writes this string is called draw_coordinates():

sub draw_coordinates { my ($pboard) = @_; # Delete existing coordinate text my $canvas = $pboard->{'canvas'}; my $ptext = $pboard->{'text'}; map { my_delete($canvas, $_) } @$ptext; $pboard->{'text'} = [ ]; for (my $i = 0; $i < $ncols; $i++) { my $x = $borderx + $i * $cellx + $i * $linex + $linex / 2; for (my $j = 0; $j < $nrows; $j++) { my $y = $bordery + $j * $celly + $j * $liney + $liney / 2; my @opts = ($x, $y, -text => coor_to_square([$i, $j])); push @opts, -anchor => 'nw'; ($coor_font || 0) and push @opts, -font => $coor_font; my $id = my_create($canvas, "createText", [@opts]); push @$ptext, $id; } } }

Now what I was trying to do in the above subroutine was get the hash containing the previously created text IDs:

my $ptext = $pboard->{'text'};

... delete all of them:

map { my_delete($canvas, $_) } @$ptext;

... reassign to an empty anonymous array:

$pboard->{'text'} = [ ];

... and then, in a loop, create the 20 new IDs, and save them for later deletion:

... my $id = my_create($canvas, "createText", [@opts]); push @$ptext, $id;

But there was a sneaky memory leak here!  Can you see it?

This debugging exercise reminded me that when writing Perl programs, half the fun is the "sleuthing" one does to find those latent, lurking bugs.

And Perl sure does give you all the tools to make it fun!


s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

In reply to Adventures in Debugging a Perl/Tk Game by liverpole

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.