Skip to main content
gamedev.net
🔒 Locked

Call Stack information and exceptions

Started by Shannon Barber Jan 12, 2002 at 2:36 PM 13 replies 1.2k views
Original Post
Shannon Barber
Shannon Barber
I''m kind of a n00b with exception handling, and I was wondering how you get call-stack information - like in Unreal when it crashed they dumped the call-stack. How they do?
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Kylotan
Kylotan
(edit: ignore what was here previously: it made no sense cos I misread something).

I think they use macros at the start and end of each function: the first macro basically starts a try block, and the last macro is a catch block that outputs the function name before rethrowing the exception. Or something like that.

Edited by - Kylotan on January 12, 2002 3:45:36 PM
Shannon Barber
Shannon Barber
So there''s no automated way to do this? You need to catch/rethrow in everywhere you want call-stack information?
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Brannon
Brannon
Under Win32, you can use the DBGHELP functions to walk the stack (ie. build a stack backtrace like in the debugger). If you have symbol files with your executable (even just the public symbols), you can use those to resolve function names, etc on the stack (and sometimes provide parameter info too).

If you have used windbg (from the Windows DDK/SDK), then you''re probably already familiar with this (the ''kb'' command in windbg generates the back trace).


-Brannon
-Brannon
Shannon Barber
Shannon Barber
I''ve used wind bag from time to time, but am not familar with every thing it can do.

Cool thanks Brannon, I''ll read up on using sysmbol files and minidumps - it should be possible to pin-point the line of your source a crash occured at and do a call-stack trace to show the execution in a release build using the symbol file generated by the compiler...

The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
LessBread
LessBread
Check out the MSDN "Under the Hood" articles from April and May of 1997 - the April article shows how to catch unhandled exceptions and walk the stack backwards from there - the May article shows how to use the ImageHelp.dll to correlate the stackwalk info with the debug info. Because Imagehlp.dll has been superceded by dbghelp.dll - you''ll have to put a little work into using the tricks from the second article.

‘But truth's a menace, science a public danger.’ Brave New World, Aldous Huxley
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Houdini
Houdini
I basically agree with Kylotan, that I think they use macros to do it. However, you don''t need to use exception handling for it to work.

You could create Singleton a StackHistory class that is basically a LIFO container to keep track of stack information. Then create a StackEntry class that takes a string (the class/function) as it''s constructor parameter. This class will automatcially communicate with StackHistory to push this new entry in the constructor, and pop this entry in the destructor.

Since the destructor gets called automatically when the function exits then you only need one macro at the start of the function, and no ending macros, which would be a pain since many functions have multiple exit points.

Although you don''t need a macro, it''s very useful here because it allows you to automatically record the file/line number with the class/function name, as well as allowing you to turn off the stack tracing by redefining the macro.

BTW, this stack tracing approach also makes it very easy to add code that times each function, thus giving you your own profiler.

The reason I like this approach so much better than DBGHELP is because it''s platform independant, which is very important to me.


- Houdini
Houdini
Shannon Barber
Shannon Barber
I have this funny feeling in my gut that tells me MiniDumpCallStack is approx. 10000000000000000000000000000000% faster than the daisy-chained exception/linked-list methods
Still, I wonder how they did it with Unreal ''cause the dialog box that pops up shows the whole decorated call-stack right in front of you.

The Dump method has the _added_ advantage that it requires the symbol file - this way clients can have automated error reporting, but they don''t have the symbols there to hack it (like they would with the ensueing string table the other methods would create). Also it could show the user what it''s sending, and it''ll look like a core-dump, like random crap to the untrained eye. But you have a nice little program that reads the dump and your symbol file and out pops:

printf("The buggy pos whacked the meat on line %l in file %s. Last editted by %s on %s.", iLine, szFile, szWhoDoneIt, szWhen);

And the rest of the call-stack ensues...
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Void
Void
The dbghelp library doesn''t give very accurate results in release mode because of compiler optimizes away stack frames.

I believe the unreal uses their own push pop system. The Tips of the day at Flipcode has several of such systems.
Shannon Barber
Shannon Barber
quote:
Original post by Void
The dbghelp library doesn''t give very accurate results in release mode because of compiler optimizes away stack frames.


doh!

The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Brannon
Brannon
That''s not entirely true. Yes, the compiler optimizes the hell out of the release binary, but the [release] symbols will still provide you with a stack dump. Having the program create a minidump is actually a great way to do it. Then you can get the minidump, open it in windbg (with your copy of the symbols), and debug it (get the stack trace, etc). I believe creating a minidump is *very* simple (what, one API call?).


-Brannon
-Brannon
Chris Hargrove
Chris Hargrove
Unreal''s system is done "manually" via macros, as mentioned above. This has its strengths and weaknesses. Personally, I prefer automated stack dumps when possible (like with DbgHelp on Win32).

I haven''t had many problems getting accurate stack dumps from release builds compared to debug builds, for the most part. Unfortunately, there are still a handful of issues that I don''t believe even the "Under the Hood" column or "Debugging Applications" book mentions, in terms of the StackWalk function.

For the context information passed into StackWalk, I''ve seen some examples which use GetThreadContext(). I haven''t had a whole lot of luck with this function personally, since the fact that it''s deeper inside Win32 affects the resulting context, and you have to filter out the lower calls manually. Even then, the context I got back didn''t seem to be accurate many times. So I would recommend writing your own __declspec(naked) context retrieval function instead.

Once you know your context is correct, StackWalk''s addresses are based on the return addresses stored on the stack, which are not really the "call" instructions but rather the instruction afterward. This distinction often results in line number information that is one or more lines off of the actual line you''re currently in (it''s reporting the line number connected to the instruction after the call, not the one making the call). This is a problem at the top of the stack too, since EIP will already be an instruction beyond the one that crashed. Now, while it''s difficult to deal with the TOS being off by a line or so (it''s usually close enough in practice), the other stack elements always follow "call" instructions so they''re easy to correct. MSVC always uses a 5-byte call instruction form, so for all StackWalk results other than the TOS, subtract 5 bytes from what it returns. This will move the instruction addresses from the instruction after the "call" back onto the "call" itself. This slight change has given me much more accurate stack dumps in many situations.
Null and Void
Null and Void
In case anyone cares, here''s my report on implementing Houdini''s method in my engine''s debug code. It works well, but their''s one gotcha. You have to keep in mind that multiple threads will mess with the results unless you keep them away from each other in seperate lists (which isn''t that hard, once you realize it ).

[Resist Windows XP''s Invasive Production Activation Technology!]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.