Finding crashes

Updated 4 months ago

Crashes are annoying to users and don't shed a good light on the software. Generally, all crashes are classified as "critical" bugs in the issue tracker.

Crashes can be divided into two categories. Program errors where the operating system stops the further execution of a program, because the program has attempted to perform an illegal operation (for example a reference through a null pointer, accessing an non-allocated address or even a divide by zero). The other category is where the program logic tries to detect invalid conditions. That can either be in the MuseScore application code, or in the underlying Qt runtime. Qt provides a macro Q_ASSERT (). If during program execution the condition isn't met, a message is printed to the console (with qFatal) and the execution is aborted with abort(). The Qt runtime internally uses Q_ASSERT; MuseScore application code uses both Q_ASSERT and abort(), typically after printing a debug message to the console with (qDebug or qFatal). In Windows, that looks like this: crash-image.png

The crashes in the first category are usually easy to find. The debugger will just stop and show you the location of where the exception occurred in the call stack. You then inspect the program logic. If no apparent solution springs to mind, you report your analysis in the issue tracker.

Crashes in the second category are harder to find. Since MuseScore application code uses abort() in various places, you need to look at the debug message, and then search through the source to locate the abort(). This bug calls for the removal of direct use of abort () to facilitate the use of the next option. This option is to place a breakpoint in the message handler (see below). To catch an abort caused by Q_ASSERT, trapping the message handler can be the only way to breakpoint before the crash, since the Q_ASSERT can come from within the Qt runtime and it may not be possible to use a breakpoint.

On Windows in more recent Qt versions, the Qt runtime abort () no longer activates the debugger, so the program just aborts without giving the developer the opportunity to inspect the call stack. Regarding this, we read in a Qt forum: If somewhere in the code a Q_ASSERT() evaluates false, the application is stopped with an info, where the Q_ASSERT() is and what expression evaluates false. If the application was run with a debugger, debugger is also stopped. If the developer wants to inspect a call stack, he must call qInstallMsgHandler() with a new MsgHandler, set a breakpoint in it and reproduce the bug.

Such a message handler already exists - it is located just before the main() function in mscore/mscore/musescore.cpp. Set your breakpoint where indicated in the code. The message handler is also useful, if you see debug output (printed via qDebug) and want to find out where it originates.

Note: you may have to set environment variable QT_LOGGING_TO_CONSOLE=1 to get qDebug messages to appear on QtCreator's Application Output window (noted by ericfont on Qt 5.12 on arch linux).