SIGSEGV - Segmentation Fault bei dll Funktionsaufruf

Alles rund um die Programmierung mit Qt
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

SIGSEGV - Segmentation Fault bei dll Funktionsaufruf

Beitrag von embweb »

Hallo,

ich hatte schon einmal einen Post bezüglich meiner Probleme, Funktionen aus einer dll unter Qt zu verwenden. Beim ersten Thread bin ich davon ausgegangen, dass es sich um ein Timingproblem handelt und ich meine dll-Lesezugriffe irgendwie entkoppeln muss.
Ob es sich um ein Qt-spezifisches Problem handelt, bin ich mir nicht sicher. Meine weiteren Testapplikationen laufen jedenfalls allesamt auf der Kommandozeile und mit dem Microsoft Express C++ 2008 übersetzt.

Mein Problem: Ich bekomme einen "SIGSEGV - Segmentation Fault", wenn ich in meiner Methode eine Schleife verwende, die mehrmals durchlaufen wird und dabei eine Lesefunktion der dll verwendet.

Ich habe drei verschiedene Interfaces, ein Peak CAN Dongle (USB), eine Meilhaus ME4660 und ein PicoScope PS4224. Alle binde ich mit der mitgelieferten dll ein.
1.) Meilhaus funktioniert fehlerfrei - ich kann lesend/schreibend auf IO zugreifen und auch die ADC kanäle lesen
2.) Peak CAN wird mit SIGSEGV Fehler beendet, wenn ich in meiner Lese-Methode mehrfach die dll-Lesefunktion aufrufe (while buffer_read_completely)
3.) PicoScope crasht mit SIGSEGV wenn ich zum DRITTEN mal in meiner Lese-Methode die dll Funktion zum auslesen der Versionsdaten aufrufe.

Interessant ist, dass bei 2.) die App stabil(er) läuft, wenn ich in der Methode nur einmal lese und dafür dann den QTimer zum Aufruf auf eine kürzere Zeit setze (hier 2ms).
Auch kann ich bei 3.) meine Methode beliebig oft nacheinander aufrufen, wenn ich in dieser nicht mehr als zweimal auf die dll Lesefunktion zugreife.

Ich kapier's nicht ! Wenn es mit der Übergabe der Daten (call-by-value und -reference) zusammenhängt, müsste doch schon der erste Zugriff in die Hose gehen ? Wie oben schon erwähnt, funktionieren auch meine Testapplikationen für die Kommandozeile allesamt, die vom Konzept ähnlich aufgebaut sind.

Hoffe, Ihr könnt mir helfen - oder bin ich echt der Einzige, der sich damit schon mal rumgeärgert hat ?

Viele Grüße,

Marcus
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Kannst du etwas Code posten? Es ist gut möglich dass du irgendwo undefiniertes Verhalten produzierst. Nicht/falsch initialisierte Variablen, Indexzugriffe über die Grenzen hinaus, usw.
Was sagt denn dein Debugger dazu? Der kann dir eigentlich genau sagen, wo und wie der SegFault zustande kommt.
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

Beitrag von embweb »

Hallo,

ich werde mal den code zusammenstellen. Vom Konzept her ist es immer das selbe gewesen. Das MainWindow habe ich mit dem QDesigner erstellt, dann gibt es die main.c und die MainWindow Klasse und meine PicoScope Klasse. Zuerst habe ich beide Objekte in der main() deklariert und den Pointer von dem PicoScope Objekt an mainWindow übergeben. Das hab' ich geändert, da ich im Destruktor das Interface schliessen und die dll mit unload sauber "abwerfen" wollte...

1. main.cpp

Code: Alles auswählen

int main(int argc, char *argv[])
{
   QApplication app(argc,argv);
   int retval;

   MainWindow *win = new MainWindow();
   win->show();
   retval = app.exec();
   delete win;             // wird sonst scheinbar nicht zerstört, zumindest nicht das ps Objekt
   return retval;
}
2. MainWindow.cpp - Hauptfenster

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
   setupUi(this);

   ps = new qtps();        // anlegen des ps Objects - wird im Destruktor gelöscht
   if(ps->psDllLoaded)
      this->statusBar()->showMessage("PicoScope DLL loaded");
   else
      this->statusBar()->showMessage("PicoScope DLL failed");

   // signal slot associations
   QObject::connect(ps, SIGNAL(isOpened()), this, SLOT(portOn()));
   connect(ps, SIGNAL(isClosed()), this, SLOT(portOff()));

   // ...
}

MainWindow::~MainWindow()
{
   delete ps;      // löschen, damit das PicoScope wieder freigegeben wird !
}

void MainWindow::on_pushButton_getInfo_clicked(void)
{
   textEdit_Debug->append("check PicoScope Unit");
   ps->getUnitInfo();
   textEdit_Debug->append("ok");
}

4. qtps.h Header mit Klassendefinition

Code: Alles auswählen

#ifndef QTPS_H
#define QTPS_H

#include <QObject>
#include <QLibrary>                             // dll loading support

extern "C"
{
 #include "picoscope/ps4000Api.h"
}

/** typedefs for dll import */
typedef PICO_STATUS (*prot_psPspShort) (short* handle);
typedef PICO_STATUS (*prot_psPsShort) (short handle);
typedef PICO_STATUS (*prot_psPsShortShort)  (short handle, short start);
typedef PICO_STATUS (*prot_psGetUnitInfo) (short handle, char * string, short stringLength, short * requiredSize, PICO_INFO info);

// ...

class qtps : public QObject
{
      Q_OBJECT
public:
    qtps();
    ~qtps();

    bool psDllLoaded;
    bool psIsOpen;

public slots:
    void openUnit(void);
    void closeUnit(void);
    void getUnitInfo(void);
    void debug(void);

signals:
    void isOpened(void);
    void isClosed(void);
    void isDataReady(void);

private:
   QLibrary psLib;                              // use PicoScope DLL
   prot_psPspShort      ps4000OpenUnit;
   prot_psPsShort       ps4000CloseUnit;        // Function from DLL
   prot_psGetUnitInfo   ps4000GetUnitInfo;
   prot_psPsShortShort  ps4000FlashLed;

   PICO_STATUS psStatus;
   UNIT_MODEL  psUnit;

   tTriggerDirections directions;
};

5. qtps.cpp für die Kommunikation

Code: Alles auswählen

qtps::qtps()
{
   // PicoScope Dll laden
   QLibrary psLib("ps4000.dll");

   // Funktionen aufloesen
   ps4000OpenUnit =  (prot_psPspShort) psLib.resolve("ps4000OpenUnit");
   ps4000CloseUnit = (prot_psPsShort)  psLib.resolve("ps4000CloseUnit");
   ps4000FlashLed = (prot_psPsShortShort) psLib.resolve("ps4000FlashLed");
   ps4000GetUnitInfo = (prot_psGetUnitInfo) psLib.resolve("ps4000GetUnitInfo");

   // initialize public and private variables
   psDllLoaded = (ps4000OpenUnit && ps4000CloseUnit &&
                  ps4000FlashLed && ps4000GetUnitInfo)? true:false;
   // set default
   closeUnit();
}

qtps::~qtps()
{
   if(psIsOpen)
      closeUnit();

   if(psDllLoaded)
      psLib.unload();
}

void qtps::getUnitInfo(void)
{
   char line[80];     // besser QByteArray verwenden
   int variant;
   short i, r;
   PICO_STATUS status = PICO_OK;

   r=0;
   if(psUnit.handle)
   {
//    for(i=0; i<5; i++)
      for(i=0; i<2; i++)      // SIGSEGV - Segmentation Fault beim dritten Zugriff auf dll Funktion !?
      {
         status = ps4000GetUnitInfo(psUnit.handle, line, sizeof(line), &r, i);
      }
   }
}
Beim debuggen kann man zwar gut verfolgen, wie die Daten per call-by-reference in dem char Array landen. Beim dritten Zugriff auf die dll Funktion ploppt dann die Fehlermeldung auf und die Überwachung der lokalen Variablen ist gelöscht... und mit den Addressen in der Dateiliste kann ich ehrlich gesagt nicht so richtig was anfangen. Bin halt dann doch eher auf den Microcontrollern zu Hause.
Dateianhänge
Debugger bei/nach SIGSEGV Fehlermeldung
Debugger bei/nach SIGSEGV Fehlermeldung
debug.jpg (99.24 KiB) 6474 mal betrachtet
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

und die dll mit unload sauber "abwerfen" wollte...
Das ist unnoetig.. Siehe:
http://doc.qt.nokia.com/4.0/qlibrary.html#unload hat geschrieben: This happens automatically on application termination, so you shouldn't normally need to call this function.
Wenn es also nur darum geht, kannst du auf die Pointer verzichten.. Beim Ende des Scopes werden Objekte übrigens sowieso zerstört.. also:

Code: Alles auswählen

int main(int argc, char *argv[])
{
   QApplication app(argc,argv);
   MainWindow win;
   win.show();
   return app.exec();
}
Zum Problem: das Verhalten ist (wie schon von franzf erwähnt) typisch für ein Programm, welches entweder fehlerhafte Pointeroperationen hat oder den Stack zerschiesst. Den Stack kriegst du in deinem Fall kaputt, wenn die Funktionspointer nicht mit dem Aufruf der Library übereinstimmen (Anzahl und Grösse der Argumente).

Auf den ersten Blick sehen ich lediglich, dass die Variabel "psLib" einmal lokal (Im CTor) und einmal als Member existiert. Das ist vermutlich nicht gewollt.
Zweitens sind die Funktionspointer solange gültig, wie die DLL im Speicher besteht. Wehe du entlädst die DLL und rufst danach "qtps::getUnitInfo()"auf!!

Zum Vorgehen: Ich würde sowas unbedingt vorher in einer Testumgebung untersuchen (um andere Fehler auszuschliessen). In einer einzelnen main.cc kannst du problemlos die Library laden und einfach mal alle 7 Infos durch ps4000GetUnitInfo(..) abrufen. Klappt dies, kannst du dich an das grössere Projekt machen und eine saubere Struktur reinbringen.

hth..
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

Zerstörung ja, aber keine Ausführung des Destruktors

Beitrag von embweb »

Hallo,

danke für Deine Antwort.
Ich bin auch davon ausgegangen, dass Objekte automatisch zerstört werden. Jedoch ist "Zerstört werden" und "Destruktor ausführen" wohl zweierlei.
Bei der automatischen Zerstörung wird der Code, den ich explizt im Destruktor angegeben habe, nicht ausgeführt, somit bleibt das Interface (PicoScope) geöffnet, da CloseUnit nicht ausgeführt wird und beim Neustart des Programms ist dann kein Zugriff möglich, da das PicoScope immer noch geöffnet ist.
Daher mein Ansatz mit delete, der auch funktioniert.

Ich werde mal alles in die main() verfrachten und schauen, ob die App dann auch crasht.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Zerstörung ja, aber keine Ausführung des Destruktors

Beitrag von franzf »

embweb hat geschrieben:Ich bin auch davon ausgegangen, dass Objekte automatisch zerstört werden. Jedoch ist "Zerstört werden" und "Destruktor ausführen" wohl zweierlei.
Eigentlich eher nein... Wenn du ein Objekt selber zerstörst dann (hoffentlich) nur wenn das ein Pointer ist, und das machst du mit delete. Das delete ruft aber den Destruktor auf! Danach wird Speicher freigegeben.
Bei automatischer Zerstörung besteht der einzige Unterschied darin, dass du nicht selber delete aufrufst.
Bei der automatischen Zerstörung wird der Code, den ich explizt im Destruktor angegeben habe, nicht ausgeführt
Das ist aber ein Irrglaube ;)
Wie gesagt wird der Destruktor aufgerufen.
Versuch es doch einfach mit einer kleinen Testklasse, die im Konstruktor und Destruktor eine kleine Meldung auf die Konsole zaubert, und teste die ganzen Möglichkeiten zur Erstellung/Zerstörung durch.
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

Test Destruktor

Beitrag von embweb »

Wie schon gesagt, bin ich ja auch zuerst davon ausgegangen, dass es "automatisch" abläuft.
Wenn ich delete jedoch nicht explizit aufrufe, bleibt die LED am Interface an und wird nicht beendet mit closeUnit. Die main() hatte ich ja zuerst auch so, wie Du oben nochmal gezeigt - ganz klassisch nach Qt-Lehrbuch.

Ich habe den Ansatz mit "alles in die main() und debuggen" noch mal weiter verfolgt...
Irgendwas haut's da kaputt im Speicher. Die for() sollte von 0..4 Zählen. Im Fehlerfall zählt sie bis 3, dann gibt es einen Sprung zurück auf 0 und sie zählt weiter bis 4. Manchmal läuft die for() zu Ende, manchmal gibt es auch wieder nach dem 3. oder 4. Zugriff eine SIGSEGV.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Test Destruktor

Beitrag von franzf »

embweb hat geschrieben:Wie schon gesagt, bin ich ja auch zuerst davon ausgegangen, dass es "automatisch" abläuft.
Wenn ich delete jedoch nicht explizit aufrufe, bleibt die LED am Interface an und wird nicht beendet mit closeUnit. Die main() hatte ich ja zuerst auch so, wie Du oben nochmal gezeigt - ganz klassisch nach Qt-Lehrbuch.
Verwechlse "Destruktor" nicht mit "Deinitialisierer"!
Deine Zugriffe auf das Handle sind C. C kennt keine Destruktoren.
Dein "UNIT_MODEL psUnit;" wird auch zerstört. Aber eben die Verbindung nicht geschlossen. Deshalb ist das close auch notwendig.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Re: Test Destruktor

Beitrag von solarix »

embweb hat geschrieben:[...]Im Fehlerfall zählt sie bis 3, dann gibt es einen Sprung zurück auf 0 und sie zählt weiter bis 4. Manchmal läuft die for() zu Ende, manchmal gibt es auch wieder nach dem 3. oder 4. Zugriff eine SIGSEGV.
Da wird der Stack zersäbelt. Kannst du mal die komplette main.cc posten?

[Edit]
Und du verwendest nicht die 32Bit dll unter Windows 7/Vista 64Bit.. oder?
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

meine Test-main()

Beitrag von embweb »

Hallo,

nein - kein Visto oder Windows 7. Das System besteht aus einem Intel D945GCLF2 Mainboard mit Atom 330 und 1GB RAM, es ist ein WIndows XP Prof. 32Bit drauf. Parallel wollte ich das ganze später noch auf Ubuntu portieren, jedoch fehlt mir zu einem Interface ein Linux-Treiber.

Anbei mein letzter Stand der Test-main:

Code: Alles auswählen

#include <QApplication>
#include <QLibrary>

#define PICO_OK                           0x00000000UL
#define PICO_EEPROM_CORRUPT					0x0000000AUL
#define PICO_INFO_UNAVAILABLE             0x0000002AUL
#define PICO_NOT_ENOUGH_SEGMENTS				0x00000042UL

// typedefs
typedef unsigned long PICO_STATUS;
typedef unsigned long PICO_INFO;

typedef PICO_STATUS (*prot_psPspShort) (short* handle);
typedef PICO_STATUS (*prot_psPsShort) (short handle);
typedef PICO_STATUS (*prot_psGetUnitInfo) (short handle, char * string, short stringLength, short * requiredSize, PICO_INFO info);

// prototypes
prot_psPspShort      ps4000OpenUnit;
prot_psPsShort       ps4000CloseUnit;
prot_psGetUnitInfo   ps4000GetUnitInfo;

/** main Process entry...
 */
int main(int argc, char *argv[])
{
   QApplication app(argc,argv);
   QLibrary psLib("ps4000.dll");

   static bool DllLoaded;
   static bool IsOpen;
   static bool failure = false;
   static char line[256];              // besser QByteArray etc verwenden
   static short i, r=0;

   static short handle=0;
   static PICO_STATUS retval;

   // Funktionen aufloesen
   ps4000OpenUnit  = (prot_psPspShort) psLib.resolve("ps4000OpenUnit");
   ps4000CloseUnit = (prot_psPsShort)  psLib.resolve("ps4000CloseUnit");
   ps4000GetUnitInfo = (prot_psGetUnitInfo) psLib.resolve("ps4000GetUnitInfo");
   // check if assigned
   DllLoaded = (ps4000OpenUnit && ps4000CloseUnit && ps4000GetUnitInfo)? true:false;

   // test it now
   if(DllLoaded)
   {
      retval = ps4000OpenUnit(&handle);
      if((retval != PICO_OK) && (retval != PICO_EEPROM_CORRUPT) )
         IsOpen = false;
      else
         IsOpen = true;
   }

   // test read access and close unit afterwards
   if( (IsOpen== true) && (handle!=0) )
   {
      for(i=0; i<5; i++)      // SIGSEGV - Segmentation Fault beim dritten Zugriff auf dll Funktion !?
      {
         retval = ps4000GetUnitInfo(handle, line, sizeof(line), &r, i);
         switch(retval)
         {
            case PICO_OK:             
               failure = false;
               break;

            case PICO_INFO_UNAVAILABLE:
               failure = true;
               break;

            case PICO_NOT_ENOUGH_SEGMENTS:
               failure = true;
               break;

            default:
               failure = true;
               break;
         }
      }
      // close Interface
      retval = ps4000CloseUnit(handle);
      IsOpen = false;
   }
   return 0;
}
Wenn der SIGSEGV Fehler kommt, bleibt er im Debugger in qobject.cpp stehen, und zwar bei:

Code: Alles auswählen

/*! \internal
 */
void QObjectPrivate::clearGuards(QObject *object)
{
    QObjectPrivate *priv = QObjectPrivate::get(object);
    QGuard<QObject> *guard = priv->extraData ? priv->extraData->objectGuards : 0;
...
Zuletzt geändert von embweb am 23. April 2010 10:19, insgesamt 1-mal geändert.
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

Beitrag von embweb »

... ach ja, das hatte ich noch vergessen hinzuzufügen:

Die Variablen waren bei meinen ersten Tests zuvor nicht alle static. War nur so eine Vermutung...
Bei beiden Versionen kommt ein SIGSEGV Fehler, einziger Unterschied:
- bei der Version ohne static bleibt er beim Aufruf der
retval = ps4000GetUnitInfo(handle, line, sizeof(line), &r, i); Zeile hängen,

- bei der mit static wie beschrieben in der qobject.cpp

Ich "blick" das nicht... Vielleicht regnet es ja am Wochenende, dann arbeite ich das Qt Handbuch noch mal 'gscheit durch um die Abläufe im Hintergrund mal besser zu verstehen... wobei das Problem wohl, bis auf die Verwendung von QLibrary, wenig mit Qt zu tun hat.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Das Problem hängt nicht mit Qt zusammen. Wenn der Stack zerschossen wird, crashed halt irgendwas...

Hast du ein Code-Beispiel vom Hersteller?

[EDIT] geloescht. was überlesen [EDIT]
Zuletzt geändert von solarix am 23. April 2010 10:23, insgesamt 1-mal geändert.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Hast du schonmal versucht zu checken ob es evtl. an der Abfragefrequenz liegt?
In der Schleife ein

Code: Alles auswählen

retval = ps4000GetUnitInfo(handle, line, sizeof(line), &r, 0);
Dann die "0" schrittweise erhöhen.
Desweiteren könntest du den "handle != 0"-Check in der Schleife direkt vor den Aufruf der Funktion setzen.
Desweiteren wäre eine genauere Spezifikation der ps4000*-Funktionen nicht schlecht
*) Parameter
*) erwartete Werte der Parameter
usw
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

@franzf: vermutlich http://www.imex.ie/files/u3/ps4000pg_en_pdf_31123.pdf

@embweb: zeig uns mal das in der Doku erwähnte "ps4000.c".. wenigstens die Funktionspointer-Deklaration daraus..
embweb
Beiträge: 15
Registriert: 15. Oktober 2009 12:30
Wohnort: Alling
Kontaktdaten:

Beitrag von embweb »

... kann man im Forum Dateien anhängen/hochladen, so dass diese als Link auftauchen? Sonst wird's unübersichtlich.
Die angegebene Doku (Link) ist korrekt. Es ist ein PicoScope 4224.

Allerdings gibt es im SDK keine ps4000.c ... nur die Header und ein Beispiel für MS Express / Kommandozeile.

In der ps4000Api.h ist die Funktion wie folgt definiert :

Code: Alles auswählen

PREF0 PREF1 PICO_STATUS PREF2 PREF3 (ps4000GetUnitInfo)
 	(
	  short   handle, 
	  char * string,
	  short   stringLength,
	  short * requiredSize,
	  PICO_INFO   info
	);
wobei die ganzen PREFx zuvor per #define in Abhängigkeit vom System gesetzt werden. Also :

Code: Alles auswählen

#ifdef __cplusplus
	#define PREF0 extern "C"
	#define TYPE_ENUM
#else
	#define PREF0
	#define TYPE_ENUM enum
#endif

#ifdef WIN32
	typedef unsigned __int64 u_int64_t;
	#ifdef PREF1
	  #undef PREF1
	#endif
	#ifdef PREF2
	  #undef PREF2
	#endif
	#ifdef PREF3
	  #undef PREF3
	#endif
	/*	If you are dynamically linking PS4000.DLL into your project #define DYNLINK here
	 */
	#ifdef DYNLINK
	  #define PREF1 typedef
		#define PREF2
		#define PREF3(x) (__stdcall *x)
	#else
	  #define PREF1
		#ifdef _USRDLL
			#define PREF2 __declspec(dllexport) __stdcall
		#else
			#define PREF2 __declspec(dllimport) __stdcall
		#endif
	  #define PREF3(x) x
	#endif
#else
...
PICO_INFO ist als unsigned long in der picoStatus.h definiert, so wie in meiner Test-main auch. Es gibt 5 Einträge, die damit selektiert werden können (index 0..4), z.B. die Serien-Nr, Typ, USB Support, Firmware etc.
Antworten