Debug with QSignalSpy a timer object causing crashes
-
Hi.
I have an my object that uses a timer and generates events, occasionally and randomly, the program closes on the event manager connected to this object (it seems to me that an exception is generated because the QT consider this event as invalid/unhandled).
Can anyone give me an example how to use the QSignalSpy object to track/debug this case?
-
Hi.
I have an my object that uses a timer and generates events, occasionally and randomly, the program closes on the event manager connected to this object (it seems to me that an exception is generated because the QT consider this event as invalid/unhandled).
Can anyone give me an example how to use the QSignalSpy object to track/debug this case?
-
@giusdbg
I don't know about QSignalSpy. If you genuinely have a "crash"/"exception" then run under debugger, allow to crash, look at stack trace. Not having a slot attached to, say, aQTimer
signal does not crash and is not "invalid/unhandled".@JonB The stack trace is related to the QT event loop, there is no reference to a file of my program (except in main.cpp for a.exec() ).
Due to my inexperience and superficiality I didn't save the stack trace because I considered it useless, next time I'll do it.
My guesses are destroyed objects, multiple events, something the my program isn't doing right.
-
@JonB The stack trace is related to the QT event loop, there is no reference to a file of my program (except in main.cpp for a.exec() ).
Due to my inexperience and superficiality I didn't save the stack trace because I considered it useless, next time I'll do it.
My guesses are destroyed objects, multiple events, something the my program isn't doing right.
-
Hi @giusdbg,
Can anyone give me an example how to use the QSignalSpy object to track/debug this case?
QSignalSpy is part of Qt Test - ie its for test code (such as unit tests) to verify which signals were emitted when testing your code. So not really what you want here.
Along with @JonB's suggestion re using a debugger (this should always be your first step), I would also strongly recommend running your app with a memory checker like Valgrind.
Cheers.
-
@giusdbg
Still worth looking at/posting the stack trace for clues.
Tracking down "destroyed objects, multiple events, something the my program isn't doing right" is still going to be tricky with QSignalSpy. -
@giusdbg
The stack trace shows the crash in theQObjectPrivate
. Hard to say for sure without seeing your code. However, that part gets deleted first before the actual object is getting deleted. So most likely the crash is a UAF. -
@giusdbg
The stack trace shows the crash in theQObjectPrivate
. Hard to say for sure without seeing your code. However, that part gets deleted first before the actual object is getting deleted. So most likely the crash is a UAF.@Axel-Spoerl I've tried to analyze the stack trace, but I couldn't figure out which objects or events in my program were involved.
I just have the unfortunate feeling that the stack trace is not related to the problem itself, but only to the application shutdown caused by the problem.
P.S. UAF?
-
@Axel-Spoerl I've tried to analyze the stack trace, but I couldn't figure out which objects or events in my program were involved.
I just have the unfortunate feeling that the stack trace is not related to the problem itself, but only to the application shutdown caused by the problem.
P.S. UAF?
-
@giusdbg
Your stack trace showsqprocess.cpp:1089
. That is probably inQProcessPrivate::_q_processDied()
, somewhere around https://code.woboq.org/kde/qt4/src/corelib/io/qprocess.cpp.html#1089. Does your code spawn other OS processes viaQProcess
?@JonB No, or rather I think all kde applications do it by default, in fact there is a --nofork option.
I believe it does here
int main(int argc, char *argv[]) { ................ if(!KUniqueApplication::start()) return 0; KUniqueApplication a; ................ a.setQuitOnLastWindowClosed(false); return a.exec(); }
And it occurs to me that maybe I should try running the program with --nofork for a few days, and see what happens.
I'm talking nonsense, if it weren't for the --nofork when debugging, I wouldn't see anything in the debugger.
-
@JonB No, or rather I think all kde applications do it by default, in fact there is a --nofork option.
I believe it does here
int main(int argc, char *argv[]) { ................ if(!KUniqueApplication::start()) return 0; KUniqueApplication a; ................ a.setQuitOnLastWindowClosed(false); return a.exec(); }
And it occurs to me that maybe I should try running the program with --nofork for a few days, and see what happens.
I'm talking nonsense, if it weren't for the --nofork when debugging, I wouldn't see anything in the debugger.
@giusdbg
UAF = use after free, accessing a deleted object. That's what most likely happens.
The code shown above doesn't have to do anything with aQSignalSpy
or a timer of any nature.
If there is reason to assume those (or one of them) cause(s) the crash, please show the code! -
@giusdbg
UAF = use after free, accessing a deleted object. That's what most likely happens.
The code shown above doesn't have to do anything with aQSignalSpy
or a timer of any nature.
If there is reason to assume those (or one of them) cause(s) the crash, please show the code!@Axel-Spoerl It's not possible, it's a complex program
-
@Axel-Spoerl It's not possible, it's a complex program
@Axel-Spoerl @JonB Maybe I have an answer (at least partial) to your questions.
There is this part (which is called by the timer and generates events) which launches external processes to get values from the sensors.
This uses the KProcess object which is derived from QProcess, and in my opinion is involved in the crash (maybe not directly).
I can't show you the full code that calls this part without posting a lot of code.
#include <QtDebug> #include <string.h> #include "processexec.h" ProcessExec::ProcessExec(): KProcess() { clearData(); connect( this, SIGNAL(readyReadStandardOutput()),this, SLOT(slotReceivedStdout()) ); connect( this, SIGNAL(readyReadStandardError()),this, SLOT(slotReceivedStderr()) ); } ProcessExec::~ProcessExec(){ } bool ProcessExec::run() { qDebug() << "\033[91m" << "qDebug(): ProcessExec::run()" << "\033[0m"; clearData(); start(); return true; } bool ProcessExec::runAndWait() { qDebug() << "\033[91m" << "qDebug(): ProcessExec::runAndWait()" << "\033[0m"; clearData(); setOutputChannelMode(KProcess::SeparateChannels); execute(); return true; } void ProcessExec::slotReceivedStdout() { // setReadChannel(KProcess::StandardOutput); buffer.append ( readAllStandardOutput() ); qDebug() << "\033[91m" << "qDebug(): ProcessExec::slotReceivedStdout(): buffer " << buffer << "\033[0m"; } void ProcessExec::slotReceivedStderr() { fErrors= true; // setReadChannel(KProcess::StandardError); buffer.append ( readAllStandardError() ); qDebug() << "\033[91m" << "qDebug(): ProcessExec::slotReceivedStderr(): buffer " << buffer << "\033[0m"; } ........................................................ void HDSensorsList::updateSensors() { if(process) return; const QObjectList *list= &children(); if(!list) return; QStringList params; for ( QObjectList::ConstIterator it = list->begin(); it != list->end(); ++it ) { Sensor *obj = (Sensor *)(*it); if(obj->monitorized()) params << obj->objectName(); } if(params.count()>0) { process= new ProcessExec; *process << "hddtemp" << "-q" << params; connect( process, SIGNAL(finished(int)), this, SLOT(slotProcessExited(int)) ); process->run(); } } void HDSensorsList::slotProcessExited(int exitCode) { const QObjectList *list= &children(); if (!list) return; if (process->outputErrors()) qWarning("HddTemp Error:\n%s", process->getStdoutData().toAscii().data()); QStringList buf = process->getStdoutData().split(QChar('\n')); for(QStringList::Iterator it = buf.begin(); it != buf.end(); ++it ) { for ( QObjectList::ConstIterator olIt = list->begin(); olIt != list->end(); ++olIt ) { Sensor *obj = (Sensor *)(*olIt); QRegExp rx(QString(obj->objectName()) + QString(":\\s+.+:\\s+(\\d+).*C")); if (rx.indexIn((*it)) > -1) obj->setValue(rx.cap(1).toDouble(), Sensor::dgCelsius); } } delete process; process= 0; } ........................................................
-
@Axel-Spoerl @JonB Maybe I have an answer (at least partial) to your questions.
There is this part (which is called by the timer and generates events) which launches external processes to get values from the sensors.
This uses the KProcess object which is derived from QProcess, and in my opinion is involved in the crash (maybe not directly).
I can't show you the full code that calls this part without posting a lot of code.
#include <QtDebug> #include <string.h> #include "processexec.h" ProcessExec::ProcessExec(): KProcess() { clearData(); connect( this, SIGNAL(readyReadStandardOutput()),this, SLOT(slotReceivedStdout()) ); connect( this, SIGNAL(readyReadStandardError()),this, SLOT(slotReceivedStderr()) ); } ProcessExec::~ProcessExec(){ } bool ProcessExec::run() { qDebug() << "\033[91m" << "qDebug(): ProcessExec::run()" << "\033[0m"; clearData(); start(); return true; } bool ProcessExec::runAndWait() { qDebug() << "\033[91m" << "qDebug(): ProcessExec::runAndWait()" << "\033[0m"; clearData(); setOutputChannelMode(KProcess::SeparateChannels); execute(); return true; } void ProcessExec::slotReceivedStdout() { // setReadChannel(KProcess::StandardOutput); buffer.append ( readAllStandardOutput() ); qDebug() << "\033[91m" << "qDebug(): ProcessExec::slotReceivedStdout(): buffer " << buffer << "\033[0m"; } void ProcessExec::slotReceivedStderr() { fErrors= true; // setReadChannel(KProcess::StandardError); buffer.append ( readAllStandardError() ); qDebug() << "\033[91m" << "qDebug(): ProcessExec::slotReceivedStderr(): buffer " << buffer << "\033[0m"; } ........................................................ void HDSensorsList::updateSensors() { if(process) return; const QObjectList *list= &children(); if(!list) return; QStringList params; for ( QObjectList::ConstIterator it = list->begin(); it != list->end(); ++it ) { Sensor *obj = (Sensor *)(*it); if(obj->monitorized()) params << obj->objectName(); } if(params.count()>0) { process= new ProcessExec; *process << "hddtemp" << "-q" << params; connect( process, SIGNAL(finished(int)), this, SLOT(slotProcessExited(int)) ); process->run(); } } void HDSensorsList::slotProcessExited(int exitCode) { const QObjectList *list= &children(); if (!list) return; if (process->outputErrors()) qWarning("HddTemp Error:\n%s", process->getStdoutData().toAscii().data()); QStringList buf = process->getStdoutData().split(QChar('\n')); for(QStringList::Iterator it = buf.begin(); it != buf.end(); ++it ) { for ( QObjectList::ConstIterator olIt = list->begin(); olIt != list->end(); ++olIt ) { Sensor *obj = (Sensor *)(*olIt); QRegExp rx(QString(obj->objectName()) + QString(":\\s+.+:\\s+(\\d+).*C")); if (rx.indexIn((*it)) > -1) obj->setValue(rx.cap(1).toDouble(), Sensor::dgCelsius); } } delete process; process= 0; } ........................................................
@giusdbg The sequence of the case is
- Timer calls HDSensorsList::updateSensors()
- updateSensors() creates a new ProcessExec object (derived from K-QProcess) and asynchronously start an external program.
- External process terminates, ProcessExec raises finished event which is connected to HDSensorsList::slotProcessExited.
- slotProcessExited does some graphical updates, then deletes the ProcessExec object.
- When exiting from slotProcessExited function, occasionally and randomly, a crash occurs.
It looks like a race condition that causes a deleted object to be used.
-
@giusdbg The sequence of the case is
- Timer calls HDSensorsList::updateSensors()
- updateSensors() creates a new ProcessExec object (derived from K-QProcess) and asynchronously start an external program.
- External process terminates, ProcessExec raises finished event which is connected to HDSensorsList::slotProcessExited.
- slotProcessExited does some graphical updates, then deletes the ProcessExec object.
- When exiting from slotProcessExited function, occasionally and randomly, a crash occurs.
It looks like a race condition that causes a deleted object to be used.
@giusdbg said in Debug with QSignalSpy a timer object causing crashes:
- External process terminates, ProcessExec raises finished event which is connected to HDSensorsList::slotProcessExited.
- slotProcessExited does some graphical updates, then deletes the ProcessExec object.
I would not
delete process
when I am inside a slot attached to a signal received from that process object. Doesprocess->deleteLater()
improve behaviour? -
@giusdbg said in Debug with QSignalSpy a timer object causing crashes:
- External process terminates, ProcessExec raises finished event which is connected to HDSensorsList::slotProcessExited.
- slotProcessExited does some graphical updates, then deletes the ProcessExec object.
I would not
delete process
when I am inside a slot attached to a signal received from that process object. Doesprocess->deleteLater()
improve behaviour?@JonB Thanks, probably yes, I'll try deleteLater() .
After this modification it is necessary to test for several days, sometimes the crash occurs after ten seconds, sometimes after many hours.P.S. I would have liked to find out what event was involved in the crash stack trace, to have some event debugging capability.
(That's why I thought of QSignalSpy.) -
@giusdbg said in Debug with QSignalSpy a timer object causing crashes:
- External process terminates, ProcessExec raises finished event which is connected to HDSensorsList::slotProcessExited.
- slotProcessExited does some graphical updates, then deletes the ProcessExec object.
I would not
delete process
when I am inside a slot attached to a signal received from that process object. Doesprocess->deleteLater()
improve behaviour? -