Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
 
Other projects
   Altirra

Archives

Blog Archive

_ReturnAddress() is my favorite compiler intrinsic

I'm sick with a cold at the moment, so I'll blog twice today. It'll somewhat make up for my not having blogged the entire month.

Visual C++ has a compiler intrinsic called _ReturnAddress() that gives the return address of the calling function. It exists back to at least VC6, but wasn't documented until VC7. This has become my favorite intrinsic, because you can do lots of evil debugging hacks with it, and it's very fast. There's a known issue with this intrinsic that if the function is inlined the intrinsic returns the return address above the inline site, but I've found that's actually sometimes desirable behavior.

Got a memory leak and the caller didn't provide file/line information when allocating? Overload operator new, call _malloc_dbg() with the return address as the line number, and track down the call site from the leak report. I use this in shutdown of debug builds of VirtualDub, along with DbgHelp, to print names of guilty functions that leaked memory without requiring specially tagged operator new() calls. I also lookup the first four bytes of the memory block to check if it's a virtual method table pointer, which then also gives the type of object leaked.

If you are doing COM-style (AddRef/Release) reference counting, you've probably discovered what a pain it can be to track down a leak from a missing Release() call. It used to be that I'd do this by capturing and manually matching the call stacks of every AddRef() and Release() on a particular class, a very slow and laborious technique. VS2005 makes this a little easier because you can sometimes use tracepoints with $CALLSTACK, but it's still slow. My new favorite technique is to maintain a global map of all AddRef() and Release() callers by _ReturnAddress() and compile call counts by calling site. You can then quickly eliminate the smart pointer code quickly — as long as the totals add up — and then determine which call sites are involved and who screwed up manual reference counting.

My latest exploit involving _ReturnAddress() was to deal with a chatty DLL that was spewing to Win32 debug output, making it unusable for anything I was trying to debug. Solution: Patch the export for OutputDebugString() at runtime and redirect it to a function that used _ReturnAddress() to look up the calling DLL. Now I can filter debug traces by DLL.

_ReturnAddress() quickly becomes inadequate for more complex scenarios because it only gives you one level of stack walk. That's often sufficient, though, and since a real stack walk is so much more expensive (and on x86, unreliable) I find it a useful shortcut.

Comments

This blog was originally open for comments when this entry was first posted, but was later closed and then removed due to spam and after a migration away from the original blog software. Unfortunately, it would have been a lot of work to reformat the comments to republish them. The author thanks everyone who posted comments and added to the discussion.