Crash Help

ripple01

Emperor
Joined
Mar 7, 2006
Messages
1,254
Location
New York City
Hello all,

I am having some crash issues with my mod I am hoping someone can help me out with.

I have a crash that is happening in the late game 700 or 800 turns into a 1200 turn game.

The problem is that the crash is not consistent. I know that crashes re artdefines and the like will happen on the same turn, meaning I can save the game on, say, turn 700, and if there is an artdefines problem, it will consistently crash on, say, turn 705. This crash is not happening at the same time. For example, lets say I get a crash on turn 705. I can go back to my saved game at turn 700 and the next time it will get through turn 705 and may crash on, say, turn 732. Does anyone know what might cause these non-consistent crashes?

Furthermore, I have been using a debug .DLL, and I am getting a crash that is happening ~ turn 100 that is not happening when I use the regular release .DLL. This is the error message I am getting:

First-chance exception at 0x05f3b676 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xfeeefef2.
Unhandled exception at 0x05f3b676 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xfeeefef2.

The code in question is from CvDeal:

Code:
void CvDeal::kill(bool bKillTeam)
{
	if ((getLengthFirstTrades() > 0) || (getLengthSecondTrades() > 0))
	{
		CvWString szString;
		CvWStringBuffer szDealString;
		CvWString szCancelString = gDLL->getText("TXT_KEY_POPUP_DEAL_CANCEL");

		if (GET_TEAM(GET_PLAYER(getFirstPlayer()).getTeam()).isHasMet(GET_PLAYER(getSecondPlayer()).getTeam()))
		{
			szDealString.clear();
			GAMETEXT.getDealString(szDealString, *this, getFirstPlayer());
			szString.Format(L"%s: %s", szCancelString.GetCString(), szDealString.getCString());
			gDLL->getInterfaceIFace()->addMessage((PlayerTypes)getFirstPlayer(), true, GC.getEVENT_MESSAGE_TIME(), szString, "AS2D_DEAL_CANCELLED");
		}

		if (GET_TEAM(GET_PLAYER(getSecondPlayer()).getTeam()).isHasMet(GET_PLAYER(getFirstPlayer()).getTeam()))
		{
			szDealString.clear();
			GAMETEXT.getDealString(szDealString, *this, getSecondPlayer());
			szString.Format(L"%s: %s", szCancelString.GetCString(), szDealString.getCString());
			gDLL->getInterfaceIFace()->addMessage((PlayerTypes)getSecondPlayer(), true, GC.getEVENT_MESSAGE_TIME(), szString, "AS2D_DEAL_CANCELLED");
		}
	}

	CLLNode<TradeData>* pNode;

	for (pNode = headFirstTradesNode(); (pNode != NULL); pNode = nextFirstTradesNode(pNode))
	{
		endTrade(pNode->m_data, getFirstPlayer(), getSecondPlayer(), bKillTeam);
	}

	for (pNode = headSecondTradesNode(); (pNode != NULL); pNode = nextSecondTradesNode(pNode))
	{
		endTrade(pNode->m_data, getSecondPlayer(), getFirstPlayer(), bKillTeam);
	}

	GC.getGameINLINE().deleteDeal(getID());
}


The line that is breaking is:

endTrade(pNode->m_data, getFirstPlayer(), getSecondPlayer(), bKillTeam);


Anyone give me any pointers on how to resolve these?
 
Regarding the second problem -

1. In general: There are many differences between a debug DLL and a release DLL. Usually when you see a bug only in one of them, it doesn't mean that the bug exists only there, but that you only notice it there. It might cause more subtle problems in the other configuration, or none at all. Usually this is the other way around (a bug manifests itself in release and not in debug) so consider yourself lucky ;)
For way too much information regarding this, and only if you're interested, see some explanations here.

2. Regarding the issue at hand: This error means that the code tries to access memory it has no permissions for. In each process, you have a few memory areas that the code is allowed to access. The address 0xfeeefef2 is way out of the allowed range.
If you look at the addresses of the variables when the exception breaks into the debugger, you'll see that 0xfeeefef2 is the address of pNode->m_data, and that the value of pNode (the address it points to) is 0xfeeefeee (4 bytes less). This address is a special address, which is assigned only in debug mode (hence the difference between debug and release).
Here you can find a short list (originally taken from Wikipedia) of the known bad-addresses. Specifically:

* 0xFEEEFEEE : Used by Microsoft's HeapFree() to mark freed heap memory

This means that you get a node that was already deleted (e.g. delete pNode;), but for some reason was not removed from the nodes list. The list I'm talking about is CvDeal::m_firstTrades which is accessed using headFirstTradesNode() and nextFirstTradesNode().
Are there any code changes in your mod that can cause this?

And this bug might also cause the first problem :dunno:
 
Thanks, Asaf!

With your help, I think I found the problem. I am using the Advanced Diplomacy 2 mod by Afforess and repackaged by stolenrays. In Afforess's original code, he forgot to comment the following functions in CvDeal.cpp and the corresponding code in CvDeal.h:

void CvDeal::removeFirstTradeNode(CLLNode<TradeData>* pNode)
{
m_firstTrades.deleteNode(pNode);
}

and

void CvDeal::removeSecondTradeNode(CLLNode<TradeData>* pNode)
{
m_secondTrades.deleteNode(pNode);
}

Accordingly, stolenrays missed these functions when creating his standalone modcomp. I'm testing now, but based upon what you wrote, this seems to be the cause of the problem.

Thanks again, and thanks for the helpful explanation of debug vs. release dlls.
 
Well, damn.

I added those missing sections and recompiled and ran the debug again. Got another exception on the same line of code, but the message was slightly different.

First-chance exception at 0x05ddb746 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xfeeefef2.
Unhandled exception at 0x05ddb746 (CvGameCoreDLL.dll) in Civ4BeyondSword.exe: 0xC0000005: Access violation reading location 0xfeeefef2.

Seems to be the same root problem, however. The only section of code added to CvDeal that includes headFirstTradesNode() and nextFirstTradesNode() is the following:

Code:
bool CvDeal::isEmbassy()
{
	CLLNode<TradeData>* pNode;

	for (pNode = headFirstTradesNode(); (pNode != NULL); pNode = nextFirstTradesNode(pNode))
	{
		if (pNode->m_data.m_eItemType == TRADE_EMBASSY)
		{
			return true;
		}
	}

	for (pNode = headSecondTradesNode(); (pNode != NULL); pNode = nextSecondTradesNode(pNode))
	{
		if (pNode->m_data.m_eItemType == TRADE_EMBASSY)
		{
			return true;
		}
	}
	
	return false;
}
 
Well, deleteNode removes the node from the list in addition to deleting it, so I don't think this is the problem?
What other changes do you have there? Can you attach the modified source files?
 
I understand that you code is based on BBAI. Are there any other mods included?
I couldn't find anything that looks suspicious (but that doesn't mean I didn't miss something).

When it breaks, what does the m_firstTrades list look like? You can add it to the watch windows to look at it. That way you can also check all of its nodes (expand the 'next' in each node), and see if it matches the list's kept length.
This might give some hint to what happens there, and if not, it might help determine how to catch the problem sooner when you next debug it.

Can you attach the saved game, assuming I don't need any other assets to run it?
 
Hi Asaf,

I have added a number of mods to the source code: RevDCM, Afforess's Advanced Diplomacy (which I suspect is ultimately the root of this problem), Aussie Lurker's Civics Mod, and a few other tweaks of my own. Unfortunately, it is a big mod with lots of assets, so I don't think a saved game will be helpful.

I added the m_firsttrades list to the Watch window (at least, I think I did it correctly), and although most of this is over my head, I do notice that the list seems to have an odd length and a negative one to boot. I've posted of a screenshot that shows the watch window and I've expanded all of the nodes. Is this helpful at all?



EDIT: I also just found this BUG code in CvDLLWidgetData.cpp that deals with some of these same things. Perhaps this code is conflicting with the Advanced Diplomacy code?

Code:
// BUG - Kill Deal Info - start
	if (pDeal != NULL)
	{
		szBuffer.append(NEWLINE);
		GAMETEXT.getDealString(szBuffer, *pDeal, GC.getGameINLINE().getActivePlayer());

		int iItem = widgetDataStruct.m_iData2;
		if (iItem != -1)
		{
			const CLinkList<TradeData>* listTradeData = NULL;

			if (iItem < pDeal->getLengthFirstTrades())
			{
				listTradeData = pDeal->getFirstTrades();
			}
			else
			{
				iItem -= pDeal->getLengthFirstTrades();
				if (iItem < pDeal->getLengthSecondTrades())
				{
					listTradeData = pDeal->getSecondTrades();
				}
			}

			if (listTradeData != NULL)
			{
				int iCount = 0;
				for (CLLNode<TradeData>* pNode = listTradeData->head(); NULL != pNode; pNode = listTradeData->next(pNode))
				{
					if (iCount++ == iItem)
					{
						TradeData& kTradeData = pNode->m_data;
						if (kTradeData.m_eItemType == TRADE_RESOURCES)
						{
							szBuffer.append(NEWLINE NEWLINE);
							GAMETEXT.setBonusHelp(szBuffer, (BonusTypes)kTradeData.m_iData);
						}
						break;
					}
				}
			}
		}
	}
// BUG - Kill Deal Info - end
 

Attachments

  • m_firsttrades.jpg
    m_firsttrades.jpg
    481.7 KB · Views: 74
Oh man, this is worse then I thought ;)

The list itself was freed, and since it's not a pointer to a list but an actual member, the deal itself must have been deleted, and then used.
Take a look at the full callstack of the crash, go over the functions in it and see if there was a call to delete (or SAFE_DELETE) of the deal before reaching the crash point.
 
Bingo! Taking a good look at the call stack helped me figure this out (at least, it seems to be working so far.)

I made an error while merging the Advanced Diplomacy code in CvPlayerAI and had this line of code repeated twice:

pLoopDeal->kill(); // XXX test this for AI...

I've removed it and so far, everything seems to be working. Now to test whether that problem is what was causing my crash in the release .dll

Thank you, thank you, thank you!!
 
Top Bottom