2-in-1

2016-09-24
#devel #qmidiplayer #c++ #qt

The visualization plugin for QMidiPlayer in Linux has suffered from a dbus-related bug for a long time: after you have opened the visualization window, close it and try to bring it up again, it will either result in several lines of dbus warning which suggests there's a bug in the program (on Debian sid), or simply a crash (on Arch Linux).

Well, I have to admit the design of QMidiPlayer's visualization architecture is pretty weird: the visualization plugin spawns its own thread and do almost everything in it. After doing some research on the Internet, I realized that everyone suggests that Qt and SDL shouldn't be used together, "use QtOpenGL instead". However I thought that will hang the main UI thread and I just don't believe in the myth.

So I started my investigation in early May. I even opened an related issue on github. Unfortunately nobody offered help. So I had to solve the issue myself. First I had a look into the dbus warnings:

process <PID>: arguments to dbus_connection_unref() were incorrect, assertion "connection->generation == _dbus_current_generation" failed in file ../../dbus/dbus-connection.c line 2822.
This is normally a bug in some application using the D-Bus library.

Qt definitely uses dbus. After spitting these into stderr, QMidiPlayer crashes instantly in the QDBusConnection thread. Googling the warning turned out nothing. Then I came across with this blog post from a KDE developer:
https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0

Unfortunately, after applying the "fix", the bug didn't change even a little bit. So I had to move on.

UPD 2016-10-28

After several months' idleness of the investigation due to the lack of idea, I suddenly decided to have a look at all dbus calls when QMidiPlayer crashes. Then I found the following dbus call:

path=/org/freedesktop/ScreenSaver; interface=org.freedesktop.ScreenSaver; method=Inhibit;
string "My SDL application"
string "Playing a game"

Apparently SDL is also using dbus to do some dirty work -- disabling the screen saver! So I appended SDL_EnableScreenSaver in SMELT's init function. Unfortunately, nothing happened again. However I've guessed out the cause to the problem: both Qt and SDL try to initialize a dbus connection in one process, which is not allowed. So I have to get rid of either one of them.

Therefore I began to port the SMELT engine to GLFW. After two days of work, the port was finished and it worked like a charm -- I have solved the problem!

BUT THAT'S NOT THE END OF THE STORY.

Driven by the unstoppable merriness after fixing a long-existed bug, I started to build GLFW version of SMELT for Windows. The process was painful but finally I managed to do it successfully. After that it's time to compile the visualization library. A beautiful DLL file, sized 1096192 bytes, was produced. This build is almost 4x smaller than the previous failed mingw build and I regarded this as a omen of fortune.

I put the DLL file into the plugin folder of QMidiPlayer and launched the application. No errors occured as the mingw build once caused and the plugin showed up correctly in the plugin manager. I was so excited that I turned on the plugin immediately and I couldn't wait to announce the result to my friends. However when I hit the visualization I was greeted by a déjà vu scene.

And this nasty little dialog from the Windows 2000 era.

So another tale of a battle between a fierce bug and me is due to happen. Stay tuned and watch the fight!

[1]

UPD@2016-12-28

So the bug has been (mostly) fixed by the following two commits!
b79c4b7e3cab3711e87ba9e28fa8423a84ea7efa@QMidiPlayer
b13f72857af93489f535b84d62882f681dc84a73@SMELT

Basically the most tough part to debug is caused by the stupidest typo...

@@ -62,7 +62,7 @@ bool SMELT_IMPL::smInit()
  		else if(i==0x80000004)
  		memcpy(cpuName+32, CPUInfo, sizeof(CPUInfo));
  	}
 -	while(*cpuName=' ')++cpuName;
 +	while(*cpuName==' ')++cpuName;
  	smLog("%s:" SLINE ": CPU: %s\n", SYS_SDL_SRCFN,cpuName);
  	free(loced);

This naïve mistake screwed up the memory and this should be the cause that made the debugger report different stack traces in different build modes... (that reminds me of the experience of screwing up the call stack, in which the backtrace was filled up by "0x0000000000000000 in ??()"...)

After having fixed this, it was much easier to debug the application. Soon afterwards the first working visualization plugin for QMP on Windows came out.

Finally I have to make more complaints about the develop environment for a cross-platform C++ application developer in Microsoft Windows. It's bloated, complicated and confusing. I wonder whether Microsoft is deliberately making it difficult in order to force more developers to migrate to C#/.Net. What a big ambition Microsoft is bearing!



[1]: Current status of the bug in Windows:
Release build QMidiPlayer+Release build plugin=crash at somewhere in visualization.dll.
Release build QMidiPlayer+Debug build plugin=crash at somewhere in Qt5Core.dll.
Debug build QMidiPlayer+Debug build plugin=crash instantly on launch at plugin initialization.
I have totally no idea about what to do next.