Crash in QTreeWidget / QTreeView index mapping on Mac OSX 10.10 part III

So, one more attempt. Previous articles (part1, part2) mentioned possible solutions to fix crash insinde the QTreeWidget and QAccessibleTableCell.

Unfortunately, deselecting current item still doesn’t fix all issues. The problem with deselection is that it’s not handled correctly via QAccessibleTableCell:

If current index isn’t valid, QAccessible doesn’t correctly update currect QAccessibleTableCell object which caused all this evil crashes.

So there are two ways how to fix it. One is to manually set QAccessibleTableCell but I didn’t find a way how to do that. The second way is to disable QAccessible for Property editor. Unfortunately it’s not an easy task. In QAccessible exists method setActive:

QAccessible::setActive(false)

But this method works only with AccessibleObserver class but not with QPlatformAcessibility which handles real QAcessible::isActive result. Because of this it’s necessary to update setActive method in file qacessible.cpp inside the Qt.

Original method:


void QAccessible::setActive(bool active)
{
  for (int i = 0; i < qAccessibleActivationObservers()->count() ;++i)
    qAccessibleActivationObservers()->at(i)->accessibilityActiveChanged(active);
}

and updated version:

void QAccessible::setActive(bool active)
{
  #ifndef QT_NO_ACCESSIBILITY
    if ( QPlatformAccessibility *pfAccessibility = platformAccessibility() )
      pfAccessibility->setActive(active);
  #endif

  for (int i = 0; i < qAccessibleActivationObservers()->count() ;++i)
    qAccessibleActivationObservers()->at(i)->accessibilityActiveChanged(active);
}

In case you will find better way how to turn off accessibility, please let me know.

But there is still one more problem. Occasionally OS X decides to switch accessible back on:

And this is probably the reason why everything works ok on older OS X but not on 10.10. Because OS X 10.10 randomly turns accessible on which causes this crash. Accessible is usually turned on when opening some modal dialog.

This explains why opening an empty dialog increased the probability to crash the application. The only solution I have found is to disable QAccessible every time the PropertyEditor is cleared up.

Qt5 application crashed with error 0xc0000005

This is very interesting crash and I think it should be considered as Qt bug. This problem arise only under very special circumstances. But one after another.

You will identify this problem when your application stopped/exited immediately after the start without any visible error message. It looks like the application isn’t executed at all. When you check application log (Computer -> manage -> Event viewer -> Windows logs -> Application), you will see Error logs:

Windows applications logs

The most interesting part of this log is crash location: ntdll.dll

Faulting application name: Skipper.exe, version: 3.0.1.1120, time stamp: 0x53e9c8d7
Faulting module name: ntdll.dll, version: 6.1.7601.18247, time stamp: 0x521ea8e7
Exception code: 0xc0000005
Fault offset: 0x0002e3be
Faulting process id: 0x1c88
Faulting application start time: 0x01cfb606553e594b
Faulting application path: T:\S2\Skipper.exe
Faulting module path: C:\Windows\SysWOW64\ntdll.dll
Report Id: 98cc8228-21f9-11e4-ab5d-005056c00008

At first sight it seems like some problem inside the windows. But the opposite is true, the problem (as almost always) is inside your app ;-).

As the next step, you can try to debug this executable via Visual Studio to see what happens inside. Simply open executable as project together with .pdb files and execute it. Now you can see that application is correctly executed but crashes as soon as it touches Qt library. The location of crash is inside ntdll.dll in RtlHeapFree() function.

Debuging crash in VisualStudio 2013

So the problem is inside the Qt, right? Almost true, but not for the 100%. When I tried to run this application on computers of my colleagues, everything works ok. So why the application doesn’t work on my computer too?

Resolution

The problem is in new Qt5 plugin system. Besides the common Qt5*.dll files which are loaded immediately after the application start, Qt5 is also loading plugins/platform-plugins dynamically when the application is executed. To locate this plugins, Qt5 uses following method to identify directories where to search for plugins:

QStringList QCoreApplication::libraryPaths()

For some strange reason this library returns as first directory path where Qt5 libraries were compiled and after that location based on the executable. So if your Qt5 path is C:\Qt5, this will be the first path where all plugins are searched for, no matter if the correct version of plugin is located in APP\plugins or APP\platforms. I think this is serious bug in Qt5.

Where is the problem?

And here we’re getting to the core of the whole problem.

If application is compiled on computer with one compiler and used on second  computer which contains the same path to which original computer has installed Qt, the application will load all plugins from your folder instead of itself folder.

In case your computer will contain different version of Qt, different compiler or different platform, application loads incorrect libraries and crashes. Completely, silently and without easy way to determine what’s wrong.

Solution?

The solution is simple, but it isn’t achievable from outside of the Qt library. It would be necessary to Qt as first tried to load libraries from application directory. And only if no plugins were found in application directory, the application would try to search for plugins in Qt directory.

Qt change solution

The simplest way how to fix this issue inside the Qt library would be to rename/update appendApplicationPathToLibraryPaths  function to prependApplicationPathToLibraryPaths and change

void QCoreApplicationPrivate::prependApplicationPathToLibraryPaths()
{
#ifndef QT_NO_LIBRARY
    QStringList *app_libpaths = coreappdata()->app_libpaths;
    if (!app_libpaths)
        coreappdata()->app_libpaths = app_libpaths = new QStringList;
    QString app_location = QCoreApplication::applicationFilePath();
    app_location.truncate(app_location.lastIndexOf(QLatin1Char('/')));
#ifdef Q_OS_WINRT
    if (app_location.isEmpty())
        app_location.append(QLatin1Char('/'));
#endif
    app_location = QDir(app_location).canonicalPath();
    if (QFile::exists(app_location) && !app_libpaths->contains(app_location))
        //CHANGE THIS ROW: app_libpaths->append(app_location);
        //TO FOLLOWING
        app_libpaths->prepend(app_location);
#endif
}

InApp solution

Unfortunately it isn’t possible to simply change this behavior from your app. All of these operations happen directly in QCoreApplication constructor so if you try to change it after, it’s too late.

The temporary solution before this problem will be resolved is to reinitialize library paths before QCoreApplication is initialized. It’s necessary to clean libray paths, compute new paths and re-initialize QCoreApplication::libraryPaths before QCoreApplication object is initialized. This can be done in main.cpp of your application before you will create QApplication/QCoreApplication object.

  QString executable = argv[0];
  QString executablePath = executable.mid(0,executable.lastIndexOf("\\"));
  QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
  QCoreApplication::removeLibraryPath(installPathPlugins);
  QCoreApplication::addLibraryPath(installPathPlugins);
  QCoreApplication::addLibraryPath(executablePath);

It’s not a nice solution, but it works. I tried  to report this issue also to bugreports.QtProject, so maybe in later version this will be fixed.

Qt app crash on mac when executed with parameters

Our reporting tool occasional crashed on MacOs when was executed with several command line params. Command looked like this:

/app-path/OrmDesigner2 -crash-report -dump-directory /var/folders/4z/k1r35jnn4v77mzl7hzf8wvvw0000gn/T/OrmDesigner2/CrashReports

When inspecting exception report, we found following stack trace:

Thread 0 Crashed:  Dispatch queue: com.apple.main-thread
0   QtCore                         0x0000000100d671da QString::fromLocal8Bit(char const*, int) + 42
1   QtCore                         0x0000000100e72dac QCoreApplication::arguments() + 124
2   QtGui                          0x00000001000de8ef -[QCocoaApplicationDelegate application:openFiles:] + 223
3   com.apple.AppKit               0x00007fff805f3b52 -[NSApplication(NSAppleEventHandling) _handleAEOpenDocumentsForURLs:] + 505
4   com.apple.AppKit               0x00007fff804c0065 -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] + 217
5   com.apple.Foundation           0x00007fff800f90d6 -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] + 360
6   com.apple.Foundation           0x00007fff800f8f06 _NSAppleEventManagerGenericHandler + 114
7   com.apple.AE                   0x00007fff82b1c32b aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 162
8   com.apple.AE                   0x00007fff82b1c224 dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 32
9   com.apple.AE                   0x00007fff82b1c12b aeProcessAppleEvent + 210
10  com.apple.HIToolbox            0x00007fff875ed619 AEProcessAppleEvent + 48
11  com.apple.AppKit               0x00007fff803c5095 _DPSNextEvent + 1191
12  com.apple.AppKit               0x00007fff803c4801 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 155
13  com.apple.AppKit               0x00007fff8038a68f -[NSApplication run] + 395
14  QtGui                          0x00000001000e7c04 QEventDispatcherMac::processEvents(QFlags) + 1588
15  QtCore                         0x0000000100e70774 QEventLoop::processEvents(QFlags) + 68
16  QtCore                         0x0000000100e70a94 QEventLoop::exec(QFlags) + 180
17  QtCore                         0x0000000100e720bc QCoreApplication::exec() + 188
18  com.inventic.ormdesigner       0x0000000100003447 main + 46 (main.cpp:31)
19  com.inventic.ormdesigner       0x00000001000032e0 start + 52

After some google-searching I found out that the problem is in core of our application. When subclassing Qt QApplication, it’s necessary to pass argc with reference, not by value. So after adding one little &, everything works like charm!

//construct application object
CApplication(int& argc, char **argv);

External links