|Ned Batchelder : Blog | Code | Text | Site|
Stack traces on windows
» Home : Blog : October 2005
For ages, we've had stack trace code in our product at work. I picked it up from a sample someplace, and it gave me a good feeling: I had a powerful diagnostic tool built into the product, and we could use it to pinpoint fleeting problems. It had only one flaw: it didn't work.
At least, it didn't work reliably. Sometimes, it would give a beautiful deep stack trace, complete with symbols and line numbers. Sometimes it would list only two functions, KiFastCallSomethingOrOther and DumpUserModeThingaMaJig.
My code used GetThreadContext to load up the program counter and frame pointer for the current thread. It looked something like this:
There seem to be lots of people out there advocating this method. But the docs for GetThreadContext say,
But that's just what I wanted: a stack trace for the current thread. It seemed like a lot of bother to spawn another thread just to suspend the current one, get a context, and restart it. And judging from my empirical data, it seemed like the docs were right: getting a context on the current thread didn't work too well.
Holy moly. In the words of a colleague, "If it uses inline assembly code, it's got to be good!". I tried out the code, and it worked really well, until I built a Release version, when it seemed to be worse than the old GetThreadContext code. I stepped through it, and read about stack frames, and discovered that the "ebp + 4" line should really be "esp + 4". After that change, the code worked perfectly.
But while I was researching the __asm keyword, I discovered a Microsoft built-in function: _ReturnAddress. Using this, I could get rid of some of the inline assembly language, including the bit that I had to fix:
Funny thing about _ReturnAddress: everyone seems to agree that it's designed for figuring out who's calling you so you can decide whether to trust them, and everyone also agress that's a really bad thing to try to do.
tagged: windows» 7 reactions