PDA

View Full Version : Qt 5.4 sudden segfault when calling invokable c++ function from qml



motaito
25th April 2015, 21:51
Hi,

I have these two situations:

class myClass
{
...
}

class myModel : public QAbstractTableMOdel
{
public:
...
Q_INVOKABLE myClass getElement(int index)
{
if(0 > index || index >= m_Data.size())
return 0;

return m_Data[index];
}

private:
QList<myClass> m_Data;
}
calling the getElement(index) function from qml works just fine. However, if I change to pointers like so:

class myClass : public QObject
{
Q_OBJECT
...
}

class myModel : public QAbstractTableMOdel
{
public:
...
Q_INVOKABLE myClass* getElement(int index)
{
if(0 > index || index >= m_Data.size())
return 0;

return m_Data[index];
}

private:
QList<myClass*> m_Data;
}
Then it first works fine as well. But every now and then I get a segfault when calling the getElement(index) function from qml. I don't understand how it can work most of the time but suddenly create a segfault. Also, I have no clue how to debug it any further. I narrowed it down to the actual function call but I have no clue why it would give a segfault. Can anyone tell my why this might be happening?

wysota
25th April 2015, 22:02
Show us the backtrace, please.

motaito
25th April 2015, 22:52
Uhh, this is embarrassing... I am not very familiar with debugging and the terminology. I am doing this as a learning project. Can you tell me how to get the backtrace in qt creator 3.4? I will be happy to provide any information.

wysota
25th April 2015, 22:57
Start your app in debugging mode. When it crashes, the backtrace will be presented in the bottom part of the window. You can access "Copy Contents to Clipboard" option from the context menu to copy the result to clipboard so that you can post it here. Other than that you can google gdb+backtrace.

motaito
25th April 2015, 23:13
I am not entirely sure, but I think I found it :)
Right click on the left debugger pane -> "create full backtrace"


Thread 4 (Thread 0x7fffeb542700 (LWP 2134)):
#0 0x00007ffff5c024ed in poll () from /usr/lib/libc.so.6
No symbol table info available.
#1 0x00007ffff17ef9f2 in ?? () from /usr/lib/libxcb.so.1
No symbol table info available.
#2 0x00007ffff17f156f in xcb_wait_for_event () from /usr/lib/libxcb.so.1
No symbol table info available.
#3 0x00007fffed53d399 in ?? () from /usr/lib/qt/plugins/platforms/libqxcb.so
No symbol table info available.
#4 0x00007ffff6c4c56e in ?? () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#5 0x00007ffff66f7374 in start_thread () from /usr/lib/libpthread.so.0
No symbol table info available.
#6 0x00007ffff5c0b27d in clone () from /usr/lib/libc.so.6
No symbol table info available.

Thread 3 (Thread 0x7fffeab36700 (LWP 2135)):
#0 0x00007ffff5c024ed in poll () from /usr/lib/libc.so.6
No symbol table info available.
#1 0x00007ffff3e5cc7c in ?? () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#2 0x00007ffff3e5cd8c in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#3 0x00007ffff6e901b7 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#4 0x00007ffff6e35852 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#5 0x00007ffff6c475d4 in QThread::exec() () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#6 0x00007ffff73517d5 in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#7 0x00007ffff6c4c56e in ?? () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#8 0x00007ffff66f7374 in start_thread () from /usr/lib/libpthread.so.0
No symbol table info available.
#9 0x00007ffff5c0b27d in clone () from /usr/lib/libc.so.6
No symbol table info available.

Thread 2 (Thread 0x7fffe9a84700 (LWP 2136)):
#0 0x00007ffff5c024ed in poll () from /usr/lib/libc.so.6
No symbol table info available.
#1 0x00007ffff3e5cc7c in ?? () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#2 0x00007ffff3e5cd8c in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#3 0x00007ffff6e901cc in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#4 0x00007ffff6e35852 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#5 0x00007ffff6c475d4 in QThread::exec() () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#6 0x00007ffff7370948 in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#7 0x00007ffff6c4c56e in ?? () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#8 0x00007ffff66f7374 in start_thread () from /usr/lib/libpthread.so.0
No symbol table info available.
#9 0x00007ffff5c0b27d in clone () from /usr/lib/libc.so.6
No symbol table info available.

Thread 1 (Thread 0x7ffff7e5c7c0 (LWP 2130)):
#0 0x00007ffff728a81f in QV4::QObjectWrapper::wrap(QV4::ExecutionEngine*, QObject*) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#1 0x00007ffff735352c in QV8Engine::fromVariant(QVariant const&) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#2 0x00007ffff728f247 in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#3 0x00007ffff72904dc in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#4 0x00007ffff7290b43 in QV4::QObjectMethod::callInternal(QV4::CallData*) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#5 0x00007ffff729c4f8 in QV4::Runtime::callProperty(QV4::ExecutionContext*, QV4::String*, QV4::CallData*) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#6 0x00007ffff729620d in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#7 0x00007ffff7297056 in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#8 0x00007ffff724c2fd in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#9 0x00007ffff733754f in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#10 0x00007ffff72d8c96 in QQmlBoundSignalExpression::evaluate(void**) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#11 0x00007ffff72d9293 in ?? () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#12 0x00007ffff7319fb7 in QQmlNotifier::emitNotify(QQmlNotifierEndpoint*, void**) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#13 0x00007ffff72b739c in QQmlData::signalEmitted(QAbstractDeclarativeData*, QObject*, int, void**) () from /usr/lib/libQt5Qml.so.5
No symbol table info available.
#14 0x00007ffff6e67b41 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#15 0x00007ffff7ccd012 in QQuickMouseArea::clicked(QQuickMouseEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#16 0x00007ffff7c03a76 in QQuickMouseArea::setPressed(Qt::MouseButton, bool) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#17 0x00007ffff7c0459b in QQuickMouseArea::mouseReleaseEvent(QMouseEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#18 0x00007ffff7b82028 in QQuickItem::event(QEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#19 0x00007ffff6e37c85 in QCoreApplication::notify(QObject*, QEvent*) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#20 0x00007ffff6e37dbb in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#21 0x00007ffff7b92753 in QQuickWindow::sendEvent(QQuickItem*, QEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#22 0x00007ffff7b92b22 in QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#23 0x00007ffff7b95546 in QQuickWindow::mouseReleaseEvent(QMouseEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#24 0x00007ffff759df25 in QWindow::event(QEvent*) () from /usr/lib/libQt5Gui.so.5
No symbol table info available.
#25 0x00007ffff7b9a1b3 in QQuickWindow::event(QEvent*) () from /usr/lib/libQt5Quick.so.5
No symbol table info available.
#26 0x00007ffff6e37c85 in QCoreApplication::notify(QObject*, QEvent*) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#27 0x00007ffff6e37dbb in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#28 0x00007ffff7595752 in QGuiApplicationPrivate::processMouseEvent(QWindowS ystemInterfacePrivate::MouseEvent*) () from /usr/lib/libQt5Gui.so.5
No symbol table info available.
#29 0x00007ffff7597305 in QGuiApplicationPrivate::processWindowSystemEvent(Q WindowSystemInterfacePrivate::WindowSystemEvent*) () from /usr/lib/libQt5Gui.so.5
No symbol table info available.
#30 0x00007ffff757c1ef in QWindowSystemInterface::sendWindowSystemEvents(QFl ags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Gui.so.5
No symbol table info available.
#31 0x00007fffed5671b0 in ?? () from /usr/lib/qt/plugins/platforms/libqxcb.so
No symbol table info available.
#32 0x00007ffff3e5c9fd in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#33 0x00007ffff3e5cce0 in ?? () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#34 0x00007ffff3e5cd8c in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
No symbol table info available.
#35 0x00007ffff6e901b7 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#36 0x00007ffff6e35852 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#37 0x00007ffff6e3d22c in QCoreApplication::exec() () from /usr/lib/libQt5Core.so.5
No symbol table info available.
#38 0x0000000000403fec in main (argc=1, argv=0x7fffffffe6e8) at ../Renamer4Qml/main.cpp:19
app = <incomplete type>
engine = <incomplete type>


Start your app in debugging mode. When it crashes, the backtrace will be presented in the bottom part of the window. You can access "Copy Contents to Clipboard" option from the context menu to copy the result to clipboard so that you can post it here. Other than that you can google gdb+backtrace. At the bottom I have the 6 buttons
- Issues
- Search Results
- Application Output
- Compile Output
- QML/JS Console
- General Messages
None of them contains any useful information. When the crash happens the it just stops and I get a disassembly output window.

wysota
25th April 2015, 23:23
I am not entirely sure, but I think I found it :)
Right click on the left debugger pane -> "create full backtrace"
It looks like the crash takes place while trying to wrap your QObject instance. This might happen if the pointer points to an invalid object. Did you initialize contents of mData with allocated objects?



At the bottom I have the 6 buttons
- Issues
- Search Results
- Application Output
- Compile Output
- QML/JS Console
- General Messages
None of them contains any useful information. When the crash happens the it just stops and I get a disassembly output window.
Too much to the bottom.

motaito
25th April 2015, 23:47
I doubt that the contents of m_Entries (in my first snippet I called it m_Data) are faulty. Because if they were, it should always fail. Not just sometimes. I have a tableView in a qml which contains a MouseArea in a column. If I press on the MouseArea I would like to perform some tasks. Normally that works but for a entry where it worked before, If I click a bunch of times on the MouseArea suddenly I get a segfault. I have reduced it to only getting the element and not actually doing something with it. There, I get the segfault, if I click several times on the MouseArea.

My data class looks like this:

// header:
#ifndef FILEENTRY_H
#define FILEENTRY_H

#include <QObject>

class FileEntry : public QObject
{
Q_OBJECT
Q_PROPERTY(QString fileName READ getFileName WRITE setFileName NOTIFY fileNameChanged)
Q_PROPERTY(QString filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)

public:
FileEntry(QObject* parent=0);

void setFileName(const QString& fileName);
void setFilePath(const QString& filePath);
QString getFileName() const;
QString getFilePath() const;

signals:
void fileNameChanged(const QString& fileName);
void filePathChanged(const QString& filePath);

private:
QString m_FileName;
QString m_FilePath;
};

#endif // FILEENTRY_H


// cpp file:
#include "fileentry.h"

FileEntry::FileEntry(QObject* parent)
: QObject(parent)
{
m_FileName = "";
m_FilePath = "";
}

void FileEntry::setFileName(const QString& fileName)
{
m_FileName = fileName;
}

void FileEntry::setFilePath(const QString& filePath)
{
m_FilePath = filePath;
}

QString FileEntry::getFileName() const
{
return m_FileName;
}

QString FileEntry::getFilePath() const
{
return m_FilePath;
}
In my file model I fill the entries like this:

bool FileModel::getFiles(const QString& folderPath, const bool append)
{
QDir dir(folderPath);
if(!dir.exists())
return false;

if(!append)
{
for(int e=0; e<m_Entries.count(); ++e)
delete m_Entries[e];

m_Entries.clear();
}

QFileInfoList fileInfoList = dir.entryInfoList();
for(int i=0; i < fileInfoList.count(); ++i)
{
if(fileInfoList[i].isFile())
{
FileEntry* entry = new FileEntry();
entry->setFileName(fileInfoList[i].fileName());
entry->setFilePath(fileInfoList[i].absoluteFilePath());
addEntry(entry);
}
}

return true;
}
// getting the data
Q_FileEntry* FileModel::getElement(int index)
{
if(0 > index || index >= m_Entries.size())
return 0;

return m_Entries[index];
}

wysota
25th April 2015, 23:52
Can we see code executed when you click on MouseArea?

motaito
26th April 2015, 00:09
These are the relevant parts. The area you asked for is towards the bottom.

// In main.cpp
qmlRegisterType<FileEntry>("MoeFileEntry", 1, 0, "FileEntryHandler");
qmlRegisterType<FileModel>("MoeFileModel", 1, 0, "FileModelHandler");


// in qml file
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import MoeFileEntry 1.0
import MoeFileModel 1.0


Rectangle {
id: wrapper
anchors.fill: parent

FileModelHandler {
id: fileModelHandler
}

Component.onCompleted: {
loadFolder("SampleFiles", false);
}


function loadFolder(directory, append)
{
var path = mediaManagerHandler.getDirectoryPathAbsolute(direc tory);
fileModelHandler.getFiles(path, append);
}


TableView {
property int sizeOpen: 60
property int sizeClosed: 20
id: moeTableView
anchors.fill: parent
model: fileModelHandler

rowDelegate: Rectangle {

id: rowDelegate
color: styleData.alternate ? "#666666" : "#555555"
height: getSize() // styleData.selected? sizeOpen : sizeClosed

function getSize()
{
if(!moeTableView.selection.contains(styleData.row) )
{
doClose.start();
return moeTableView.sizeClosed;
}

return moeTableView.sizeOpen;
}

MouseArea {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: moeTableView.sizeClosed
propagateComposedEvents: true
preventStealing: true
acceptedButtons: Qt.LeftButton | Qt.RightButton

onClicked: {
if(moeTableView.sizeOpen == rowDelegate.height)
{
moeTableView.selection.deselect(styleData.row);
doClose.start()
}
else
{
moeTableView.selection.clear();
moeTableView.selection.select(styleData.row);
doOpen.start();
}
}
}

ParallelAnimation {
id: doOpen
running: false
NumberAnimation { target: rowDelegate; easing.type: Easing.OutSine; property: "height"; to: moeTableView.sizeOpen; duration: 100 }
}
ParallelAnimation {
id: doClose
running: false
NumberAnimation { target: rowDelegate; easing.type: Easing.OutSine; property: "height"; to: moeTableView.sizeClosed; duration: 100; }
}
}

TableViewColumn {
id: icon
width: 50
delegate: Item{
anchors.fill: parent
clip: !styleData.selected
Rectangle {
clip:false
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: moeTableView.width
color: "#222222"
}
Rectangle {
width:20
height:20
color:"red"
anchors.top: parent.top
anchors.left: parent.left

MouseArea {
anchors.fill:parent
onClicked: {
// Here is the problem area where the segfault occurs!
// I am not useing "e" , I can also not assign it and
// just call the function, the segfault still happens.
var e = fileModelHandler.getElement(styleData.row);
}
}
}
}
}

TableViewColumn {
id: rowFileName
role: "fileName"
title: "Name"
width: 100
delegate: Item {
id: itemFileName
clip:true
anchors.fill: parent
Text{
id:txtFileName
anchors.fill: parent
text: styleData.value
elide: Text.ElideRight
}
}
}

TableViewColumn {
id: rowFilePath
role: "filePath"
title: "Path"
width: 200
delegate: Item {
id: itemFilePath
anchors.fill: parent
clip: true
Text{
id:txtFilePath
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: styleData.value
elide: Text.ElideRight
}
}
}

}
}

wysota
26th April 2015, 08:29
I don't see anything suspicious here. I would still opt for an invalid pointer in the list. You need to use the debugger to check it out.

anda_skoa
26th April 2015, 11:37
You need to set the ownership to CppOwnership of the object before returning it from the Q_INVOKABLE method.
If you do not, then Qt assumes JavaScriptOwnerShip and garbage collects the object, at which point the pointer in your list points to an invalid address.

Cheers,
_

motaito
26th April 2015, 14:42
I don't see anything suspicious here. I would still opt for an invalid pointer in the list. You need to use the debugger to check it out. I am not sure how to do that because it only happens after clicking several times in a succession. If I make a break point, I don't really get to the needed amount of clicks. I guess I could add some sort of assert check in the getElement(index) function. I'll research how to check for a valid pointer.


You need to set the ownership to CppOwnership of the object before returning it from the Q_INVOKABLE method.
If you do not, then Qt assumes JavaScriptOwnerShip and garbage collects the object, at which point the pointer in your list points to an invalid address.That sounds very reasonable. I have not thought of that at all. It would explain why the crash happens suddenly and not right away. It would take some time for the garbage collection to kick in. I am however not sure how to set the ownership because it needs to be set per object. In my case I create the objects in my FileModel class. An instance of the FileModel class is created within a qml file. So I can't figure out how to get a hold of the proper context from the QQmlApplicationEngine. How would I set the object ownership?

Added after 56 minutes:

Never mind I found I can use

FileEntry* entry = new FileEntry();
QQmlEngine::setObjectOwnership(entry, QQmlEngine::CppOwnership);
without having to have a handle on the QQmlApplicationEngine. I still have a hard time to make heads and tails of the documentation :)

The problem has not happened since I use it this way. So, it looks like you guys were both right. The problem was caused by an invalid pointer. It got invalidated by the javascript engine destroying the object through the garbage collection. Once again you guys saved my bacon :)

Thanks a lot for the help and the insight in what is happening!