Jump to content
  • Advertisement
Sign in to follow this  
steve coward

First chance exception debug question

This topic is 4261 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am getting a curious first chance exception error 0xC0000005:Access Violation in my game. I have narrowed it down to one particular line, a declaration of a pointer to a unit class in my game: CGroundUnit* pGroundUnit; With this line commented out I get no exception. With the line included I get an unreferenced local variable from the Visual C++ (6.0) compiler that I am using and then I get an exception when I enter the procedure containing this line. Note that the first line of the procedure simply does a return so the above declaration is never executed. The exception is a new development in my game that I have been working on for years. My code contains over 100 declarations exactly like the above one and a week ago I was not experiencing this exception. My program runs fine if I ignore the exceptions. My program runs fine if I simply step over the offending procedure call using F10 key. But if I enter via F11, I get an exception. Any help would be appreciated. thanks, steve coward

Share this post


Link to post
Share on other sites
Advertisement
The compiler can strip 'empty' functions, if they do nothing. Hence the procedure is never called if all it does is return. That can happen when you create debug functions, that have their implementation #defined out, so in some configurations, the function does absolutely nothing.

My point is, if the function does nothing, it wont even be called, and you won't get an exception.

If the function does something (here, it just add a local variable to the stack), it will possibly be called (it should be stripped though). If the object with that member function is called upon and NULL, then you will get the exception.

It looks like what's happening here.

1) Object is NULL.
2) empty function stripped from the binary, so never called.
3) function not empty, so not stripped, so called but the objects is NULL -> Exception at 0x0000005.

Sorry, a bit winded, early morning.

Share this post


Link to post
Share on other sites
basically


class C_Object
{
public:
int dummy;
C_Object() : dummy(rand())
{}

void DoNothing() {}
void DoSomething() { printf("a-Ha! dummy = %d", dummy); }
};

C_Object* pNULLObject = NULL;
pNULLObject->DoNothing(); // should not crash
pNULLObject->DoSomething(); // should crash with access violation 0x00000005


Share this post


Link to post
Share on other sites
As I stated in my initial post, the offending function (OnLButtonUp1) is executed because I am setting a breakpoint at the point of entry and hitting F11. The first line of the function is a return. After the return there are many variable declarations and many lines of code that do not get executed. Oddly enough a couple of the declarations after the return but still within the body of the function are several similar style of declarations:
CGroundUnit* pGroundUnitPrevSelected;
CGroundUnit* pGroundUnitCurrSelected;

These two seem to be OK. The situation is illustrated below. This is contrived code since my original code has been hacked to simplify the situation. (This is why many of the variables in the call stack are uninitialized.) (Also the classes that are being constructed are large, containing many STL and Microsoft lists, maps etc.) This code is being called from within the constructor of the CGS_GameStarted1 class.


bool CGS_GameStarted1::OnLButtonUp1(bool bAltPressed, bool bCtrlPressed, bool bShiftPressed, bool bSpacePressed, CPoint point)
{
return(true);
.
.
.
CGroundUnit* pGroundUnitPrevSelected;
CGroundUnit* pGroundUnitCurrSelected;

.
.
.
// comment out the next line to avoid exception
CGroundUnit* pGroundUnit;
return(false);
}

Looking at call stack, at the point in time with the call stack as below, and sitting at my breakpoint at OnLButtonUp1(), hitting F11 causes an exception (if that one line of code is uncommented.) I do no understand this.

CGS_GameStarted1::CGS_GameStarted1(GameState EGS_GAME_STARTED, CD3DApplication * 0x00000000, CMyD3DApplication * 0xcccccccc, CWindowRegions * 0xcccccccc, CDrawUtilities * 0xcccccccc, _APP_GAME_STATUS_INFO * 0xcccccccc, CDirectSound * 0xcccccccc) line 99
CMyD3DApplication::makeGameStates() line 5098 + 99 bytes
CMyD3DApplication::CMyD3DApplication() line 154
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, HINSTANCE__ * 0x00000000, HINSTANCE__ * 0x00000000) line 125
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 7c816fd7()


CGS_GameStarted1::CGS_GameStarted1(GameState gameState, CD3DApplication* pD3DApp, CMyD3DApplication* pMyD3DApp, CWindowRegions* pWindowsRgns, CDrawUtilities* pDrawUtilities, LPAPP_GAME_STATUS_INFO pGameStatusInfo, CDirectSound* pDS)
{
OnLButtonUp1(false, false, false, false, CPoint(1,1));
.
.
.
}

Share this post


Link to post
Share on other sites
I continue to play with this.

When I change the name of the pointer variable from

CGroundUnit* pGroundUnit;


to

CGroundUnit* _pGroundUnit;

I do not get an exception.

The name pGroundUnit is a generic name I use for over a hundred different declarations of pointers to CGroundUnit objects in my code. Somehow a collision is occurring?

Share this post


Link to post
Share on other sites
You're barking up the wrong tree.

An access violation is a result of accessing memory you aren't allowed to access -- most often as a result of either failing to initialize a pointer or some memory, or attempting to access a null pointer.

Memory corruption errors can be subtle, and can sometimes manifest themselves (actually crash) very far from the actual location of the problem. Furthermore, small changes to the code generation can drastically alter the program flow in such a way as to make the problem appear to go away, when it really is still there waiting to bite you later.

Quote:

This is contrived code since my original code has been hacked to simplify the situation. (This is why many of the variables in the call stack are uninitialized.)

THIS IS BAD.

Because errors of this type are so subtle, presenting us with "contrived" code is not going to help us solve the problem -- you may have accidentally hidden the problem from us without knowing. Your handwaving dismissal of the uninitialized variables on the stack is suspicious, because uninitialized variables are a potential cause of the problem.

So, show us the real code for the function that crashes, exactly as you see it when you observe the crash. Also, show us the code for the function that immediately called the function that crashed, if you can. Also, use tags (using lowercase letters) to block off your code in posts so its easier to read.

Also, post the exact complete error message. Typically they look like: "error 0x00000005: Acces violation when trying to read 0x????????," or something. The question marks can be a big clue -- if the question marks form an address near 0, for example, you're probably dereferencing a null pointer.

Also, on an unrelated note, consider upgrading to Visual C++ Express. It's free, and it actually lets you write C++ instead of the pre-standard attempt at C++ that VC6 supports. It's just better.

Actually, VC6 might even be the cause of the problem, since it's prestandard C++. You're calling something that looks like it might be a virtual function (OnWhatever methods tend to be overridable, after all). Now, normally dynamic dispatch can't happen in a constructor because the object is not fully constructed -- typically then most compilers would optimize away attempts at doing so (and thus remove attempts to touch the 'this' pointer). VC6 may not have done this, since it's possible it decided to allow dynamic dispatch in constructors. This means you might be seeing a crash that's really caused by an attempt to access a screwed up 'this' pointer rather than a screwed up member of the class. The fact that unreachable code has an apparent effect on the code generation is also suspect.

This is a pretty blind shot in the dark, though. I can't do much more than speculate and wave my hands about without seeing the actual code that crashes, the actual line that does so, and the error messages. The call stack for this new example would also be helpful.

Share this post


Link to post
Share on other sites
jpetrie - thank you for your response.

Perhaps I have not communicated clearly. The code that I am posting *is* a failing case - it is contrived only in the
manner that the code is useless in a meaningful way. Calling a OnLButtonUp function from a constructor is contrived. I mention this
only to prevent people from responding back with "Why are you calling function xxx from within yyy?", etc.
Nonetheless the code should operate properly as far as I can tell.

My project contains 100,000 lines of code. It takes 2 minutes to reach the failing line of code in the real world case.
I have reproduced the original error message in a test case that executes 3-4 lines of my code in a matter of 1 second.
Furthermore I have narrowed down the difference between a failing case to a passing case by the presence of 1 line of code
(out of 100,000). It seems to me I am standing under a correct tree.


Here is the code presented in a more readable form.
[Small changes have been added from the code posted above to further simplify
the situation. Please do not reference the above code.]



INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
CMyD3DApplication myD3DApp;
.
.
.
}





class CMyD3DApplication : public CD3DApplication
{
.
.
.
}





CMyD3DApplication::CMyD3DApplication()
{
makeGameStates();
.
.
.
}





void CMyD3DApplication::makeGameStates()
{
CGameState* pGameState;

pGameState = (CGameState*)new CGS_GameStarted1(EGS_GAME_STARTED1, NULL, NULL, NULL, NULL, NULL, NULL);
.
.
.
}





CGS_GameStarted::CGS_GameStarted(GameState gameState, CD3DApplication* pD3DApp, CMyD3DApplication* pMyD3DApp, CWindowRegions* pWindowsRgns, CDrawUtilities* pDrawUtilities, LPAPP_GAME_STATUS_INFO pGameStatusInfo, CDirectSound* pDS)
{
OnLButtonUp1(false, false, false, false, CCltPoint(1,1));
.
.
.
}




The exception occurs upon entry into this procedure below. I employ polymorphism in my unit definitions -
CUnit is base class, CGroundUnit derived from CUnit, CVehicle derived from CGroundUnit.


bool CGS_GameStarted::OnLButtonUp1(bool bAltPressed, bool bCtrlPressed, bool bShiftPressed, bool bSpacePressed, CCltPoint point)
{
//CUnit* pGroundUnit; // passes when other 5 declarations are commented out
//CVehicleUnit* pGroundUnit; // fails when other 5 declarations are commented out
//CVehicleUnit* _pGroundUnit; // passes when other 5 declarations are commented out
//CGroundUnit* pGroundUnit; // fails when other 5 declarations are commented out
CGroundUnit* _pGroundUnit; // passes when other 5 declarations are commented out
//CGroundUnit* y; // passes when other 5 declarations are commented out
bool x = true;
return(x);
.
.
.
}





Here is call stack. At this point the execution has stopped because an exception has just occurred.

CGS_GameStarted1::OnLButtonUp1(unsigned char 0, unsigned char 0, unsigned char 0, unsigned char 0, CPoint {x=1 y=1}) line 658
CGS_GameStarted1::CGS_GameStarted1(GameState EGS_GAME_STARTED, CD3DApplication * 0x00000000, CMyD3DApplication * 0x00000000, CWindowRegions * 0x00000000, CDrawUtilities * 0x00000000, _APP_GAME_STATUS_INFO * 0x00000000, CDirectSound * 0x00000000) line 102
CMyD3DApplication::makeGameStates() line 5090 + 54 bytes
CMyD3DApplication::CMyD3DApplication() line 154
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, HINSTANCE__ * 0x00000000, HINSTANCE__ * 0x00000000) line 125
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 7c816fd7()





The error statement is exactly:

First-chance exception in TankBattle.exe: 0xC0000005: Access Violation.




It occurred to me that there might be a collision with the Microsoft virtual function OnLButtonUp(), which is why I renamed my function to OnLButtonUp1().


Perhaps I should upgrade to the free version of Visual C++;

Share this post


Link to post
Share on other sites
Quote:

It seems to me I am standing under a correct tree.

Yes, but the implication in your original posts that it has something to do with the declaration of a variable is barking up the wrong tree. I was referring to the fact that memory corruption errors often are caused by something far from the crash site.

Quote:

The code that I am posting *is* a failing case - it is contrived only in the
manner that the code is useless in a meaningful way.

I see; I took "contrived" to mean you doctored it like you did your most-current snippets (where you removed some code and replaced them with "..."). It's doctoring I take issue with, not the fact that the code is reduced. As long as the code you're working with actually crashes, that's fine. Unfortunately, the code you presented most recently is still doctored so doesn't show me everything I'd like to see.

My suspicion is still with the state of the "this" pointer. There's nothing in the function that is crashing that could cause a crash, as far as you've known, so the only possibilities are:

a) Something screwy with the "this" pointer. This should not have an effect, but since we are talking about VC6, who knows what weird and wonderful crap it's generating.
b) Something screwy with the stack itself. I raise this issue because your comments imply that the number of variables declared at the entry point of the function has an effect (is this right?). All that should change, given that they're all pointers, is the amount added to the stack pointer when the function is entered. If something overwrote the stack somewhere that might be enough to sufficient break something the boolean value of true is copied into the location for "x."

In both cases the line the source line the debugger shows you will be misleading, the actual faulting instruction is probably part of the prologue or call setup generated (e.g., assembly with no matching source lines).

Without even further information (the full code and headers for your simplified example), I'm not sure.

Something you might try is breaking into the debugger fairly early, enabling view of the disassembly, and single-stepping instruction-by-instruction until you find the offending instruction.

Share this post


Link to post
Share on other sites
Unless I'm overlooking something, there is no reason why simply changing the name of a variable to start with an underscore should change the behaviour of something like this.

I am suspicious that the error message does not give the address of the access violation, though perhaps this is because it's an old version of VC. I would definitely recommend upgrading to a newer version - MS do not support VC6, it's effectively dead.

The only other thing of note is that it's a member function inside a constructor that is throwing the AV - which should be OK, unless something inside the member function is assuming the object already exists, which it doesn't. But there's nothing in the code you've shown us to suggest that.

Assuming there's nothing you're not showing us, something a bit funny is going on. Can you show the disassembly at the point the exception is thrown? We really need to know the location that is being violated in order to determine what is causing this.

Share this post


Link to post
Share on other sites
///////////////////////////////
// FAIL
/////////////////////////////////


0043E25F int 3
--- d:\my projects\tankbattleexc\gs_gamestarted1.cpp -------------------------------------------------------------------------------------
656: // assumes g_dpActivePlayer has been set
657: bool CGS_GameStarted1::OnLButtonUp1(bool bAltPressed, bool bCtrlPressed, bool bShiftPressed, bool bSpacePressed, CCltPoint point)
658: {
0043E260 push ebp
0043E261 mov ebp,esp
0043E263 push 0FFh
0043E265 push offset __ehhandler$?OnLButtonUp1@CGS_GameStarted1@@QAE_N_N000VCPoint@@@Z (00724110)
0043E26A mov eax,fs:[00000000]
0043E270 push eax
0043E271 mov dword ptr fs:[0],esp
0043E278 sub esp,2ACh
0043E27E push ebx
0043E27F push esi
0043E280 push edi
0043E281 push ecx
0043E282 lea edi,[ebp-2B8h]
0043E288 mov ecx,0ABh
0043E28D mov eax,0CCCCCCCCh
0043E292 rep stos dword ptr [edi]
0043E294 pop ecx
0043E295 mov dword ptr [ebp-10h],ecx
659: //CUnit* pGroundUnit; // passes
660: //CVehicleUnit* pGroundUnit; // fails
661: //CVehicleUnit* _pGroundUnit; // passes
662: CGroundUnit* pGroundUnit; // fails
663: //CGroundUnit* _pGroundUnit; // passes
664: //CGroundUnit* y; // passes
665: bool x = true;
0043E298 mov byte ptr [ebp-18h],1
666: return(x);
0043E29C mov al,byte ptr [ebp-18h]




EAX = 0012F0C4 EBX = 7FFD4000
ECX = 01116F08 EDX = 00000001
ESI = 00000000 EDI = 0012F108
EIP = 0043E260 ESP = 0012F054
EBP = 0012F114 EFL = 00000246 CS = 001B
DS = 0023 ES = 0023 SS = 0023 FS = 003B
GS = 0000 OV=0 UP=0 EI=1 PL=0 ZR=1 AC=0
PE=1 CY=0
ST0 = +0.00000000000000000e+0000
ST1 = +0.00000000000000000e+0000
ST2 = +0.00000000000000000e+0000
ST3 = +0.00000000000000000e+0000
ST4 = +9.96093750000000000e-0001
ST5 = +9.99609374999994360e-0001
ST6 = +1.00000000000000000e+0000
ST7 = +1.00000000000000000e+0000
CTRL = 027F STAT = 4020 TAGS = FFFF
EIP = 773D6E8A CS = 001B DS = 0023
EDO = 773D1C48



//////////////////////////////////
// PASS
////////////////////////////////////

0043E25F int 3
--- D:\My Projects\TankBattleExc\GS_GameStarted1.cpp -------------------------------------------------------------------------------------
656: // assumes g_dpActivePlayer has been set
657: bool CGS_GameStarted1::OnLButtonUp1(bool bAltPressed, bool bCtrlPressed, bool bShiftPressed, bool bSpacePressed, CCltPoint point)
658: {
0043E260 push ebp
0043E261 mov ebp,esp
0043E263 push 0FFh
0043E265 push offset __ehhandler$?OnLButtonUp1@CGS_GameStarted1@@QAE_N_N000VCPoint@@@Z (00724110)
0043E26A mov eax,fs:[00000000]
0043E270 push eax
0043E271 mov dword ptr fs:[0],esp
0043E278 sub esp,2ACh
0043E27E push ebx
0043E27F push esi
0043E280 push edi
0043E281 push ecx
0043E282 lea edi,[ebp-2B8h]
0043E288 mov ecx,0ABh
0043E28D mov eax,0CCCCCCCCh
0043E292 rep stos dword ptr [edi]
0043E294 pop ecx
0043E295 mov dword ptr [ebp-10h],ecx
659: //CUnit* pGroundUnit; // passes
660: //CVehicleUnit* pGroundUnit; // fails
661: //CVehicleUnit* _pGroundUnit; // passes
662: //CGroundUnit* pGroundUnit; // fails
663: CGroundUnit* _pGroundUnit; // passes
664: //CGroundUnit* y; // passes
665: bool x = true;
0043E298 mov byte ptr [ebp-18h],1
666: return(x);
0043E29C mov al,byte ptr [ebp-18h]




EAX = 0012F0C4 EBX = 7FFDA000
ECX = 01116F08 EDX = 00000001
ESI = 00000000 EDI = 0012F108
EIP = 0043E260 ESP = 0012F054
EBP = 0012F114 EFL = 00000246 CS = 001B
DS = 0023 ES = 0023 SS = 0023 FS = 003B
GS = 0000 OV=0 UP=0 EI=1 PL=0 ZR=1 AC=0
PE=1 CY=0
ST0 = +0.00000000000000000e+0000
ST1 = +0.00000000000000000e+0000
ST2 = +0.00000000000000000e+0000
ST3 = +0.00000000000000000e+0000
ST4 = +9.96093750000000000e-0001
ST5 = +9.99609374999994360e-0001
ST6 = +1.00000000000000000e+0000
ST7 = +1.00000000000000000e+0000
CTRL = 027F STAT = 4020 TAGS = FFFF
EIP = 773D6E8A CS = 001B DS = 0023
EDO = 773D1C48



Registers are identical. Disassembly is identical.

Looking into migrating to VC8 I see that it would not support MFC for free.
Looks like much work fixing all porting issues as well.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!