Help! My project is crashing and I don't know why! :((

A while back now (over a year?) I posted a little adventure game my daughters thought up.

It’s sweet, though it goes really fast. There’s lots of kid art in it–perhaps not too interesting to those of us who aren’t breeders, but for the childed there may be a twinge of affectionate recognition.

Anyway I always thought I’d submit it to the App Store, and recently I’ve been trying to finish it up.

Big problem: it crashes! Consistently!

I’ve assumed it had something to do with loading/unloading all that art, so I’ve done everything I can think of to make sure the images are dumped from RAM once they’re not needed. But it still crashes.

If anyone can give me pointers as to where I’m going wrong, I’d be very grateful.

To reproduce the crash, just run one of the games and tap on the buttons very quickly. For me the crash usually comes around the 12th-17th button press.

Project at:

https://gist.github.com/GlueBalloon/594f2a8ab825d4ec232d

Needed assets at:

https://www.dropbox.com/sh/hg5cplngi7v7268/AAA8ezcK_ZJk2gZNF52sAvL_a?dl=0

I’m not sure there’s a quick way to just see the problem by running the code.

Two things I’d suggest

  1. Add a bunch of print statements that tell you where the code has got to

  2. Make sure they don’t disappear when the program crashes, either by saving them in local storage or writing them to a code tab

Then by seeing what was printed just before the crash, you can start narrowing down the place that causes the crash, until you find the offending line.

If the crashing is new, I would also keep in mind the things that changed in Codea since you wrote the app.

I couldn’t get the program work - I don’t think all the images are in the Dropbox link although I couldn’t find an easy way to dump them into the project folder (I tried copying them into Dropbox and doing a global replace of Project: with Dropbox: but getting errors)

For me, I’ve try it without images (just override the sprite function and add a little line in inventory about image width), and all works correctly, so, I think the problem is around the image and the memory… If I have some times, maybe I will retry with images (If there are all on Dropbox…). Sorry, I can’t help more for the moment…

However, I’ve look a little at your code, and I can give you some tricks :


Utilities:thisIfThisIsThisElseThis : a == b and c or d


function Utilities:isPointInRect(point, rectX, rectY, rectWidth, rectHeight)
    --a variable to return, initially stating the point to be outside the rect
    local pointIsInRect = false
    --if the point is inside the rect's values, make the variable true
    if point.x > rectX and point.x < rectX + rectWidth and point.y > rectY 
        and point.y < rectY + rectHeight then
        pointIsInRect = true
    end
    --send back the result
    return pointIsInRect
end

When you manipulate boolean, return it directly :wink:

function Utilities:isPointInRect(point, rectX, rectY, rectWidth, rectHeight)
    return point.x > rectX and point.x < rectX + rectWidth and point.y > rectY and point.y < rectY + rectHeight
end

*.5 is faster than /2


function Utilities:arbitraryValueFromKeyValueTable(keyValueTable)
    --a variable to hold the arbitrary value
    local returnMe = nil
    --iterate through the table
    for key, value in pairs(keyValueTable) do
        --grab the first value (unpredictable: pairs(table) doesn't guarantee consistent sorting) 
        returnMe = value
        --stop after first iteration
        break
    end
    --send the value back
    return returnMen
end

In Lua, you can directly use next

local k,v = next(tbl) -- return "first" (key, value) pair from a table

function Utilities:doesTableHaveStringKey(tableToCheck, stringKey) It’s just tableToCheck[stringKey]


I can’t look at all, but this link is really useful : learn lua in 15 minutes

Good luck!

@HyroVitalyProtago, thanks for the close reading!

That’s really interesting results on commenting out the images. I’ll have to try it too. Thanks a lot!

I also appreciate your code tips–especially on thisIfThisIsThisElseThis!

Some of the these are conscious decisions though:

  • I often prefer to assign return values to separate variables before returning them. It makes them easier to inspect later if something goes wrong.
  • I like clear variable names that help me remember what something is if I return to the code months and months later (like I am now).
  • tableToCheck[stringKey] is great for some situations but not all. It returns different values than my utility method; it’s either a value or nil. The utility method returns true or false. Those aren’t equivalent. (I should forewarn you that I’ve had long, long arguments with friends over whether or not true and false are meaningfully different from nil and not nil. I’d really like to avoid that. I hope we can just agree that they are different, and leave the decision about meaningully different aside as a matter of personal preference.)
  • I wrote this for my daughters, and I had that in mind at all times. I hope someday they’ll be interested in looking at the code for themselves. I’d like them to understand why I did every single thing I did.
  • Case in point: Utilities:arbitraryValueFromKeyValueTable(keyValueTable) tells you explicitly that it’s returning an arbitrary value. The only way to know that local k,v = next(tbl) is not guaranteed to be a consistent value is to happen to have read that specific part of the lua manual–which I actually had–and happen to have remembered it–which I hadn’t. Since I had to look it up twice, I made some code that would help me never have to look it up again, as well as explain it to Charlotte and Rosie if they ever cared to find out.
  • That said, local k,v = next(tbl) is a much better way to write what’s inside my explicit utility method! Thanks!

@West–oh no! Can you possibly identify which images are missing?

There actually is a really easy way to move things from the Dropbox folder to the Project folder. If you tap the ‘edit’ button at the top of the asset window, you can select a bunch of items at once, and then move them to a different folder by tapping ‘add to’ at the bottom of the asset window.

@HyroVitalyProtago, can you tell me how you got it to run without images?

You said you overrode sprite, which I’ve done this way:

r,g,b = math.random(256) - 1, math.random(256) - 1, math.random(256) - 1
--overriding sprite for debugging
function sprite(name, x, y, width, height)
    rectMode(CORNER)
    fill(r,g,b,255)
    local thisWidth,thisHeight = width, height
    local thisx, thisy = x - (thisWidth * .5), y - (thisHeight * .5)
    rect(thisx, thisy, thisWidth, thisHeight)
end

And you said you did something with width in Inventory, which I took my best guess at. In the draw method I commented out this:

        itemX = itemX + self.spaceBetweenIcons + (item.icon.width / 2)

And inserted this:

        itemX = itemX + self.spaceBetweenIcons + 100

…but all I get is a flat colored screen that complains about mainScreen Being undefined whenever I touch it. Was there anything else you did?

@HyroVitalyProtago - I got it working finally, my sprite() override was flawed and I fixed it.

The crash still happens, though. It just takes a lot more taps, between 30 and 50. So there’s a good clue here, I just don’t for sure see what it is.

I’m definitely leaking memory somewhere. I used @Yojimbo2000’s memory profiler and the memory just climbs up and up and up and never goes down. Useful tool, Yojimbo!

Another odd thing is that this totally-static project has an atrocious frame rate, around 12-15 FPS. I’m doing something very wrong.

…FPS was being killed by backingMode(RETAINED) once again. I guess as a noob I had thought retaining something meant less work for Codea–obviously not!

Glad to see I can help a little! I understand all your points about conscious decisions.

From my point of view, I prefer writing something like this

function Utilities:doesTableHaveStringKey(tableToCheck, stringKey)
    return tableToCheck[stringKey] ~= nil
end

or, at least :

function Utilities:doesTableHaveStringKey(tableToCheck, stringKey)
    for k,v in pairs(tableToCheck) do
        if k == stringKey then
            return true
        end
    end
    return false
end

even if I understand your first point about assigning the return value before returning it…

I expect one day your daughters will be in measure to understand it!

backingMode(RETAINED) tell to Codea to copy the buffer between each draw, so, it’s useful for drawing app but not really for others…

=> Is your your problem entirely resolved?

BackingMode should just be set once, it’s not something you invoke in every draw cycle, as it will eat frame rate (that was the issue with your 3d plane touching code, AFAIR)

I haven’t tried to run this code, but handling lots of images can be tricky. Keep an eye on file sizes, some PNGs can be quite large. I guess you need them to be PNGs for the alpha channels?

@Yojimbo2000, I think it can’t be a problem with any one image’s size.

If it was I then it would consistently crash on that screen, but the crash doesn’t correlate with any particular screen–it correlates with the amount of times that new screens that have been loaded.

Every time I load a new screen, I nil out the old screen, but I think the current facts imply that some reference somewhere is causing every screen to be retained, ultimately leading to a crash.

@HyroVitalyProtago, no, not resolved, sad to say. When I override sprite(), the game still crashes, but it takes 2-3 times more screen transitions. Around 50 total. If you wouldn’t mind, it would be a help if you could confirm similar results.

EDITED

So, even without images, I got the same crash after (maybe more than 50 for me…).

Even if this is not the problem, I’ve looking at the use of readImage, and you use it on each new screen… I don’t know if there is some memoization behind it, but if there isn’t one, maybe do it by yourself like in this little example (this is the code I use for testing the app)

local __test = {}
local __readImage = readImage
function readImage(...)
    local k = select(1, ...)
    if not __test[k] then
        print("[readImage] new image ", k)
        __test[k] = __readImage("Cargo Bot:Crate Blue 1")
    end
    return __test[k]
end

local __sprite = sprite
function sprite(...)
    local fst = select(1, ...)
    if fst then
        __sprite(...)
    else
        ellipse(select(2, ...))
    end
end

I’ll try to see why there is crash…


I see a lot of calls of setContext, and I think this can be the origin of the problem… I try to look at more


I don’t understand why, but with this, I can’t crash the app…

local __setContext = setContext
function setContext(...)
    saveProjectTab("trace", debug.traceback())
    return __setContext(...)
end

local __sprite = sprite
function sprite(...)
    local fst = select(1, ...)
    if fst then
        __sprite(...)
    else
        ellipse(select(2, ...))
    end
end

In your sprite override, it looks as if you 1) check if it was called with arguments, and 2) if it was, you do a normal sprite call with them, and 3) if there were no arguments, you call ellipse(select(2,…). It seems like there’s a logical flaw in that ellipse will never be called with any arguments at all.

Regarding loadImage, I’ve been chasing image-loading-related crashes for a long time. I used to load all the project images at startup, but that caused crashes immediately; then I changed to loading only all the images for one game whenever that game was run, but that caused quick crashes too; so finally I settled on loading only one screen’s images at a time, hoping to accept stability in exchange for a slight lag between screens. It’s definitely more stable, but not completely, so arggh. Can’t get more granular than that unless I only load images when someone looks directly at them. :smiley:

For my sprite override, the signification is : if the first argument is not nil, use sprite normally, else, use ellipse with the rest.

In Codea, I don’t think you can “unload” images from memory…

I would have thought if you re-use the same variable names, the previous images will get garbage collected

Try adding collectgarbage() after images get overwritten. You don’t want to overdo it with collectgarbage calls, but a few strategically placed ones help. I have a program that redefined lots of images on orientationChanged, and after a few flips it would crash Codea. Adding collectgarbage to the end of orientationChnaged fixed it.

Images don’t exactly get overwritten.

Images are stored by Screen objects, and currentScreen gets overwritten every time the scene changes.

And no, I don’t keep an index of Screens–at least I don’t think I do. I keep a table of parameters for assembling each screen, and when it’s time to present a new screen I run assembleScreen(screenTable). If I’m accidentally storing a reference to the assembled screens somewhere, I don’t know about it.

@Yojimbo2000, so should I add collectGarbage() after I overwrite currentScreen?