So I recently got a hold of RapidXML to use as a way to parse XML in my program, I have mainly been using it as a way to mess around but I have been getting some very weird issues that I'm really struggling to track down. Try and stick with me through this, because I was pretty thorough with trying to fix this issue, but I must be missing something.
First off here's the XML:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<image key="tilemap_roguelikesheet" path="res/media/tilemaps/roguelikesheet.png" />
<image key="tilemap_tiles" path="res/media/tilemaps/tiles.png" />
</resources>
The function the segfault occurs:
void TextureManager::LoadResource(const char* pathToFile)
{
rapidxml::xml_document<>* resource = Resources::LoadResource(pathToFile);
std::string imgName;
std::string imgPath;
if (resource != NULL)
{
rapidxml::xml_node<>* resourcesNode = resource->first_node("resources");
if (resourcesNode != NULL)
{
for (rapidxml::xml_node<>* child = resourcesNode->first_node("image"); child; child = child->next_sibling())
{
//Crash here on the second loop through.
imgName = child->first_attribute("key")->value();
imgPath = child->first_attribute("path")->value();
Astraeus::Log(moduleName, "Image Name: " + imgName);
Astraeus::Log(moduleName, "Image Path: " + imgPath);
TextureManager::AddTexture(imgName, imgPath);
}
}
else
{
Astraeus::Error(moduleName, "Resources node failed to load!");
}
resource->clear();
}
else
{
std::string fileName(pathToFile);
Astraeus::Error(moduleName, fileName + " could not be loaded.");
}
}
So segfault happens on the second loop of the for loop to go through all the nodes, and triggers when it tries to do the imgName assignment. Here's where things get a bit odd. When doing a debug of the program, the initial child nodes breakdown shows it has memory pointers to the next nodes and it's elements/attributes etc. When investigating those nodes, you can see that the values exist and rapidxml has seemingly successfully parsed the file.
However, when the second loop occurs, child is shown to still have the exact same memory pointers, but this time the breakdown in values show they are essentially NULL values, so the program fails and we get the code 139. If you try and look at the previous node, that we have just come from the values are also NULL.
Now say, I comment out the line that calls on the AddTexture function, the node is able to print out all the nodes values no problems at all. (The Log method is essentially just printing to console until I do some more funky stuff with it.) so the problem must lie in the function? Here it is:
void TextureManager::AddTexture(const std::string name, const std::string path)
{
Astraeus::Log(moduleName, "Loading texture: " + path);
if (texturesLookup.find(name) != texturesLookup.end())
{
Astraeus::Error(moduleName, "Texture Key: " + name + " already exists in map!");
}
else
{
texturesLookup.insert(std::make_pair(name, path));
//Texture* texture = new Texture();
/*if (texture->LoadFromFile(path))
{
//textures.insert(std::make_pair(name, texture));
}
else
{
Astraeus::Error(moduleName, "Failed to add texture " + name + " to TextureManager!");
}*/
}
}
Ignoring the fact that strings are passed through and so should not affect the nodes in any way, this function is still a bit iffy. If I comment out everything it can work, but sometimes just crashes out again. Some of the code got commented out because instead of directly adding the key name, plus a memory pointer to a texture, I switched to storing the key and path strings, then I could just load the texture in memory later on as a workaround. This solution worked for a little bit, but sure enough began to segfault all over again.
I can't really reliably replicate or narrow down what causes the issue everytime, so would appreciate any help. Is RapidXML doc somehow going out of scope or something and being deleted?
For the record the class is practically just static along with the map that stores the texture pointers.
Thanks!
So for anybody coming back again in the future here's what was happening.
Yes, it was a scope issue but not for the xml_document as I kept initially thinking. The xml_file variable that was in the resources load function was going out of scope, which meant due to the way RapidXML stores things in memory, as soon as that goes out of scope then it frees up the memory, which led to the next time dynamic allocation happened by a specific function it would screw up the xml document and fill it with garbage data.
So I guess the best idea is to make sure xml_file and xml_document do not go out of scope. I have added some of the suggestions from previous answers, but I will point out those items WERE in the code, before being removed to help with the debug process.
Thanks everybody for the help/advice.
I'm not sure, but I think that Martin Honnen made the point.
If next_sibling() return the pointer to the text node between the two "image" elements, when you write
imgName = child->first_attribute("key")->value();
you obtain that child->first_attribute("key") is a null pointer, so the ->value() is dereferencing a null pointer. Crash!
I suppose you should get the next_sibling("image") element; something like
for (rapidxml::xml_node<>* child = resourcesNode->first_node("image");
child;
child = child->next_sibling("image"))
And to be sure not to use a null pointer, I strongly suggest you to check the attribute pointers (are you really sure that "image" elements ever carry the "key" and the "path" elements?); something like this
if ( child->first_attribute("key") )
imgName = child->first_attribute("key")->value();
else
; // do something
if ( child->first_attribute("path") )
imgPath = child->first_attribute("path")->value();
else
; // do something
p.s.: sorry for my bad English.
This line is setting my teeth on edge...
rapidxml::xml_document<>* resource = Resources::LoadResource(pathToFile);
LoadResource returns a pointer, but you never free it anywhere...?
Are you 100% sure that function isn't returning a pointer to an object that's now gone out of scope. Like this classic bug...
int * buggy()
{
int i= 42;
return &i; // UB
}
As #max66 says. You should use next_sibling("image"). If that's failing, you need to find out why.
Related
Currently, there is one exception thrown from program written by C++, and running under windows.
here is the min dump information in the logs.
08/12/15 04:37:19 I New Information for UID 2d936a, FloorLoc F1505
08/12/15 04:37:19 E >>>>> EXCEPTION: Access Violation while trying to read address 20203567
[Fault address: 004AF945 01:000AE945 C:\Program Files (x86)\MySystems\WPR.exe 00400000] <<<<<
Call stack:
Load addr Address Frame Logical addr Module
00400000 004AF945 0588F8CC 0001:000AE945 C:\Program Files (x86)\MySystems\WPR.exe
00400000 004A89A4 0588FAEC 0001:000A79A4 C:\Program Files (x86)\MySystems\WPR.exe
According to the logical addr and .map file, I can find the codes where this exception thrown.
if (TempMSE->m_elem == NULL)
{
TempMSE->m_elem = new Element(element);
TempMSE->m_elem->SetLocation(FloorLoc);
LoggerInfo("New Information for UID %x, FloorLoc %s", Id, FloorLoc.ToString(buf));
}
TempMSE->m_elem->SetValue0(CIN_0, 0); // this exception is thrown here!!! through logical address 0001:000AE945
It seems that the m_elem gets one address from new operator, and there is NO exception for SetLocation function calling. Also the following log output correctly.
Why there is one exception thrown from SetValue0? Here is function SetValue0
void SetValue0(INDEX idx, DWORD val)
{
if (idx >= 0 && idx < MAX_INDEX){
if(val != m_Info[idx])
{
m_Info[idx] = val;
}
}
}
The m_Info is one array variable in the Element, and its size is MAX_INDEX.
On the other side, the address 0x20203567 seems one readable address, how could it be read violation?
Edit
Add more information here
class Element {
// other function here...
private:
FloorLocation m_FloorLoc;
DWORD m_Info[MAX_INDEX];
bool m_Dirty;
};
Element::Element(const Element& elem) {
m_FloorLoc = elem.m_FloorLoc;
for (int i = 0; i < MAX_INDEX; ++i)
m_Info[i] = elem.m_Info[i];
m_Dirty = elem.m_Dirty;
}
class FloorLocation {
// other function here...
private:
FloorId m_floorloc;
};
FloorLocation::FloorLocation( const FloorLocation& loc )
{
memset(&m_floorloc, ' ', 8); // space filled
if(loc.m_floorloc.id[0] != 0)
{
memcpy(m_floorloc.id, loc.m_floorloc.id, 8);
// eliminate nulls
for(int ndx=0; ndx < 8; ndx++)
{
if(m_floorloc.id[ndx] == 0)
m_floorloc.id[ndx]=' ';
}
}
}
typedef struct {
char id[8];
} FloorId;
These kinds of questions are a little hard to answer. I gave some ideas in comments, which I'll elaborate on here. Here are the kinds of things I look for when I have these sorts of crash logs with no other leads.
An access violation on read at that location suggests one of the following:
TempMSE is not a valid pointer, and the exception is thrown when attempting to get m_elem from it;
TempMSE->m_elem is not valid, and the exception is thrown inside SetValue0 when attempting to test the value of m_Info[idx].
In the latter case, this could occur if you delete TempMSE->m_elem somewhere but don't set it to NULL. If another thread is responsible for that delete, perhaps you have a race condition here where it's about to be set to NULL, but this code is executed first.
Another possibility is that either TempMSE or TempMSE->m_elem get corrupted somewhere along the way. This could be the result of a buffer overrun inside TempMSE (if you have arrays), or basically any sort of undefined behaviour that occurs near these pointers in memory. If TempMSE is on the stack, then look for any potential trouble there.
I don't want to fill this answer with other kinds of speculation (like heap corruption), but hopefully it gives you some avenues to try. The basic list of common culprits is this:
coding error (not initialising or resetting a value)
threading issues, race conditions...
undefined behaviour or overruns trashing data
Good luck!
I can't say what is actually wrong, but I would disassemble the code at 0x004AF945 - and several instructions before, and try to understand what part of the failing function that is.
As pointed out in one of the comments, the address that the fault happens at is suspiciously looking like 'C# ', which makes me think that somewhere a string is overflowing somewhere...
This is just a guess, but I suspect TempMSE->m_elem is what contains the value 0x20203567, and thus is NOT NULL when it tries to access it, meaning no logging is performed. [Obviously this is based on what code you have shown so far, and if there is logging before/after that show this is not the case, my second guess is that m_info is somehow wrong...
I'm having a seg fault: 11 when I run a particular program. I feel like this problem wasn't present before I upgraded my system to Mac OS X 10.9, but it's possible I just overlooked it..
Anyway, my function looks like:
// this applies a warp to the field given, and saves output. simple!
void Apply(string warpName, string fieldName, bool conserve, string outName) {
// get lon, lat dimensions of warp
int noLongs = GetDimension(warpName, 3, "warp");
int noLats = GetDimension(warpName, 2, "warp");
int origNoLongs = noLongs, origNoLats = noLats;
// read in params
vector<double> params = ImportWarpFromNetCDF(warpName);
// rescale field to warp's dimensions, and read in
string tempName = "scaledField";
ReScale(fieldName, tempName, noLongs, noLats);
vector<vector<vector<double> > >inIntensities = ImportFieldFromNetCDF(tempName);
RemoveFile(tempName);
// just enter inIntensities for ref image, and 1 for lambda, to keep objective function happy
ObjectiveFunction objective(inIntensities, inIntensities, conserve, 1, false);
objective.setParameters(params);
// output files
ExportOutputToNetCDF(objective, outName);
cout << "BAH?!" << endl;
}
where the cout line at the end was just checking to see I'd got to the end of the function properly (which I have). Any thoughts on why this would be segfaulting here? I appreciate it might be hard to tell without seeing what the individual function calls do, and so I'll add those if necessary.
It doesn't actually matter too much, as this function is the last thing to be called (so the seg fault doesn't interrupt anything), but I still would rather get to the bottom of it!
The only thing that happens "after" the function are destructor calls. Check all your destructors of local variables. It looks like ObjectiveFunction is the only local variable that's not a primitive or standard library container, so check ObjectiveFunction::~ObjectiveFunction() for potential problems.
What is the correct way to remove a rigid body, I am doing just this to remove it:
void removeRigidBody(btDynamicsWorld* pDynamicsWorld, btRigidBody* rb)
{
pDynamicsWorld->removeRigidBody(rb);
delete rb->getMotionState();
delete rb;
}
However, the object still appears in pDynamicsWorld->getCollisionObjectArray() after I do a pDynamicsWorld->stepSimulation
Strangely enough this does not happen on ARM, just x86.
Actually, this is what I've found. Posting code in the comments would look awful, that's why the answer instead.
//remove the rigidbodies from the dynamics world and delete them
int i;
for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
{
btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
m_dynamicsWorld->removeCollisionObject( obj );
delete obj;
}
So you remove the body from the collision objects.
This was, like most bugs just a stupid mistake. Sorry to those who took time to read it.
The error was actually in some java that called the removeRigidBody by jni.
if (body.id > 0) {
The id is actually an int cast of the btRigidBody address, so of course any != 0 integer could be a valid address. On the x86, the addresses happened to be < 0 which on the other device happened to be > 0.
I'm having problems creating a tmx map from string input.
bool LevelManager::initLevel(int currentLevel)
{
const char* map;
try {
map = LevelManager::getLevel(currentLevel);
} catch (int) {
throw 1;
}
if(map != NULL){
CCLog("%s", map);
tileMap = CCTMXTiledMap::create(map);
tileMap->setAnchorPoint(ccp(0,0));
tileMap->setPosition(ccp(15,20));
this->addChild(tileMap, 5);
backgoundLayer = tileMap->layerNamed("Background");
} else {
throw 1;
}
return true;
}
Thats my code.
It is very unstable. Most of the times it crashes and sometimes it doesn't.
I'm loading my map from the string map. Wich is a const *char.
My map is named Level1.tmx and when i load the map like this: tileMap = CCTMXTiledMap::create("Level1.tmx"); it always works and never crashes.
And i know for a fact that the value of map is Level1.tmx because i log it in the line before the load.
When it crashes the log outputs this: (lldb)
and on the line tileMap->setAnchorPoint(ccp(0,0)); it says "Thread 1: EXC_BAD_ACCESS (code=2, adress=0x0)
Does anyone know why this happens and how to fix it?
Many thanks.
Ps: i'm using xcode, the latest cocos2d-x release and the iPhone simulator
Edit:
Using breakpoints i checked where things go bad while loading the tilemap.
on the line tileMap = CCTMXTiledMap::create(map);
my variable map is still fine
but on line tileMap->setAnchorPoint(ccp(0,0));
it is suddenly corrupted (most of the time)
It sounds like you're returning a char* string created on the stack, which means the memory may or may not be corrupted, depending on circumstances, moon phases, and what not.
So the question is: How is getLevel defined and what does it do (post the code)?
If you do something like this:
const char* LevelManager::getLevel(int level)
{
char* levelName = "default.tmx";
return levelName;
}
…then that's going to be the culprit. The levelName variable is created on the stack, no memory (on the heap) is allocated for it. The levelName variable and the memory it points to become invalid as soon as the method returns.
Hence when the method leaves this area of memory where levelName points to can be allocated by other parts of the program or other method's stack memory. Whatever is in that area of memory may still be the string, or it may be (partially) overridden by other bits and bytes.
PS: Your exception handling code is …. well it shows a lack of understanding what exception handling does, how to use it and especially when. I hope these are just remnants of trying to get to the bottom of the issue, otherwise get rid of it. I recommend reading a tutorial and introductions on C++ exception handling if you want to continue to use exceptions. Especially something like (map != NULL) should be an assertion, not an exception.
I fixed it.
const char* was to blame.
When returning my map as a char * it worked flawless.
const char *levelFileName = level.attribute("file").value();
char *levelChar = new char[strlen(levelFileName) + 1];
std:: strcpy (levelChar, levelFileName);
return levelChar;
Thats how i now return the map.
I'm currently working on a project in C++ where I need to read some things from a xml file, I've figured out that tinyxml seams to be the way to go, but I still don't know exactly how to do.
Also my xml file is a little tricky, because it looks a little different for every user that needs to use this.
The xml file I need to read looks like this
<?xml version="1.0" encoding="utf-8"?>
<cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xmlns:d="http://www.kuju.com/TnT/2003/Delta" d:version="1.0">
<cCareerModel d:id="154964152">
<ScenarioCareer>
<cScenarioCareer d:id="237116344">
<IsCompleted d:type="cDeltaString">CompletedSuccessfully</IsCompleted>
<BestScore d:type="sInt32">0</BestScore>
<LastScore d:type="sInt32">0</LastScore>
<ID>
<cGUID>
<UUID>
<e d:type="sUInt64">5034713268864262327</e>
<e d:type="sUInt64">2399721711294842250</e>
</UUID>
<DevString d:type="cDeltaString">0099a0b7-e50b-45de-8a85-85a12e864d21</DevString>
</cGUID>
</ID>
</cScenarioCareer>
</ScenarioCareer>
<MD5 d:type="cDeltaString"></MD5>
</cCareerModel>
</cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
Now the goal of this program is to be able to insert some string (via. a variable) and serch for the corresponding "cScenarioCarrer d:id" and read the "IsComplete" and the "BestScore".
Those strings later need to be worked with in my program, but that I can handle.
My questions here are
A. How do I go by searching for a specific "cScenarioCareer" ID
B. How do I paste the "IsComplete" and "BestScore" into some variables in my program.
Note: The xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx string is unique for every user, so keep in mind it can be anything.
If anyone out there would like to help me, I'd be very graceful, thank you.
PS. I'd like to have some kind of understanding for what I'm doing here, all though "paste this code into your program" answers are acceptable, I think it would be much better if you can tell me how and why it works.
Since you're doing this in C++ I'll make this example using the ticpp interface to
TinyXml that available at ticpp.googlecode.com.
Assumptions:
A given xml file will contain one <cloud> tag and multiple
<cCareerModel> tags.
Each <cCareerModel> contains a single <ScenarioCareer> tag which in turn contains a single <cScenarioCareer> tag
You've parsed the xml file into a TiXmlDocument called xmlDoc
You don't need to examine the data type attributes
You don't mind using exceptions
I'll also assume that you have a context variable somewhere containing a pointer to the
<cloud> tag, like so:
ticpp::Element* cloud = xmlDoc.FirstChildElement("cloud");
Here's a function that will locate the ticpp::Element for the cScenarioCareer with
the given ID.
ticpp::Element* findScenarioCareer(const std::string& careerId)
{
try
{
// Declare an iterator to access all of the cCareerModel tags and construct an
// end iterator to terminate the loop
ticpp::Iterator<ticpp::Element> careerModel;
const ticpp::Iterator<ticpp::Element> modelEnd = careerModel.end();
// Loop over the careerModel tags
for (careerModel = cloud->FirstChildElement() ; careerModel != modelEnd ;
++careerModel)
{
// Construct loop controls to access careers
ticpp::Iterator<ticpp::Element> career;
const ticpp::Iterator<ticpp::ELement> careerEnd = career.end();
// Loop over careers
for (career = careerModel->FirstChildElement("ScenarioCareer").FirstChildElement() ;
career != careerEnd ; ++career)
{
// If the the d:id attribute value matches then we're done
if (career->GetAttributeOrDefault("d:id", "") == careerId)
return career;
}
}
}
catch (const ticpp::Exception&)
{
}
return 0;
}
Then to get at the information you want you'd do something like:
std::string careerId = "237116344";
std::string completion;
std::string score;
ticpp::Element* career = findScenarioCareer(careerId);
if (career)
{
try
{
completion = career->FirstChildElement("IsCompleted")->GetText();
score = career->FirstChildElement("BestScore")->GetText();
}
catch (const ticpp::Exception&)
{
// Handle missing element condition
}
}
else
{
// Not found
}
Naturally I haven't compiled or tested any of this, but it should give you the idea.