I have a library written in C++. The library has a function which accepts commands as a string and executes them. If an error is encountered (either in the command or while running the command) an "error function" is called which does some cleanup and finally calls exit(1). I am now trying to implement a graphical user interface (using Qt) to the library. The problem is that when an error is encountered, exit is called and my application crashes. I have access to the library source code but I would like to keep modifying the source code to minimum.
I am thinking of rewriting the error function such that it just stops executing code and stays in an idle state until another command is passed to the library from the user-interface. The problem is I am not sure how to go about doing it. I am basically looking for a function call equivalent to exit system call (so that the error function never returns to the code which generated the error) except that I do not want the application to exit but instead just go to an idle state and wait for calls from the user interface.
If there is another way to implement this please let me know. Please also let me know if you need more details.
Thanks in advance,
Here is some code which shows what my problem is
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
void error_func(string error);
void create_sphere(float radius);
void create_rect(float length, float width);
int main()
{
string command;
while(1) {
cout << "Enter command: ";
cin >> command;
if(command.compare("create_sphere") == 0) {
float radius;
cout << "Enter radius: ";
cin >> radius;
create_sphere(radius);
}
else if(command.compare("create_rect") == 0) {
float l, w;
cout << "Enter length and width: ";
cin >> l >> w;
create_rect(l, w);
}
else if(command.compare("quit") == 0)
break;
}
}
void create_sphere(float radius)
{
if(radius < 0)
error_func(string("Radius must be positive"));
cout << "Created sphere" << endl;
}
void create_rect(float length, float width)
{
if(length < 0)
error_func(string("Length must be positive"));
if(width < 0)
error_func(string("Width must be positive"));
cout << "Created rectangle" << endl;
}
void error_func(string error)
{
// do some cleanup
cout << "ERROR: " << error << endl;
exit(1);
}
Assume that create_sphere, create_rect and error_func are provided by the library. I can modify error_func as required but not the other functions (since there are many such functions).
Now when an error is encountered, I would like to go back to the while loop in main so that I can keep accepting other commands.
I am basically looking for a function call equivalent to exit system call (so that the error function never returns to the code which generated the error) except that I do not want the application to exit but instead just go to an idle state and wait for calls from the user interface.
Basically, you are looking for an event loop. The typical minimal Qt program is as follows:
#include <QApplication>
#include <QMainWindow>
int main(int argc, char **argv)
{
QApplication(argc, argv);
QMainWindow w;
w.show();
return application.exec(); // What you want instead of exit
}
Now, you could replace QMainWindow with your own class, and declare a slot in that which gets called when you are trying to handle a command from the user interface.
#include <QWidget>
...
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent) : QWidget(parent)
{
connect(sender, SIGNAL(mySignal()), SLOT(handleCommand()));
}
public slots:
void handleCommand()
{
// Handle your command here.
// Print the error code.
qDebug() << error_func(string("Radius must be positive"));
// or simply:
qDebug() << "Radius must be positive";
} // Leaving the scope, and getting back to the event loop
}
As for the bogus library, well, if it exits, it does. There is not much you can do about that without fixint the library. It is a very bad behavior from most of the libraries.
The modification would be not to exit, but return an error code - which is a general practice in Qt software - and leave it with the application when to exit if they wish.
The application would not quit in your case. Again, It is a very bad idea for a library function to exit. Even Qt does not do except 1-2 times in a few million LOC.
I would suggest not to throw an exception. It is generally not common in Qt software, and you could make your software consistent by just using error codes like the rest of Qt does for you.
Invent an error state (idle state) and make the function never fail. The error state should become visible and be resolvable by some means.
If you can not reach a resolvable error state, it might be possible to rollback to some prior (initial) state.
If the options above are not possible you have some serious failure (software, hardware, data) and you might terminate the program.
All above can be achieved with return values or a getter function (indicating the current state) and a setter manipulating the current state - an exit call is a poor solution in a library. If you have an unresolvable state or can not rollback to a prior state you might throw an exception, catch it in the user interface and terminate the program after displaying the issue.
You should install a message handler which will automatically reduce a lot of your work.
Additionally it will help in reducing your debugging too. Here is my message handler for my Qt5 application. It will need a little tweaking if you are using Qt4:
QFile *logFile = NULL;//The file in which you will output the debug info to
QTextStream *logStream = NULL;//text stream for your log file
QMutex *mutex = NULL;//Always use mutex if you are multi threading your application
bool *debugMode = NULL;//it just a flag in case you want to turn off debugging
bool errorMsg = false;//use the value of this variable after QApplication::exec() if you need to show an error message
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if(((logFile != NULL) && (debugMode != NULL)))
{
mutex->lock();
switch (type)
{
case QtDebugMsg:
if(!*debugMode)
{
mutex->unlock();
return;
}
*logStream << msg;
logStream->flush();
break;
case QtWarningMsg:
if(!((QString)context.function).contains("setGeometry"))
{
*logStream << "\n*** Warning ***\n";
*logStream << msg << endl;
*logStream << "Category: " << context.category << endl;
*logStream << "File: " << context.file << endl;
*logStream << "Function: " << context.function << endl;
*logStream << "Line: " << context.line << endl;
*logStream << "Version: " << context.version;
*logStream << "\n*** Warning Complete ***\n";
logStream->flush();
errorMsg = true;
SessionManager::get_obj()->saveCurrentSession();
}
break;
case QtCriticalMsg:
*logStream << "\n*** Critical ***\n";
*logStream << msg << endl;
*logStream << "Category: " << context.category << endl;
*logStream << "File: " << context.file << endl;
*logStream << "Function: " << context.function << endl;
*logStream << "Line: " << context.line << endl;
*logStream << "Version: " << context.version;
*logStream << "\n*** Critical Complete ***\n";
logStream->flush();
errorMsg = true;
SessionManager::get_obj()->saveCurrentSession();
break;
case QtFatalMsg:
*logStream << "\n*** Fatal ***\n";
*logStream << msg << endl;
*logStream << "Category: " << context.category << endl;
*logStream << "File: " << context.file << endl;
*logStream << "Function: " << context.function << endl;
*logStream << "Line: " << context.line << endl;
*logStream << "Version: " << context.version;
*logStream << "\n*** Fatal Complete ***\n";
logStream->flush();
errorMsg = false;
SessionManager::get_obj()->saveCurrentSession();
ShowErrorMsg(SessionManager::getSessionName());
exit(0);
}
mutex->unlock();
}
}
To install a message handler add the following code in the main() of your GUI.
qInstallMessageHandler(myMessageOutput);
You can ignore the check for setGeometry if you want to but I find that this warning is emitted unnecessarily. So you can keep it.
Also you may want to have a Session Manager which will automatically save the current session whenever an error is encountered.
When you have done this, you can safely call qFatal() when you want to terminate your application, or else use qCritical() if you want some other functionality.
Related
While using a class to hold my window class and Vulkan class, this error VK_ERROR_FEATURE_NOT_PRESENT is returned when I use vkCreateDevice however, when I put the same code the class is running into the main class, it works completely fine. I also had a similar problem with getting the instance extensions via SDL_Vulkan_GetInstanceExtensions.
working main.cpp
window.createWindow();
engine.window = window.window;
try {
engine.initialize();
}
catch (XiError error) {
std::cout << "Error " << error.code << ": " << error.definition << std::endl;
}
window.instance = engine.getVkInstance();
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(engine.physicalDevice, &deviceProperties);
std::cout << deviceProperties.deviceName << ", Driver Version " << deviceProperties.driverVersion << std::endl;
try {
window.createSurface();
}
catch (XiError error) {
std::cout << "Error " << error.code << ": " << error.definition << std::endl;
}
window.mainLoop();
vkDestroyDevice(engine.logicalDevice, nullptr);
vkDestroySurfaceKHR(engine.instance, window.surface, nullptr);
vkDestroyInstance(engine.instance, nullptr);
SDL_DestroyWindow(window.window);
SDL_Quit();
not working main.cpp
try {
app.run();
}
catch (XiError error) {
std::cout << "Error " << error.code << ": " << error.definition << std::endl;
}
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(app.engine.physicalDevice, &deviceProperties);
std::cout << deviceProperties.deviceName << ", Driver Version " << deviceProperties.driverVersion << std::endl;
app.window.mainLoop();
app.shutDown();
app.run()
window.createWindow();
engine.window = window.window;
engine.createVulkanInstance();
window.instance = engine.getVkInstance();
window.createSurface();
engine.getPhysicalDevices();
engine.selectPhysicalDevice();
engine.createLogicalDevice();
window.mainLoop();
app.shutDown()
vkDestroyDevice(engine.logicalDevice, nullptr);
vkDestroySurfaceKHR(engine.instance, window.surface, nullptr);
vkDestroyInstance(engine.instance, nullptr);
SDL_DestroyWindow(window.window);
SDL_Quit();
window engine and app are pre-defined by my own classes
I've tried manually adding the different required and supported extensions, and it works, but it feels hacky and is quite a large bulk of code. If this is a weird out of scope error, I've really no idea. if any other code is needed I'll be happy to provide it and the GitHub can also be found here: https://github.com/XiniaDev/Xinia-Engine
I think your problem is that requiredFeatures in XiEngine is not initialised. You set a few values to true, but I think you need a memset(&requiredFeatures, 0, sizeof(requiredFeatures)); or similar at the start of XiEngine::XiEngine to fix it.
I have some production-critical code that has to keep running.
think of the code as
while (true){
init();
do_important_things(); //segfault here
clean();
}
I can't trust the code to be bug-free, and I need to be able to log problems to investigate later.
This time, I know for a fact somewhere in the code there is a segmentation fault getting thrown, and I need to be able to at least log that, and then start everything over.
Reading here there are a few solutions, but following each one is a flame-war claiming the solution will actually do more harm than good, with no real explanation. I also found this answer which I consider using, but I'm not sure it is good for my use case.
So, what is the best way to recover from segmentation fault on C++?
I suggest that you create a very small program that you make really safe that monitors the buggy program. If the buggy program exits in a way you don't like, restart the program.
Posix example:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <iostream>
int main(int argc, char* argv[]) {
if(argc < 2) {
std::cerr << "USAGE: " << argv[0] << " program_to_monitor <arguments...>\n";
return 1;
}
while(true) {
pid_t child = fork(); // create a child process
if(child == -1) {
std::perror("fork");
return 1;
}
if(child == 0) {
execvp(argv[1], argv + 1); // start the buggy program
perror(argv[1]); // starting failed
std::exit(0); // exit with 0 to not trigger a retry
}
// Wait for the buggy program to terminate and check the status
// to see if it should be restarted.
if(int wstatus; waitpid(child, &wstatus, 0) != -1) {
if(WIFEXITED(wstatus)) {
if(WEXITSTATUS(wstatus) == 0) return 0; // normal exit, terminate
std::cerr << argv[0] << ": " << argv[1] << " exited with "
<< WEXITSTATUS(wstatus) << '\n';
}
if(WIFSIGNALED(wstatus)) {
std::cerr << argv[0] << ": " << argv[1]
<< " terminated by signal " << WTERMSIG(wstatus);
if(WCOREDUMP(wstatus)) std::cout << " (core dumped)";
std::cout << '\n';
}
std::cout << argv[0] << ": Restarting " << argv[1] << '\n';
} else {
std::perror("wait");
break;
}
}
}
I'm trying to use a function to set a text to display on SFML based on the input parameters.
The function is of type Text and returns a text object. I've added cout statements to determine where the error is occurring. The text is as follows:
#include <string>
#include <iostream>
#include "functions.h"
using namespace std;
#include <SFML/Graphics.hpp> // include the SFML Graphics Library
using namespace sf;
Text showPoints(int& points, bool addPoints, bool isPlayer){
cout << "Function called " << endl;
string disPoints;
Text toDisp;
Font pointFont;
if(addPoints){
points += 1;
}
cout << "Points added" << endl;
if(!pointFont.loadFromFile("data/arial.ttf") ){
cout << "could not load font" << endl;
}
cout << "Loaded fonts" << endl;
if(isPlayer){
cout << "isPlayer conditional" << endl;
disPoints = "Player Points: ";
toDisp.setPosition(50, 800);
toDisp.setFont(pointFont);
toDisp.setString(disPoints);
toDisp.setFillColor(Color::White);
toDisp.setCharacterSize(30);
}
else if(!isPlayer){
cout << "isAI conditional" << endl;
disPoints = "AI Points: ";
toDisp.setPosition(1000, 200);
toDisp.setFont(pointFont);
toDisp.setString(disPoints);
toDisp.setFillColor(Color::White);
toDisp.setCharacterSize(30);
}
cout << "Conditions passed" << endl;
return toDisp;
}
//usual SFML stuff... under the while(window.isOpen()), before the
//window.display() and events check
cout << "Error on function?" << endl;
window.draw(showPoints(playerPoints, 0, 1));
cout << "First function passed" << endl;
window.draw(showPoints(AIPoints, 0, 0));
I expect that the text would show in the appropriate positions in the SFML window. However, the terminal outputs this and the window crashes:
Error on function?
Function called
Points added
Loaded fonts
isPlayer conditional
Conditions passed
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
It must be a problem with the window.draw() function as the second draw function is not called. So is it legal to call a function of type Text in the window.draw()? If not, how should I go about this? Google searches yielded nothing helpful on this issue.
I am seeing unusual signal numbers (for example 50, 80 or 117) from the following code when waiting for a child process to terminate. I am only seeing this from one particular child process, and I have no access to the process source code and it only happens some of the time.
I want to know what these unusual values mean, given NSIG == 32, and where I can find some documentation in the headers or man pages?
Note that this code runs in a loop sending progressively more menacing signals until the child terminates.
int status, signal;
if (waitpid(m_procId, &status, WNOHANG) < 0) {
LOGERR << "Failed to wait for process " << name() << ": " <<
strerror(errno) << " (" << errno << ")";
break;
} else if (WIFEXITED(status)) {
m_exitCode = WEXITSTATUS(status);
terminated = true;
LOGINF << "Process " << name() << " terminated with exit code " << m_exitCode;
} else if (WIFSIGNALED(status)) {
signal = WTERMSIG(status); // !!! signal is sometimes 50, 80 or 117 !!!
terminated = true;
LOGINF << "Process " << name() << " terminated by signal " << signal;
} else {
LOGWRN << "Process " << name() << " changed state but did not terminate. status=0x" <<
hex << status;
}
This is running under OSX 10.8.4, but I have also seen it in 10.9 GM seed.
EDIT Modifying the code as below makes the code more robust, however sometimes the child process gets orphaned as I guess the loop doesn't do enough to kill the child process.
else if (WIFSIGNALED(status)) {
signal = WTERMSIG(status);
if (signal < NSIG) {
terminated = true;
LOGINF << "Process " << name() << " terminated by signal " << signal;
} else {
LOGWRN << "Process " << name() << " produced unusual signal " << signal
<< "; assuming it's not terminated";
}
}
Note this code is part of the Process::unload() method of this class.
From the OS X manpage for waitpid, when specifing WNOHANG, you should check for a return of 0:
When the WNOHANG option is specified and no processes wish to report status, wait4() returns a process
id of 0.
The waitpid() call is identical to wait4() with an rusage value of zero. The older wait3() call is the
same as wait4() with a pid value of -1.
The code posted does not check for this, which suggests to me that the value of status is likely junk (the value of the int is never initialized). This could cause what you are seeing.
EDIT: status is indeed only set when waitpid returns > 0.
I'm modifying the stock quoter example from the wustl CORBA release. The assignment is to implement a reply handler for the StockFactory class that handles calls to get_stock()
Here's my FactoryHandler implementation:
FactoryHandler_i.h:
#ifndef TAO_TUTORIALS_QUOTER_AMI_CLIENT_FACTORYHANDLER_I_H
#define TAO_TUTORIALS_QUOTER_AMI_CLIENT_FACTORYHANDLER_I_H
#include "QuoterS.h"
class Stock_Factory_Handler_i : public POA_Quoter::AMI_Stock_FactoryHandler
{
public:
Stock_Factory_Handler_i (int *response_count, ::Quoter::Stock_var& result);
void get_stock (::Quoter::Stock_ptr ami_return_val);
void get_stock_excep (::Messaging::ExceptionHolder * excep_holder);
private:
int *response_count_;
::Quoter::Stock_var& result_;
};
#endif /* TAO_TUTORIALS_QUOTER_AMI_CLIENT_HANDLER_I_H */
FactoryHandler_i.cpp:
#include "FactoryHandler_i.h"
#include "ace/streams.h"
Stock_Factory_Handler_i::
Stock_Factory_Handler_i (int *response_count, ::Quoter::Stock_var& result)
: response_count_ (response_count), result_ (result)
{
}
void
Stock_Factory_Handler_i::get_stock (::Quoter::Stock_ptr ami_return_val)
{
cout << "storing result" << endl;
result_ = ami_return_val;
(*this->response_count_)++;
}
void
Stock_Factory_Handler_i::get_stock_excep (::Messaging::ExceptionHolder * excep_holder)
{
// We ignore the exception, but this counts as a response, otherwise
// the application would not finish.
cerr << "Exception raised while getting stock"
<< endl;
(*this->response_count_)++;
}
And the client.cpp, from just before the part where changes have been made:
// ...
// Create and activate the handler...
int response_count = 0;
Single_Query_Stock_Handler_i handler_i (&response_count);
Quoter::AMI_Single_Query_StockHandler_var handler =
handler_i._this ();
// Create and activate the factory handler...
Quoter::Stock_var result;
Stock_Factory_Handler_i factory_handler_i (&response_count, result);
Quoter::AMI_Stock_FactoryHandler_var factory_handler =
factory_handler_i._this();
// Send all the requests, careful with error handling
int request_count = 0;
for (int i = 2; i != argc+1; ++i) {
try {
// Get the stock object
cout << "looking up stock symbol " << argv[i] << endl;
factory->sendc_get_stock (factory_handler.in (), argv[i]);
sleep(3); // wait for a response
cout << "converting result" << endl;
Quoter::Single_Query_Stock_var stock =
Quoter::Single_Query_Stock::_narrow (result.in ());
cout << "checking result" << endl;
CORBA::Any any;
any <<= stock;
CORBA::TypeCode_var tc = any.type();
cout << tc->kind() << endl;
if (CORBA::is_nil (stock.in ())) {
cerr << "Cannot get single query interface for <"
<< argv[i] << ">" << endl;
continue;
}
cout << "reading result" << endl;
stock->sendc_get_price_and_names (handler.in ());
request_count++;
}
catch (Quoter::Invalid_Stock_Symbol &) {
cerr << "Invalid stock symbol <"
<< argv[i] << ">" << endl;
}
}
while (response_count < 2 * request_count // multiply by 2 because both handlers increment response_count
&& orb->work_pending ()) {
orb->perform_work ();
}
// ...
When running the client, the output is:
looking up stock symbol MSFT
converting result
checking result
14
Cannot get single query interface for <MSFT>
(The 14 is the typecode for Stock, that's only for debugging)
Notably missing from the above is the "storing result" message that's supposed to be printed in the FactoryHandler's get_stock() callback method. I'm at a loss as to why, since the sendc_get_stock() method doesn't produce any (immediate) errors and is basically just a copy of the StockHandler's code, and from there it's the responsibility of the AMI/ORB interface to make the callback. But the original example (with a StockHandler only) works fine.
What am I doing wrong (and how do I fix it)?
EDIT: another bit of information: on the server side, StockFactory's get_stock() method does get called.
Sorry, I've no aswer for you. But a hint, ask your question at TOA's maling list at http://www.cs.wustl.edu/~schmidt/ACE-mail.html
HTH
I think that your problem is that work_pending returns true only if the ORB has immediate work to do, so it returns false in the time after your client sent his request and before the server sends his reply.
To validate that, simply remove the && orb->work_pending() condition from the loop, and use the version of perform_work that takes a timeout argument.