Page 1 of 1

Possible bug... need help verifying.

Posted: Thu May 28, 2009 4:37 pm
by Sylus101
I didn't want to post this in bug reports, because I'm not 100% where my issue is. I'm going to hope to do this without being terribly vague...

I have 2 computers that I work on and use googlecode svn to update between the two. I only update my projects this way, not the devkitpro folder. On one, my work computer I'm still on devkitARM 25 and libnds 1.3.2. This will be Computer 1. On my home machine, I've updated recently and am on devkitARM 26 and libnds 1.3.3 (Computer 2).

The best I can do in laying groundwork, is that I have a structure for my sprites, and there are 3 declared as pointers for bullets, targets, and dirt (for a graphic when a bullet hits the ground and small dirt particles fly about). I'm using malloc() for these pointers to allocate a certain amount of each for each stage of the game. The calls to malloc() are successful and not returning NULL. The pointers are declared globally above main() and passed as parameters to any functions using them.

The piece of code that is giving me fits of rage at this point... is a function (defined in a separate source) with all 3 as parameters:

Code: Select all

void BulletUpdate(spriteMember *bullet, spriteMember *target, spriteMember *dirt)
A single bullet instance is passed to it, and the whole target and dirt struct pointers as well. Called in main it's like this:

Code: Select all

BulletUpdate(&bullet[i], target, dirt);
On Computer 1, everything works fine. Basically if a bullet hits no target and strikes the ground, a dirt splash sprite is created by getting an instance handle and using dirt[handle].element in spots to update information about that sprite (that it's alive, it's location, gfx, animation parameters, etc...).

On Computer 2, none of the information applied to that structure instance is kept, even inside the function. For example, if I add this inside the function:

Code: Select all

dirt[handle].alive = 1;
iprintf("alive - %d\n", dirt[handle].alive);
iprintf("alive - %d\n", dirt[handle].alive);
while(1)swiWaitForVBlank();
The output is:
alive - 1
alive - 0

When I saw that... I was totally dumbfounded. If a bullet hits a target, the target instance affected has parameters modified in exactly the same way and those work correctly. Removing the dirt parameter and using extern to bring the dirt pointer into scope at the top of the source with the function fixes the whole thing, but I don't want that to be the solution.

Before finally posting I tried one more thing... I made the dirt pointer an array and removed the malloc() for it. This worked keeping the parameter in the function. I should mention that when a handle is retrieved it happens from the top down, so if I had 8 possible dirt sprites the first handle would be 7. Is it possible that malloc() is somehow returning a pointer correctly but not to enough space?? This could be refuted though, as bringing the pointer into scope worked...

EDIT - I realized that I hadn't tried on hardware (I always do that...) and found that it's locking up on me completely somewhere in the main game loop compiled with R26 and 1.3.3...
The nds output using R25 and 1.3.2 work fine.

Re: Possible bug... need help verifying.

Posted: Wed Jun 03, 2009 9:11 pm
by Al_Bro
If I were a betting man then my money would be on heap (or possibly stack) corruption. Toss in a bit of compiler optimisation and it's not surprising you're getting different results. It's quite likely that the first call to iprintf will push the value you've just assigned to alive onto the stack, but during it's processing and return overwritten the memory wherever your 'dirt' pointer is pointing to.

Best thing is if you could post the exact calls to malloc. Don't forget that malloc works in bytes and has no idea about the size of the structure you're allocating memory for unless you tell it. I would be inclined to use 'new' anyway unless you really have a compelling reason not to use it.

Re: Possible bug... need help verifying.

Posted: Thu Jun 04, 2009 1:10 am
by Sylus101
Well, this is what I have at the moment:

Code: Select all

bullet = (spriteMember*)memalign(4, sizeof(spriteMember)*BULLET_MAX);
sassert(bullet != NULL, "bullet allocation failed");

dirt = (spriteMember*)memalign(4, sizeof(spriteMember)*DIRT_MAX);
sassert(dirt != NULL, "dirt allocation failed");

enemy = (spriteMember*)memalign(4, sizeof(spriteMember)*ENEMY_MAX);
sassert(enemy != NULL, "enemy allocation failed");

blood = (spriteMember*)memalign(4, sizeof(spriteMember)*BLOOD_MAX);
sassert(blood != NULL, "blood allocation failed");
The "MAX" values are defined as:

Code: Select all

#define BULLET_MAX 20
#define ENEMY_MAX 16
#define DIRT_MAX 8
#define BLOOD_MAX 16
There are loops checking the behavior of created sprites for all of those types. On the advice of someone on another forum, I turned on the default exception handler and got a guru meditation error... the line that produced the error (as long as I checked it right) was the second line here:

Code: Select all

for(i = 0; i < DIRT_MAX; i++){
   if(dirt[i].alive){
The "alive" elements are initialized prior to this occurring and there are 3 other loops checking the other sprite types which appear to be fine. What kills me... is that I can still just take the parameter out of the function I mentioned before, use extern to move *dirt into the source where the function is defined and the whole problem goes away.

Oh, and I'm using malloc because I don't know C++ yet, just C.

Re: Possible bug... need help verifying.

Posted: Thu Jun 04, 2009 2:23 pm
by WinterMute
Al_Bro wrote: Best thing is if you could post the exact calls to malloc. Don't forget that malloc works in bytes and has no idea about the size of the structure you're allocating memory for unless you tell it. I would be inclined to use 'new' anyway unless you really have a compelling reason not to use it.

This is incredibly misleading, new has zero advantages over malloc for standard memory allocation. The difference between malloc and new is that the latter calls constructors for instantiated objects and calls abort by default when memory is exhausted.
Sylus101 wrote: The "alive" elements are initialized prior to this occurring and there are 3 other loops checking the other sprite types which appear to be fine. What kills me... is that I can still just take the parameter out of the function I mentioned before, use extern to move *dirt into the source where the function is defined and the whole problem goes away.
It sounds like you're running into some oddness with optimisation, best thing to do is reduce the code as much as possible to create a minimal testcase which illustrates the problem so we can examine the code in a bit more detail. What we need is basically just the memory allocation & setup and the BulletUpdate function as well as necessary headers.

It's also worth noting that malloc returns word aligned pointers by default - memalign(4,size) is unnecessary.

Re: Possible bug... need help verifying.

Posted: Thu Jun 04, 2009 7:33 pm
by Al_Bro
WinterMute wrote:This is incredibly misleading, new has zero advantages over malloc for standard memory allocation. The difference between malloc and new is that the latter calls constructors for instantiated objects and calls abort by default when memory is exhausted.
Not quite what I was meaning. I wasn't suggesting that malloc had a problem in itself - but rather that using "new" gives coding advantages. Replacing:

Code: Select all

bullet = (spriteMember*)malloc(sizeof(spriteMember) * BULLET_MAX);
with

Code: Select all

bullet = new spriteMember[BULLET_MAX];
means that you're not having to specify the size of the elements you're allocating - just the number of elements required. It's much more strongly typed and if constructors are implemented later then there's less to change.

Re: Possible bug... need help verifying.

Posted: Thu Jun 04, 2009 10:52 pm
by Sylus101
WinterMute wrote: It sounds like you're running into some oddness with optimization, best thing to do is reduce the code as much as possible to create a minimal testcase which illustrates the problem so we can examine the code in a bit more detail. What we need is basically just the memory allocation & setup and the BulletUpdate function as well as necessary headers.

It's also worth noting that malloc returns word aligned pointers by default - memalign(4,size) is unnecessary.
Good to know on the memalign()...

I'll give this a whirl ASAP.