Mr. Bug Hunter

Some days ago I posted some rant(ish) comments about differences between the debug and the release version behavior. Today I’ve managed to find the bug, so thanks you guys for your help.

Programmer Tip: a crash course into “how to debug” can be found from the comments of that blog post.

In retrospect, I think it’s pretty hard to know if this was “my code problem” or “Leadwerks engine problem”. It sort of lies in between. I could argue that the difference between debug mode and release mode was caused by Leadwerks, but in the same time I kind of feel that if I had used a better coding structure, I would have never experienced the problem. I just let you decide whose the blame.

Anyway, here’s the problem and the solution. I try to explain this in a non-techy way right after the code parts, so check this out:

(This was my original code, it worked only in debug mode)

function drawhovertext(entity:tentity)
   if (entity = null)
       return
   endif
   physicalObject = TPhysicalObject(GetEntityUserData(entity))
   drawtext(physicalobject.hovertext)
end function

function update(...)
... do stuff
   entity = campick.entity

   updateappspeed
   updateworld
   renderworld

   drawhovertext(entity)
end function

And here’s the code that works (I’ll explain the difference below).

function sethovertext(entity:tentity)
   physicalObject= TPhysicalObject(GetEntityUserData(entity))
   self.hovertext = physicalobject.hovertext
end function

function drawhovertext(entity:tentity)
   drawtext(self.hovertext)
end function

function update(...)
... do stuff
   entity = campick.entity
   sethovertext(entity)

   updateappspeed
   updateworld
   renderworld

   drawhovertext()
end function

So, basically all you programmer geeks can perhaps read what’s going on but I still want to explain what was happening.

  • I was basically doing messing with an “entity”, then running “update world (and collisions)”, and then “drawing text on screen using the entity”. This didn’t work.
  • I changed things that I first “messed with the entity, prepared the hover text”, then run “update world (and collisions)” and after that “draw text on screen using a prepared variable”. This worked.
  • Also notice that entity was usable all the time – I wasn’t trying to use a “null” entity. It was only a collision of physics controllers (of all characters) that weren’t working.

The entity was no longer used after “collision check”, and basically I could have prevented this by simply “preparing things for render” before doing collision checks, renders and other things – and then simply draw the textual parts (in a “dummy way”) to the screen.

I’m glad that the problem was sorted out (but whose fault was it…?)

[poll id=12]

12 thoughts on “Mr. Bug Hunter

  1. I’m still learning C# myself but I believe Val hit the needle on the head. Its just good coding practice to ensure that the pointers get deleted/reset once the memory location they are pointing to is empty.

  2. Heh, actually, I considered putting BlitzMax as one additional vote option…

  3. I don’t understand how you are even using Self in a function.

  4. Yep, Sargon is correct. A piece of code that frees memory may or may not set the pointer(s) to NULL for performance reasons. It is typically good practice to do so to catch/avoid dangling pointer problems, but is not a requirement which is why it is important to know who is the owner of a piece of memory and to have a clear understanding of when the owner of that memory will potentially release it, and when it is safe to use. Use of dangling pointers are some of the nastiest bugs. :)

  5. I cannot really vote, because I think it’s a bug in BlitzMax itself.

    This would not have happened with C++, however you would have gotten many other wierd bugs (like particles not appearing) when running C++ in Debug mode :)

  6. I think that blaming yourself is good practice in coding. ;)
    Because only in rare cases its not your own fault.

  7. Could be something like that… well, I guess I need to hear from Josh @ Leadwerks to see who here to blame.

    (Not that I’d blame anyone, I’m really just one happy camper to see this thing solved)

  8. Many times when you run a program, and you have an uninitialized pointer, it might point to the data that it used to point to when you ran the program the last time(and by now you have already terminated that run).
    Its not exactly the same as in your case, but its an example how sometimes having data to which a pointer points to, doesn’t mean you are allowed to access that data.

  9. The entity not being null, doesn’t mean the memory it pointed to was still allocated.
    In matter of fact, sometimes when a pointer points to dynamic memory is freed, the data it pointed to is still there. It just isn’t suppose to be used, because somewhere it is marked as free, even though you can still access the data that used to be there.
    I just wonder why it didn’t catch you are accessing unallocated memory, though this can happen as well.

  10. Well, the funny thing is that the entity was usable after the loop. I could access to it, and it wasn’t null.

    The thing that occured was that collisions of player controller got messy.

    …It’s pretty darn hard to explain by text. Come here and I’ll show this to you (or video… hmm…). :)

  11. Who’s to blame depends on who owns the entity pointer/memory. If the API documents that “entity” will go away after collision detection, then you are at fault for not fully understanding the functions you are calling. But if it is not documented in the Leadwerks API that entity will go away, then they are to blame for shoddy documentation.

  12. I don’t exactly understand what you explain.
    From what you have written, it seems as if entity was no longer usable after using it for collision, thats why it was null in debug mode for drawhovertext, and garbage in release mode.
    I am not sure if blitzmax got garabage collection or other memory scheme, so I am not exactly sure what is the problem.
    But my guess is, thats its your fault. Because either the side effects of using leadwerks were probably documented, or its the kind of side effects that someone that use blitzmax should be aware of.
    Just like I can’t blame microsoft’s directx crashin if I pass it a bad pointer.
    But I am not sure yet, because I didn’t entirely understood the problem.