PDA

View Full Version : Problem with SqLite and Qt



ad5xj
2nd June 2007, 06:19
Environment:
SUSE 10.2 KDE 3.5.5 Qt 3.3 Qt Designer and KDevelop
Here is snippets from my Main Form:

mainform.h
#include <qsqldatabase.h>
#include "connection.h"

class QSqlDatabase
----------------------
mainform.cpp
#include <qsqldatabase.h>

public
QSqlDatabase * DB;
----------------------
mainform.ui.h
bool MainWindow::openDB()
try
{
DB->isOpenError();
}
catch(...)
{
qWarning( "Failed to open database: " + DB->lastError().driverText() );
qWarning( DB->lastError().databaseText() );
return false;
};
---------------------
The above situation produces a segmentation fault on execution of any DB member function. The debugger shows a pointer handle for the DB object but expanding the members indicates an incomplete type message.

sqlite from the command line executes without error for any DDL or DML statement.

What am I missing?

wysota
2nd June 2007, 09:16
1. Qt doesn't throw any exceptions
2. Have you initialized that DB pointer?

ad5xj
2nd June 2007, 15:33
I did. It is initialized in the connection.h as per the example. It is good to know that Qt does not throw exceptions. How then do you trap such things as segmentation faults and such?

jacek
2nd June 2007, 15:53
I did. It is initialized in the connection.h as per the example.
Could you post that code?


How then do you trap such things as segmentation faults and such?
You can't trap segmentation faults. OS rules are simple. You can't simply say: "I'm sorry for writing someone's else memory. It won't happen again.". There's only one penalty for a process that violates memory protection --- termination.

All you can do in such case is a post mortem analysis in a debugger.

ad5xj
2nd June 2007, 16:01
Here is the relevant code from connection.h:

#ifndef CONNECTION_H
#define CONNECTION_H

#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>

static bool createConnection()
{
QSqlDatabase * DB = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("DBName");
return true;
}

#endif

jacek
2nd June 2007, 16:38
Here is the relevant code from connection.h:
First of all you shouldn't put code in .h files --- only declarations.


QSqlDatabase * DB = QSqlDatabase::addDatabase("QSQLITE");
Here you declare a new local variable DB, which has nothing to do (except for the name) with the DB variable from your previous post.


db.setDatabaseName("DBName");
Does this even compile? Note that variable names are case sensitive, so there's a difference between db and DB.

wysota
2nd June 2007, 16:45
You can't trap segmentation faults. OS rules are simple. You can't simply say: "I'm sorry for writing someone's else memory. It won't happen again.". There's only one penalty for a process that violates memory protection --- termination.

Actually you can. You can't trap SIGKILL and SIGSTOP, but SIGSEGV is interceptable. Of course it's not always safe to continue afterwards.

jacek
2nd June 2007, 17:12
Actually you can.
In theory yes, but not in practice. You intercept SIGSEGV and then what? All you can do is to do some cleanup/logging and exit. You won't know why the signal was sent. You can't be sure if your data is still intact and even whether the stack is valid.

ad5xj
2nd June 2007, 17:30
Ok I get that you can't trap some errors.

The problem I have is that this is code and structure is from the examples in Qt supplied by Trolltech. There are very few if any other examples to go by. The Tables example works great in Qt4 on XP but my problem is getting it to work on SUSE 10.2.

I tried moving the connection creation code to the main form but the same thing happens.

I am not seeing something, and I don't know what I don't know about SQLite and the Qt database classes in order to make this work.

wysota
2nd June 2007, 17:54
In theory yes, but not in practice. You intercept SIGSEGV and then what?

It depends what happened and what your app does.


All you can do is to do some cleanup/logging and exit.
True, but sometimes it's all you need. Flushing all unsaved data might be crucial.


You won't know why the signal was sent.
Yes, but sometimes you can deduce that information if you know your app tends to crash in a specific part of the code. Ability to intercept the signal might save your butt sometimes.


You can't be sure if your data is still intact and even whether the stack is valid.
If all you need is to clean up (and you shouldn't try to do anything more), the stack doesn't have to be valid - you'll be pushing to the stack, not popping from it.

jacek
2nd June 2007, 20:10
The Tables example works great in Qt4 on XP but my problem is getting it to work on SUSE 10.2.
If it works on XP, then it should work on SUSE without any changes in the source code (provided that your application uses only Qt). Maybe the SQLite plugin is missing?

Is libqsqlite.so plugin installed on your system?

jacek
2nd June 2007, 20:30
It depends what happened and what your app does.
But you have just agreed with me that you can't really tell what happened.


if you know your app tends to crash in a specific part of the code.
If my app tends to crash, I debug it to fix the crash.


If all you need is to clean up (and you shouldn't try to do anything more),
It's good that you agree with me that clean up and termination (instead of fix and continuation) is the only option in the event of SIGSEGV.


the stack doesn't have to be valid - you'll be pushing to the stack, not popping from it.
Remember that the data you want to save might be on that stack.

wysota
2nd June 2007, 20:46
But you have just agreed with me that you can't really tell what happened.
It doesn't mean you can't guess in what part of code you are. For example you might track the state of the application. I can imagine you might trap SIGSEGV to try to recover from a problem when using an unknown and assumed dangerous external code (like a plugin or so).


If my app tends to crash, I debug it to fix the crash.
Provided you have time and manage to isolate the problem.


It's good that you agree with me that clean up and termination (instead of fix and continuation) is the only option in the event of SIGSEGV.
Now that I think of it... no :) I just remembered that when you use mprotect, access to a protected region of memory causes a segfault, but the app is perfectly fine, so you can easily recover and continue. There is a chance it's not a sole case.


Remember that the data you want to save might be on that stack.
Yes, or you might be running a system that doesn't handle signals or find dozens of other cases where it won't work.

ad5xj
2nd June 2007, 21:23
All of this discussion about error trapping is nice and very interesting but it does not tell me why the application gives me the segmentation fault. Please try to stay on point as I am very new at C++ and Qt and it can become very confusing very quickly for me. I am not a veteran C programmer (hense the newbie forum questions).

I do have the sqlite plugin. It came as I installed SUSE 10.2 development distro, otherwise I would not have been able to run sqlite from the command line.

I just noticed that the debugger flashes a notice on the status line that there was a shared library event - if that helps any. I also notice that the database object is defined twice. Once as a mainwindow member and again independently. What is that all about? DB is ONLY a mainwindow public member and the createConnection() is in the Mainwindow code. It should not have created a new object independent of mainwindow.

wysota
2nd June 2007, 21:28
I do have the sqlite plugin. It came as I installed SUSE 10.2 development distro, otherwise I would not have been able to run sqlite from the command line.
Wrong. You're mixing sqlite library and qsqlite Qt plugin. Jacek was asking about the latter. Please list the content of your plugins/sqldrivers subdirectory of the Qt root install directory.


I also notice that the database object is defined twice. Once as a mainwindow member and again independently. What is that all about? DB is ONLY a mainwindow public member and the createConnection() is in the Mainwindow code. It should not have created a new object independent of mainwindow.

Jacek has already pointed that out in one of his previous posts - you declare a local variable, thus the pointer declared in the main window is still invalid - that's the reason of your segmentation fault.

jacek
2nd June 2007, 21:30
It doesn't mean you can't guess in what part of code you are.
Yes, you can guess. Guess is a very good word here.


Provided you have time and manage to isolate the problem.
So instead, you prefer to spend time on covering up the failure and trying to fix the errors it has caused using an unreliable mechanism, right? I prefer the fail-fast strategy.


Now that I think of it... no
Too bad that you have changed your mind. Again.


I just remembered that when you use mprotect, access to a protected region of memory causes a segfault, but the app is perfectly fine, so you can easily recover and continue. There is a chance it's not a sole case.

First you give an example, then you write about my example:


Yes, or you might be running a system that doesn't handle signals or find dozens of other cases where it won't work.

I see the pot is calling the kettle black.

ad5xj
2nd June 2007, 21:37
Ok here is what I have

/usr/lib/qt3(or qt4)/plugins/sqldrivers:

libsqlite.so
libsqlmysql.so
libqsqlodbc.so
libqsqlpsql.so

This appears to be the shared library plugins for SQLite, MySQL, ODBC, and Postgress.

jacek
2nd June 2007, 21:46
DB is ONLY a mainwindow public member and the createConnection() is in the Mainwindow code.
No, it's not. Look:

static bool createConnection()
{
...
}
createConnection() is a static function, so it's not a method of Mainwindow class, even if it ends up in the same file.

The second problem is that you do have two DB variables:

QSqlDatabase * DB = QSqlDatabase::addDatabase("QSQLITE");
In this line the "QSqlDatabase * DB" part means "declare a new local variable named DB of QSqlDatabase * type". It should be:
pointerToMainwindow->DB = QSqlDatabase::addDatabase("QSQLITE");
Although you should rather make createConnection() a method of Mainwindow class, if openDB() is already there.

But before you change your code further, make sure that Qt is configured correctly on your system. If everything is OK with your system, the sqlbrowser demo should list "QSQLITE" driver in Driver combo box in Connect dialog window.

ad5xj
2nd June 2007, 22:03
Thanks very much for the clarification. I can see now how it happened. I did change my code and it did eliminate the double definitions.

However I still get a program stop due to some event in the shared library (unexplained by debug messages) on the addDatabase call.

wysota
2nd June 2007, 22:08
So instead, you prefer to spend time on covering up the failure and trying to fix the errors it has caused using an unreliable mechanism, right? I prefer the fail-fast strategy.
If I have to perform some time intensive experiments and I'm short on time, so I know that I can barely make it with a non-crashing app, then I prefer it to crash now and then if I can continue the experiment without losing the already gathered data instead of debugging the application potentially for hours (remember the bug in the proxy model that caused you so much trouble?)


Too bad that you have changed your mind. Again.
I don't know what makes you think I changed my mind. I just remembered a real and useful example of trapping SIGSEGV where you can safely continue execution. If the app is broken, it's not safe to continue, but not every SIGSEGV means the app is broken.


First you give an example, then you write about my example:
I give you an example that there exists a possibility of using the approach and you counter with an argument that it might not always be possible to use it. I never said it was the UniversalWayOfDoingThings(TM).


I see the pot is calling the kettle black.
I'm afraid I don't know that phrase...



However I still get a program stop due to some event in the shared library (unexplained by debug messages) on the addDatabase call.
That's perfectly normal when using a debugger. Just continue execution.

ad5xj
2nd June 2007, 22:29
Well I did hit continue and gdb give me this:

Failed to read a valid object file image from memory.

(gdb) backtrace
Stopped due to shared library event
#0 0xb7f0ec70 in _dl_debug_state () from /lib/ld-linux.so.2
#1 0xb7f036f6 in dl_main () from /lib/ld-linux.so.2
#2 0xb7f13e6b in _dl_sysdep_start () from /lib/ld-linux.so.2
#3 0xb7f012c4 in _dl_start () from /lib/ld-linux.so.2
#4 0xb7f008d7 in _start () from /lib/ld-linux.so.2
(gdb) info args
No symbol table info available.
(gdb) info local
No symbol table info available.
(gdb) frame 0
#0 0xb7f0ec70 in _dl_debug_state () from /lib/ld-linux.so.2
(gdb) info args
No symbol table info available.
(gdb) info local
No symbol table info available.
(gdb) continue
[Thread debugging using libthread_db enabled]
[New Thread -1221592864 (LWP 6190)]
Stopped due to shared library event
(gdb) continue
[Switching to Thread -1221592864 (LWP 6190)]
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue

Breakpoint 1, MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:354
/home/ken/NetLogger/mainwindow.ui.h:354:12111:beg:0x806100c
(gdb) info thread
* 1 Thread -1221592864 (LWP 6190) MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:354
(gdb) backtrace
#0 MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:354
#1 0x08054c10 in MainWindow::init (this=0x80d9990) at mainwindow.ui.h:53
#2 0x0805a7e0 in MainWindow (this=0x80d9990, parent=0x0, name=0x0, fl=1) at mainwindow.cpp:740
#3 0x0804f660 in main (argc=
Cannot access memory at address 0x0

) at main.cpp:21
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0806100c in MainWindow::createConnection() at mainwindow.ui.h:354
breakpoint already hit 1 time
(gdb) info args
this = (class MainWindow * const) 0x80d9990
(gdb) info local
No locals.
(gdb) whatis this
type = class MainWindow * const
(gdb) continue
Stopped due to shared library event
(gdb) continue
Stopped due to shared library event
(gdb) continue

Program received signal SIGSEGV, Segmentation fault.
0x0806108a in MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:355
/home/ken/NetLogger/mainwindow.ui.h:355:12154:beg:0x806108a
(gdb) info thread
* 1 Thread -1221592864 (LWP 6190) 0x0806108a in MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:355
(gdb) backtrace
#0 0x0806108a in MainWindow::createConnection (this=0x80d9990) at mainwindow.ui.h:355
#1 0x08054c10 in MainWindow::init (this=0x80d9990) at mainwindow.ui.h:53
#2 0x0805a7e0 in MainWindow (this=0x80d9990, parent=0x0, name=0x0, fl=1) at mainwindow.cpp:740
#3 0x0804f660 in main (argc=
Cannot access memory at address 0x0

) at main.cpp:21
(gdb) info args
this = (class MainWindow * const) 0x80d9990
(gdb) info local
No locals.
(gdb) step
Couldn't get registers: No such process.
(gdb) info thread
Couldn't get registers: No such process.
(gdb) backtrace
(gdb) Process exited

wysota
2nd June 2007, 22:34
What exactly should we be looking at? I see you have a segfault :) What's inside mainwindow.ui.h:355?

ad5xj
2nd June 2007, 22:37
A call to QSqlDatabase::setDatabasename()

jacek
2nd June 2007, 23:01
If I have to perform some time intensive experiments and I'm short on time, so I know that I can barely make it with a non-crashing app, then I prefer it to crash now and then if I can continue the experiment without losing the already gathered data
I believe that a better approach is to save the data before the crash occurs, because you can't trust the memory contents after it.


remember the bug in the proxy model that caused you so much trouble?
Yes, I do remember, even better than you. It had nothing to do with crashes and preserving critical data. Simply a few empty columns were visible in the QTableView.


I don't know what makes you think I changed my mind.
So if you didn't change your mind, you still agree with me, right?


If the app is broken, it's not safe to continue, but not every SIGSEGV means the app is broken.
We already agreed that in case of a SIGSEGV, you can't tell what caused it, so as you said, you can only guess.


I give you an example that there exists a possibility of using the approach and you counter with an argument that it might not always be possible to use it.
But earlier I've said that the stack might not be valid and you countered it with an argument that it might not always be a problem.

Don't you think you are a bit inconsistent in the way you treat arguments?

jacek
2nd June 2007, 23:03
A call to QSqlDatabase::setDatabasename()
Could you post the code of MainWindow::createConnection()?

wysota
3rd June 2007, 10:33
I believe that a better approach is to save the data before the crash occurs, because you can't trust the memory contents after it.
Yes, of course, provided it is possible. Dumping lots of data to disk (or even ramdisk) takes time. If you can do it in an incremental fashion, it's ok, but otherwise it will slow down the experiment even more. I know you like your apps to be perfect and bug free and I completely agree with you, but sometimes the practical side differs from the theory and it's nice to have a way that can save you in such cases.


Yes, I do remember, even better than you. It had nothing to do with crashes and preserving critical data. Simply a few empty columns were visible in the QTableView.
The point is you wasted a lot of time to find the problem. And the problem wasn't in your code, so fixing it wasn't that easy.


So if you didn't change your mind, you still agree with me, right?
Read my post again.


We already agreed that in case of a SIGSEGV, you can't tell what caused it, so as you said, you can only guess.
No, not in that case. Here you can be pretty certain what happened by tracking the state of the application and performing some checks. And even if you couldn't, if you test your application enough, you can assume the problem doesn't come from somewhere else. And even if you didn't, you can continue and if something is really broken, the segfault will occur again and you can abort the app then.

Consider this snippet:

void somehandler(int){ flag = TRUE; }
//...
signal(SIGSEGV, somehandler);
protected[4] = 7; // sigsegv here
signal(SIGSEGV, SIG_DFL);
if(flag){ ... }

Here I can pretty well define where the segmentation fault occured and the example can be considered practical - it mimics a try-catch block.


But earlier I've said that the stack might not be valid and you countered it with an argument that it might not always be a problem.
But even earlier I said you can save the data and then you said the data might be on the stack and it might be broken. But I never said anything about saving the data from the stack, I was talking about saving data - some data.


Don't you think you are a bit inconsistent in the way you treat arguments?
I don't see any inconsistencies in my reasoning. I just see you can't admit that trapping SIGSEGV is not only a theoretical possibility. I have given you an example of such use and you still say the same. Maybe the snippet from this post will make you reconsider. Being stubborn in this case won't make your arguments better.


Could you post the code of MainWindow::createConnection()?

And the range of line numbers, please.

jacek
5th June 2007, 01:53
Yes, of course, provided it is possible.
Exactly the same thing can be said about your approach --- you can try to dump data after you intercept SIGSEGV, but it might not be possible. If I had to choose between possible decrease of performance and correct data, I would choose correct data.


The point is you wasted a lot of time to find the problem. And the problem wasn't in your code, so fixing it wasn't that easy.
But still it has nothing to do with the subject of this discussion, that is whether it is practical or not to recover from faults that caused SIGSEGV.


you can continue and if something is really broken, the segfault will occur again and you can abort the app then.
By that time your application could do something nasty. What if the SIGSEGV was caused by a buffer overflow? IMO fail-fast is much safer approach.


Here I can pretty well define where the segmentation fault occured and the example can be considered practical - it mimics a try-catch block.
I wouldn't call such abuse a practical example. Anyway, yes, you can provoke a segmentation fault and continue execution, but you are dragging this discussion away from where it started --- recovery from memory faults.


But even earlier I said you can save the data and then you said the data might be on the stack and it might be broken. But I never said anything about saving the data from the stack, I was talking about saving data - some data.
I'm afraid you failed to see the forest behind the trees. What I tried to show you is that you rebuke my arguments, yet at the same time you use exactly the same kind of argumentation to support your point. That's what I call an inconsistency.


Being stubborn in this case won't make your arguments better.
If you are going to step further into using ad hominem arguments, let's better simply agree that:
my point is that it's impractical to recover from memory faults and continue execution after receiving SIGSEGV, because you can't be sure about the state of the memory and what caused the signal to be emitted,
you don't agree with me.