or, for this, I wrote my car off.
As
I noted a while back, for the last few months I've been busy on a project
that's actually intended to be released. It was getting home from another
Saturday at work (the 13th consecutive working day for all those
trisdekaphobes out there) when I skidded in the sudden snow and slush and
bent the car gently against a telegraph pole. The obvious damage was confined
to the headlight, wing and wheel, but - given that it was over 10 years old,
and a rear-ending back in '96 - just after the 3 year old mark - came close to being a write-off - I wasn't
surprised when the garage told me it was a total loss.
What I'd been working on was a report of a memory leak detected in log-on
stress tests. And there were a couple of obvious ones - stateless objects in
.asp files that weren't being nulled, and would be better as globals
anyway.
Next round, running the most significant COM object directly, I learned
that CComBSTR
is the invention of the very Devil. If you expect
it to behave like std::string
or even CString
,
you'll almost certainly leak memory. Better to write your own wrapper and
count all the SysAllocString
s out and all the
SysFreeString
s in again, with SysReallocString
s and
any widening of data to append done under your own control. Run the direct
test harness again - after starting transients, from 2000 uses to 25000 uses,
memory and handles steady as a rock.
So I ran the program I'd coded using WinInet to drive a dumb web client,
coded to match the test harness script (that used some test software I didn't
have) to drive scripts plus COM objects. Steady as she goes. Hand over to
test.
Leaking like a sieve, they say. At this point I went home, tired,
perplexed and annoyed, to have the unexpected detour into a ditch.
Next lesson, Monday morning. My test harness was looping forever doing
create an HINTERNET
, create a session, use a number of HTTP
requests, tear it all down, and repeat; theirs was using scripting to do the
same set of GETs and POSTs. WTF!? I thought. Then I looked at a network
trace.
If you use Connection: Keep-Alive
in your requests, even if
you destroy every HINTERNET
and create then all anew, starting with a whole new Internet Session, the same
TCP connection is held underfoot. (I'd probably have had to unload and re-load wininet.dll
to force a connection close). So it was all within one ASP session, even though I
was running what I thought were many. So remove the for(;;)
and
loop in a script. Suddenly the leak shows.
The guy who wrote the scripts looks over my shoulder at this point and
asks "why are you calling this page with that argument?" - so I show him the
test script. "Are you sure?" he says. So I do the obvious - capture a network
trace with a real browser exercising the real server. And find that the
script does indeed depart from what a browser does - it misses out the final
crucially important GET-after-redirect to the page that winds up the session.
Adapt my test harness to match reality. Run it again. Steady as a rock. At
this point it is probably just as well that the test team are 70 miles away,
as I swear if one of them had been in the room, I would have bludgeoned them
to death with my chair. But as the Ash Wednesday Chopping
Block says, there are, alas, things that one can't validly give up for
Lent.