So, one of the big problems I’m having with kt3 is memory leaks. This is probably no surprise to anyone who has done XS programming before.
I found one particularly pernicious one by using Devel::Leak.. this is in the log handler.
Originally I had this, which I’m fairly sure I got from the internet somewhere:
static void _kt_log_callback(int iLogLevel, char *log_subsystem, char *msg)
{
dTHX;
dSP;
HV *rv = newHV();
sv_2mortal((SV *)rv);
hv_store(rv, "loglevel", 8, newSViv(iLogLevel),0);
hv_store(rv, "message", 7, newSVpv(msg, strnlen(msg, KT_LOGBUFSIZE)),0);
hv_store(rv, "system", 6, newSVpv(log_subsystem, strnlen(log_subsystem,32)),0);
PUSHMARK(SP);
XPUSHs(newRV_noinc((SV *)rv));
PUTBACK;
call_pv("KittenTrader::KittenBrain::Log", G_DISCARD);
}
This worked well enough – it created a hash ref and passed it to the function – but the hash ref kept leaking.
Eventually – after much digging through the documentation – I figured out that what I needed to do on the XPUSHs was this:
XPUSHs(sv_2mortal(newRV((SV *)rv)));
If anyone is curious how I debugged.. I used this relatively simple function to invoke the callback:
my $count = Devel::Leak::NoteSV($handle);
for($i=0;$i<1000;$i++) {
KittenTrader::KittenBrain::testLog(4, "THIS IS A MESSAGE");
}
my $count2 = Devel::Leak::NoteSV($handle);
Devel::Leak::CheckSV($handle);
print "Count: $count\n";
print "Count2: $count2\n";
This gave me a count of SVs, which I could clearly see my leaking SVs in.
Then I added returns everywhere along the path until I could isolate the leaking SV to a few lines of code. In particular I could definitely see that as soon as I created the hash, I was leaking a SV, but not if I destroyed it before passing it in. I realized the problem was that the callee didn't realize that the SV was mortal - I had made the hash mortal but not the *reference* to the hash. ANd of course the hash could never be destroyed until the reference to it was.