Debug Assertion Failure
Gatleos

I'm not even sure this is worth asking, since it's such a vague yet complex problem. But here goes. My program recently started behaving... oddly. It compiles perfectly, but about 75% of the time running it immediately produces an error. It's not a run-time error, like I said it runs fine sometimes. And when my game loop is exited and the program prepares to close, I get a debug assertion failure every time.

The program is an Allegro ASCII library I'm programming, and the only guess I have is that Allegro is going crazy from all the images I'm blitting to the screen. But even that seems unlikely, the program hardly uses any CPU and even when I remove most of the draw commands from the loop I get the error just as often.

HEEEEEEELLLLLLP

Thanks in advance. :)

J-Gamer

We probably could be more of a help when you would give more details...
Did your compiler throw any warnings when building? Or do you have them turned off?
You should try and make your program into something smaller that still crashes for the same reason... You might find the problem yourself this way, otherwise you should post some code.

Gatleos

No warnings to speak of. My main .cpp probably wouldn't be of much help if I posted it, since most of it is just referencing functions in the library I'm building. That's almost certainly where the problem is coming from, but it's a mountain of code. Want to see it...?

Edgar Reynaldo
Gatleos said:

And when my game loop is exited and the program prepares to close, I get a debug assertion failure every time.

So run your program through a debugger and get a backtrace of the function stack when the assertion fails.

To do this, use allegro to set the assert handler.

int AssertHandler(const char* msg) {
   int zero = 0;
   int dividebyzero = 1/zero;
   return 0;
}

// in main
register_assert_handler(AssertHandler);

That way, when something fails an Allegro ASSERT, you will divide by zero. Your debugger will catch this exception, and then you can debug it properly.

Gatleos said:

No warnings to speak of.

So you compiled with -Wall, right?

Gatleos

I've run step-by-step through the program multiple times, it never encounters any kind of errors. It would help if I knew what the hell an assertion failure was.

On that note, I think you're overestimating my programming knowledge.

EDIT: I need to start specifying that I'm using Allegro 5. How would I use that nifty reality-breaking assert handler in 5.0?

gnolam
Gatleos said:

It would help if I knew what the hell an assertion failure was.

It's an assertion that fails. ;)

What assert() does is basically "if the supplied argument is not true, kill the program". It's a way of error checking.

Edgar Reynaldo
Gatleos said:

I need to start specifying that I'm using Allegro 5. How would I use that nifty reality-breaking assert handler in 5.0?

Sorry, I don't believe that functionality is available in Allegro 5, but it should be.

Gatleos said:

It would help if I knew what the hell an assertion failure was.

An assertion failure is when a program fails an assert call.

#include <assert.h>// C
#include <cassert>// C++

assert(1);// fine, 1 evaluates to true, assertion passed
assert(0);// %!#$%, 0 evaluates to false, assertion failed, abort called

Gatleos said:

I've run step-by-step through the program multiple times, it never encounters any kind of errors.

Are you on Windows? I think an error message pops up when assert is failed on Windows. Do you have any idea where the assertion failure is coming from?

Gatleos

Yup, windows. It comes up when I exit the game loop. The only code after that de-allocates my display, bitmaps, etc. But that code hasn't changed since long before these errors started popping up.

{"name":"R1ShD.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/e\/ae4f3d19732d0e73f7f0e56c0dc54ee6.png","w":451,"h":333,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/e\/ae4f3d19732d0e73f7f0e56c0dc54ee6"}R1ShD.png

X-G

Oh, that particular assertion is a CRT exception. It means that some memory function was attempted on a pointer that isn't a valid heap pointer pointing into your local heap. This usually means one of three things:

1) You attempted to use free(), realloc(), or delete on a pointer that isn't valid, for instance if you try to free() the same pointer twice. The pointer is invalid because it has already been freed.

2) You're freeing a pointer that was originally allocated in a DLL (as opposed to your main program), or vice versa. The pointer is invalid because DLLs you load have their own heap separate from your application's local heap.

3) You're mixing debug and release CRTs and allocating/freeing memory from different CRTs. The pointer is invalid because the debug and release CRTs have different internal allocation semantics.

Gatleos

Well, that seems to be on the right track. It's not a .dll, but in my header, there's a class constructor that dynamically allocates a 2d array, and a destructor that frees it:

#SelectExpand
1struct Tile 2{ 3int symbol; 4const char *color; 5const char *backcolor; 6}; 7 8class Window 9{ 10public: 11int x;//Window x offset 12int y;//Window y offset 13int w;//Window width 14int h;//Window height 15Tile **tile;//2d array of contents of window 16Window(int xpos,int ypos,int width,int height) 17{ 18tile = new Tile*[width+1]; 19for(int i=0;i<=width+1;i++) 20{ 21tile[i] = new Tile[height+1]; 22} 23for(int i=0;i<=width+1;i++) 24for(int j=0;j<=height+1;j++) 25{ 26tile[i][j].symbol=0; 27tile[i][j].color="white"; 28tile[i][j].backcolor="black"; 29} 30x=xpos; 31y=ypos; 32w=width; 33h=height; 34} 35~Window() 36{ 37delete tile; 38} 39};

And this function is called after the main game loop is done:

void EndAllegro()
{
al_destroy_bitmap(ascii);
al_destroy_timer(timer);
al_destroy_display(display);
al_destroy_event_queue(event_queue);
}

X-G

EDIT: Nevermind, I see your error. You're using the wrong kind of delete. You allocated the tile array with new[], so you have to delete it with delete[].

Gatleos

So:
delete[] tile;
like that?

Doesn't seem to get rid of the assertion failure. There must be something else in addition to that.

X-G

Yes, that's right. By the way, you never free the pointers inside the array. Also make sure you're not making any accidental copies of that class anywhere (doing so is a sure way to crash -- your class does not have full copy semantics).

It's entirely plausible for there to be more errors like this around. Check around for suspicious deletes, and paste if you have more details for us.

Edgar Reynaldo

Gatleos, where are your indents? :o

Also, you're not deleting your Tile** properly. You need to loop over the contents and delete [] those before you delete [] the Tile**.

tile = new Tile*[width + 1];
for (int i = 0 ; i < width + 1 ; ++i) {
   tile[i] = new Tile[height + 1];
}

for (int i = 0  ; < width + 1 ; ++i) {
   delete [] tile[i];
}
delete [] tile;

Gatleos

I thought I needed to delete the "rows" of the array first, I was trying to think of a way to gauge the size of the array to make sure I got all of them. Then I remembered I had a perfectly good variable called window.w as part of the class that I made for exactly that purpose. SIGH

If you think my lack of indents here is bad, you should see the atrocious ocean of poorly-formatted code that constitutes that library I'm building. Speaking of which, part of that library is probably where the error is originating. Thanks for the help so far, I'll post more if I find anything suspicious.

But first: the bitmap called ascii is declared and defined in the object library but freed at the end of my main program by the EndAllegro function. Could that have to do with the assertion failure?

Edgar Reynaldo

Ehhh, directly destroying a library resource from a client application seems like a bad practice to me. Just give your library a CleanUp function and call that from EndAllegro. If you're not destroying it twice, then I don't see why that would cause the assertion failure though.

X-G

All that matters if whether they are part of the same heap or not. But you know, when that debug assertion is hit, you should be able to get a stack trace out of windows that will show you exactly what caused it. What compiler do you use?

Gatleos

VC++ 2010. Euugghh.

How can I get the stack trace then?

X-G

Run your program from inside the IDE, with F5 (not Ctrl+F5). When the assertion hits, press "Retry".

Gatleos

Done. I got a message from the compiler instead:

{"name":"ABLKi.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/d\/8d13af5fac5e3afd59c27cdc5e220511.png","w":528,"h":237,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/d\/8d13af5fac5e3afd59c27cdc5e220511"}ABLKi.png

X-G

Yes, now Break. You will get the debugging view in MSVC, which has all the goodies including a stack trace.

Gatleos

AAAAAHHHHHAAAAAA!!!

I isolated the problem to this function call:
Window Game(1,1,25,20);
the errors stop when I comment it out.

The Window object is part of a class that recreates the newwin() function from pdcurses. The function call above is declaring a Window called Game and calling the class constructor that allocates a 2d array to store the ascii symbol and color data of every "tile" in that window. Something in that constructor must be causing a run-time error 75% of the time when the program starts and a debug assertion failure 100% of the time when the program terminates.

Phew. Here's the Window class again:

#SelectExpand
1class Window 2 { 3 public: 4 int x;//Window x offset 5 int y;//Window y offset 6 int w;//Window width 7 int h;//Window height 8 Tile **tile;//2d array of contents of window 9 Window(int xpos,int ypos,int width,int height) 10 { 11 tile = new Tile*[width+1]; 12 for(int i=0;i<=width+1;i++) 13 { 14 tile[i] = new Tile[height+1]; 15 } 16 for(int i=0;i<=width+1;i++) 17 for(int j=0;j<=height+1;j++) 18 { 19 tile[i][j].symbol=0; 20 tile[i][j].color="white"; 21 tile[i][j].backcolor="black"; 22 } 23 x=xpos; 24 y=ypos; 25 w=width; 26 h=height; 27 } 28 ~Window() 29 { 30 for(int i=0;i<w;++i) 31 delete[] tile[i]; 32 delete[] tile; 33 } 34 };

And here is the Tile struct (an array of these is allocated in the class constructor):

struct Tile
  {
  int symbol;
  const char *color;
  const char *backcolor;
  };

The call to the tile ID in a window looks like this:
Game.tile[i][j].symbol

What could be causing all this trouble in this code?

X-G

Well for one thing you're overrunning all your buffers:

Quote:

for(int i=0;i <= width+1;i++)

Gatleos

Okay, I solved the assertion failure and isolated the run-time error. Now that I can get at it, it turns out it is a memory write error. It's a very strange one, though, in this function:

#SelectExpand
1void Draw_Char(int code,Window window,int x,int y) 2{ 3ALLEGRO_COLOR backcolor=al_map_rgba(0,0,0,255); 4if(code<0 || code>255) 5code=63; 6int backcode=219; 7int row = code / colnum; 8int backrow = backcode / colnum; 9if(x<window.w && y<window.h) 10{ 11window.tile[x][y].symbol=code; 12window.tile[x][y].color="white"; 13window.tile[x][y].backcolor="black"; 14} 15while(code>=colnum) 16code-=colnum; 17while(backcode>=colnum) 18backcode-=colnum; 19if(x<=window.w && y<=window.h) 20{ 21al_draw_tinted_bitmap_region(ascii, backcolor, tilewidth*backcode, tileheight*backrow, tilewidth, tileheight, (tilewidth*x)+(tilewidth*window.x), (tileheight*y)+(tileheight*window.y), 0); 22al_draw_bitmap_region(ascii, tilewidth*code, tileheight*row, tilewidth, tileheight, (tilewidth*x)+(tilewidth*window.x), (tileheight*y)+(tileheight*window.y), 0); 23} 24}

This function is overloaded; the first one takes an ascii code and coordinates and prints it at that location. The second one (shown above), takes a Window variable as an additional argument. At line 11 through 13, the function saves the tile data to the Window object. Line 11 is where the error occurs.

Like I said, it's a memory write error. What's strange, though, is that it only produces the error the second time it cycles through. This function is called each frame of the game loop, but the first time it works perfectly. No variables change, the function is just run twice with the same parameters and only crashes the second time. I'm stumped. :-/

Edgar Reynaldo
Gatleos said:

if(x<=window.w && y<=window.h)

You're checking the wrong bounds again.

Quote:

void Draw_Char(int code,Window window,int x,int y)

You're passing window by value, and not by reference. That means whatever you do to modify window will be done to a local copy and not to the Window you're passing to the function. It works in this case because the local copy of window has the same Tile** tile that the one passed to the function does.

I don't know why line 11 is failing though, sorry.

Gatleos

You're checking the wrong bounds again.

Those bounds are consistent with the allocation, I just changed it earlier.

Anyway, I narrowed it down even more. The values in Game.tile[x][y] are all initialized in the constructor, and the debugger confirms this. However, after I pass the window variable to the Draw_Char function all three pointers immediately go bad.

Edgar Reynaldo said:

You're passing window by value, and not by reference. That means whatever you do to modify window will be done to a local copy and not to the Window you're passing to the function. It works in this case because the local copy of window has the same Tile** tile that the one passed to the function does.

Like you said, I'm passing by value and not reference. The only reason I do it that way is because there may be multiple Window variables to be passed to that function at different times. Could I still do that by passing by reference? That may be just what I need to do to fix this.

Edgar Reynaldo
Gatleos said:

The only reason I do it that way is because there may be multiple Window variables to be passed to that function at different times. Could I still do that by passing by reference?

Each call to the function will have it's own local memory on the stack. No need to worry about cross contamination. Passing by reference& allows you to modify the object directly (which in this case, is what I believe you want to do). If you're going to have multiple threads calling Draw_Char on the same window then you could have problems.

I just figured out why you're having problems. When the local copy of window goes out of scope at the end of Draw_Char, it's destructor gets called. The first time it is passed to the function it's Tile** tile is still valid so that's why it doesn't fail the first time. When you pass in window again, the Tile** it has has already been destroyed, so that's why you get a memory access error. Using a Window& window will solve this problem.

Gatleos

Even closer. NOW, I get a compiler error about ambiguous functions. My function that draws strings of characters calls Draw_Char at the end of a loop, and according to the compiler more than one overloaded function matches its argument list.

Overloaded Draw_Char functions:

void Draw_Char(int code,int x,int y);
void Draw_Char(int code,int x,int y,const char *colorset,const char *backcolorset);
void Draw_Char(int code,Window& window,int x,int y);
void Draw_Char(int code,Window& windowcolor,int x,int y,const char *colorset,const char *backcolorset);

As you can see, none of them have the same arguments. This has to have something to do with changing the Window to a Window& in all the functions. Or, does it have to do with the fact that I'm passing the reference to a function from inside a function?

Edgar Reynaldo

That doesn't make sense.

Post the entire compiler error(s). And the line(s) it says are ambiguous.

Do your function declarations match your function defintions?

Gatleos said:

Or, does it have to do with the fact that I'm passing the reference to a function from inside a function?

No, there's nothing wrong with that. Just make sure you're only passing your Window by reference.

Gatleos

Never mind, I figured it out. Phew. Thank you very much for all the help. I never would have gotten this far without all that help. :)

Edgar Reynaldo

No problem. ;)

X-G

Please do note well: If your class allocates and frees memory in its constructor and destructor respectively, you must provide your own copy constructors and assignment operators, or you will have mysterious memory errors and crashes. The cause of your problem is, like I mentioned earlier, that you are making copies of the Window object and freeing the memory in its destructor while still trying to use it.

Thread #606323. Printed from Allegro.cc