PDA

View Full Version : QStateMachine does not start up in Windows/Linux RELEASE build, DEBUG build is fine



bob2oneil
3rd October 2011, 20:59
I am uisng a QStateMachine in a multithreaded console based application that executes
under Windows and Linux, both 32 and 64-bit. The DEBUG build works fine, while in the
RELEASE build, the state machine fails to execute properly. This is true for both the
windows and Linux platforms. I have a file trace capability, and I am not able to enter into
the root state of the state machine at all. The secondary threads in the application all seem to
work, and there is no apparent problem viewable in terms of inappropriate linked libraries or
console output suggesting any build or runtime issues or links.

I have verified the dependencies to be correct. The necessary Qt libraries (console and library)
as on the path. All seems well with the application other than the state machine never starts
up in the RELEASE mode. This behavior is true under Windows XP, 7 and Linux Fedora 64-bit, so there
must be something I am doing wrong.

I am interested in any suggestions folks might have to try and determine what the issue might be.

Attached is the source code for the QStateMachine related lines of code.


My application is derived from QCoreApplication, and the c'tor is as follows



App::App(int &argc, char **argv, int ver) :
QCoreApplication(argc, argv),
m_appName(""),
m_appPath(""),
m_stateNumber(unknown),
m_roleID(""),
m_ftpTimeoutMs(5000),
m_tmaMode(eTmaModeStandalone),
m_pXmlParserSettings(0),
m_pSettings(0),
m_bit(false),
m_configured(false),
m_running(false),
...
{
TRACE();

Q_UNUSED(ver);

// Create state machine states
m_machine = new QStateMachine(this);
m_stateRoot = new QState();
m_stateInitialize = new QState(m_stateRoot);
m_stateConfiguring = new QState(m_stateRoot);
m_stateUnconfigured = new QState(m_stateRoot);
m_stateRunning = new QState(m_stateRoot);
m_stateIdle = new QState(m_stateRoot);
m_stateInitiatedBIT = new QState(m_stateRoot);
m_stateDone = new QFinalState();
}


Prior to calling a.exec(), I perform the following to prepare and execute the state machine



bool App::aboutToExec(xmlParserSettings *pXmlParserSettings, Settings* pSettings)
{
TRACE();

FILE_LOG(logINFO) << "App: created: thread id = " << QThread::currentThreadId();

// Save off pointers to local XML file parser and content
m_pXmlParserSettings = pXmlParserSettings;
m_pSettings = pSettings;

// Check for multiple instances of lmd
QStringList listOfPids;
int countOfProcesses = System::getProcessIdsByProcessName(szLmdProgramNam e, listOfPids);
if (countOfProcesses > 1)
{
FILE_LOG(logERROR) << "Only one instance of the Link Manager can run at the same time";
return false;
}

// Process contents of the local configuration XML file
processLocalSettings(pSettings);

// Process command line arguments (will override local XML file settings)
if (!parseArgs())
{
return false;
}

// Allow user to ask for help without being root, but must be root hereafter
#ifdef Q_OS_LINUX
if (geteuid() != 0)
{
// NOTE: SNMP Subagent connection and PTP operation when controlled requires root privilege
FILE_LOG(logERROR) << "Link Manager requires ROOT permisssions to execute";
printf("Link Manager requires ROOT permisssions to execute\n");
return false;
}
#endif

// Perform lazy initialization of singletons prior to thread execution (to ensure thread safe creation)
pSnmpInterface->onReset(restoreDefaults);
pMdlInterface->onReset(restoreDefaults);
pPtpdInterface->onReset(restoreDefaults);
pRfnmProcessor->onReset(restoreDefaults);

// Create state machine content
FILE_LOG(logINFO) << "INIT: Creating state machine...";

// Hook up state entry and exit routines (TODO: remove some of these if eventually not used)
Q_ASSERT(QObject::connect(m_stateRoot, SIGNAL(entered()), this, SLOT(onStateRootEntered())));
Q_ASSERT(QObject::connect(m_stateRoot, SIGNAL(exited()), this, SLOT(onStateRootExited())));
Q_ASSERT(QObject::connect(m_stateInitialize, SIGNAL(entered()), this, SLOT(onStateInitializeEntered())));
Q_ASSERT(QObject::connect(m_stateInitialize, SIGNAL(exited()), this, SLOT(onStateInitializeExited())));
Q_ASSERT(QObject::connect(m_stateConfiguring, SIGNAL(entered()), this, SLOT(onStateConfiguringEntered())));
Q_ASSERT(QObject::connect(m_stateConfiguring, SIGNAL(exited()), this, SLOT(onStateConfiguringExited())));
Q_ASSERT(QObject::connect(m_stateUnconfigured, SIGNAL(entered()), this, SLOT(onStateUnconfiguredEntered())));
Q_ASSERT(QObject::connect(m_stateUnconfigured, SIGNAL(exited()), this, SLOT(onStateUnconfiguredExited())));
Q_ASSERT(QObject::connect(m_stateRunning, SIGNAL(entered()), this, SLOT(onStateRunningEntered())));
Q_ASSERT(QObject::connect(m_stateRunning, SIGNAL(exited()), this, SLOT(onStateRunningExited())));
Q_ASSERT(QObject::connect(m_stateIdle, SIGNAL(entered()), this, SLOT(onStateIdleEntered())));
Q_ASSERT(QObject::connect(m_stateIdle, SIGNAL(exited()), this, SLOT(onStateIdleExited())));
Q_ASSERT(QObject::connect(m_stateInitiatedBIT, SIGNAL(entered()), this, SLOT(onStateInitiatedBITEntered())));
Q_ASSERT(QObject::connect(m_stateInitiatedBIT, SIGNAL(exited()), this, SLOT(onStateInitiatedBITExited())));
Q_ASSERT(QObject::connect(m_stateDone, SIGNAL(entered()), this, SLOT(onStateDoneEntered())));
Q_ASSERT(QObject::connect(m_stateDone, SIGNAL(exited()), this, SLOT(onStateDoneExited())));

// Set initial state of root state with common exit for all children states
m_stateRoot->setInitialState(m_stateInitialize);

// Make done state the transition target for application exit from any state
Q_ASSERT(m_stateRoot->addTransition(this, SIGNAL(aboutToQuit()), m_stateDone) != 0);

// Add states to machine
m_machine->addState(m_stateRoot);
m_machine->addState(m_stateDone);

// Add state transitions:

// Transition from stateInitialize to stateUnconfigured after initialization. This ensures SNMP
// thread is active to send out traps
//Q_ASSERT(m_stateInitialize->addTransition(m_stateInitialize, SIGNAL(entered()), m_stateUnconfigured) != 0);
Q_ASSERT(m_stateInitialize->addTransition(this, SIGNAL(initializationDone()), m_stateUnconfigured) != 0);

// SNMP configured() signal responsive states
Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);
Q_ASSERT(m_stateUnconfigured->addTransition(this, SIGNAL(configurationSucceeded()), m_stateIdle) != 0); // MDL file load at boot
Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);
Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);

// Configuration success or failure internal signals from Configuring state
Q_ASSERT(m_stateConfiguring->addTransition(this, SIGNAL(configurationSucceeded()), m_stateIdle) != 0);
Q_ASSERT(m_stateConfiguring->addTransition(this, SIGNAL(configurationFailed()), m_stateUnconfigured) != 0);

// SNMP bitInitiated() signal responsive states
// NOTE: call setBitReturnState in each state entry to specify return state from BIT
Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
Q_ASSERT(m_stateConfiguring->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);

// Transition to and from run state
Q_ASSERT(m_stateIdle->addTransition(pRfnmProcessor, SIGNAL(run()), m_stateRunning) != 0);
Q_ASSERT(m_stateRunning->addTransition(pRfnmProcessor, SIGNAL(stop()), m_stateIdle) != 0);

// SNMP reset() responsive states
Q_ASSERT(m_stateConfiguring->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
Q_ASSERT(m_stateInitiatedBIT->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);

// SNMP set epoch
Q_ASSERT(QObject::connect(pSnmpInterface, SIGNAL(setEpochMicroseconds(unsigned int)), this, SLOT(setEpochMicroseconds(unsigned int))));

// Connect state machine exit to application exit
Q_ASSERT(QObject::connect(m_machine, SIGNAL(finished()), this, SLOT(quit())));

// Start up state machine
m_machine->setInitialState(m_stateRoot);
m_machine->start();

// Create processes and secondary thread objects (TODO: add others here)

.
.
.
// Create secondary threads (TODO: add others here)
FILE_LOG(logINFO) << "INIT: Creating secondary threads...";
m_snmpSubAgentThread = new SnmpSubAgentThread(0);
m_tdmaThread = new TdmaThread(0);

// Start up secondary threads (TODO: add others here)
FILE_LOG(logINFO) << "INIT: Starting secondary threads...";
m_snmpSubAgentThread->start(QThread::NormalPriority);

return true;
}


Interested in ways to debug the RELEASE build.

totem
3rd October 2011, 22:30
Q_ASSERT() body is not compiled in release mode (if i'm right) so the connects will not execute, as well as any statement inside it.

bob2oneil
3rd October 2011, 23:04
Thanks, that was it, I was looking in the wrong place, I was thinking that Q_ASSERT acted more like a verify setting that was applicable to both DEBUG and RELEASE.
I could not see the forest for the trees, was infected with brain dumbness.