Linux/CLion: jassert seems to SIGTRAP in completely unrelated bit of JUCE library Code

I’m currently having a hard time tracking down a bug that suddenly appears in the audio callback of a standalone application, after some code changes elsewhere. I suspect it to be some kind of race condition, deletion of an object while still being used by some other thread… something like that. However what makes it nearly impossible to track down is the following fact:

I get two assertion failure prints from within AudioBuffer::copyFrom. However, the debugger does not stop there, instead I receive a SIGTRAP at that timepoint but the debugger points to some completely unrelated parts of the JUCE codebase (some low level GUI parts that blend some pixels). There are neither assertions in this part of the code, nor are there breakpoints. Furthermore, it does not always stop at the exact same line, sometimes it stops a few lines above or in the outer function that calls this function, so to me it seems that this is some part of the GUI rendering, happening on the MessageThread that is just always happening at the timepoint the real error happens on another thread by coincidence. Could that be possible that the SIGTRAP is received for a wrong thread? Did anyone encounter such a weird GDB behavior before and has some advice on how to fix this?

Assuming it is a concurrency problem, I would look for the object, that goes missing. By adding a breakpoint in that destructor you can find out, on which thread the destruction happens. You will then need to synchronise that thread with the one using the object.

You can make your life easy and do all creations and destructions on the message thread, that is low priority and easiest to synchronise (using the MessageManagerLock as last resort).
Take special attention to stuff created and destroyed on background threads.

I don’t have to mention the audio thread, there any creation and destruction is prohibited anyway.

Thanks Daniel for sharing this pieces of good advice, however I’m aware of all those things, I already built up quite complex multithreaded pieces of real-time capable DSP applications and I think I can also manage to fix the issues I have… But only if my debugger would work as expected :grimacing:

So I’m looking for answers to what’s wrong that obviously breakpoints/assertions are hit but the debugger stops at some completely unrelated part of the code and how this error can be fixed?

I also think that my initial assumption that the error happens on the audio callback (–> as the assertion came from an AudioBuffer) might be wrong, it seems more likely at the moment that it is triggered from within some ThreadPoolJob that does some none-time-critical background rendering on some audio data. However, it’s really quite hard if I just see a print that some assertion in an AudioBuffer was hit but the debugger doesn’t stop at the place where it happens, as there are a lot of AudioBuffers instances that could potentially be the source of the error. With the debugger stopping at the assertion I think the bug would most likely be easy to fix.

So once again, my issue here is the unexpected behaviour of the debugger.

I see, if you don’t mind another generic point: did you have optimisations enabled?

Apart from that, I don’t have anything to help you, sorry…

I don’t mind any point or question and I think this is a good one :wink: I have not, at least as far as I can see. I’m using the CLion exporter generated from the Linux Makefile exporter, which currently only consists of a debug configuration with the default options of a debug config, so -O0, no link time optimizations. However might there be any other locations for settings asides from the exporter settings where I could apply some optimizations I’m not aware of (in the Projucer or in CLion)?

Hey @PluginPenguin, did you ever get to the bottom of this issue?

I have been running into it for a while now and it’s driving me mad. I can reliably reproduce it in an out-of-the-box JUCE iOS project.

    button.onClick = []
    setSize (600, 400);

kill(0, SIGTRAP) (the underlying signal sent by jassert on iOS) seems to be handled asynchronously, stopping the debugger at a later time.

I really cannot even remember the circumstances and/or solutions back then, I’m sorry.

However, looking at your code I’m not sure what you want to illustrate there. If you are launching a new thread that then triggers the assertion it’s absolutely expected that this will happen asynchronously in relation to clicking the button as that’s the whole point of launching threads. So what exact timing do you expect to happen here and how does it differ to the timing that you experience?

Yes, the code inside the anonymous function passed to Thread::launch is expected to run async, however, I would expect jassert to pause the execution inbetween DBG("before") and DBG("after"). Instead, it pauses execution at later stage, making it useless (see screenshot below).

[not working as expected]

If I put a breakpoint on the same line, debugger pauses execution correctly, within the scope of the anonymous function. If I replace jassertfalse with assert(false), again, execution is paused as expected (see attached screenshot), but assert(false) has the undesirable behaviour of aborting program execution after assertion.

[working as expected]

1 Like