Painfully learned lessons
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.
No comments :
Post a Comment