Malloc induit une boucle infinie

macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011 - 15 sept. 2011 à 18:13
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011 - 18 sept. 2011 à 19:33
Bonjour tout le monde !

Pour mon premier message, j'aimerai mettre en évidence un problème assez étrange dont je ne parviens pas a m'extirper...

J'ai créé une application avec une interface graphique QT. Cela fonctionne très bien avec Linux. J'ai essayé de la porter sous Windows, et la patatra...

L'application utilise une librairie externe (.dll) que j'ai donc créée avec Cygwin pour Windows. Lorsque j'appelle une fonction contenue dans cette librairie, la première instruction est une réservation mémoire (malloc). Et la le programme entre en boucle infinie...

J'ai donc essayé un débogage pas-a-pas en assembleur, et l’implémentation du malloc semble responsable. En effet, le programme est multi-threadé, et je ne sais pas si le problème est que le malloc n'est pas "thread-safe", ou si il peut venir d'ailleurs (c'est la première fois que je compile avec Windows).

Vu le déroulement du code, j'ai vraiment l'impression qu'un thread en appelle un autre, puis un autre, jusqu’à revenir au thread de départ, pour recommencer a l'infini...
Ce n'est que mon hypothèse, le problème est peut être ailleurs.

De plus, la librairie externe a été compilée avec Cygwin, alors que l'application finale QT est compilée avec MinGW32. Peut être une incompatibilité...

Merci a ceux qui feront quelques propositions...

12 réponses

cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
16 sept. 2011 à 10:57
Cela fonctionne très bien avec Linux.

Avant d'affirmer cela, plusieurs tests à faire:
- Passe ton programme dans valgrind (memcheck) => Vérifie les leak et les mauvais accès mémoire.
- Passe ton programme dans helgrind => Vérifie les soucis de concurrence.

librairie

Bibliothèque.

le malloc n'est pas "thread-safe"

Le malloc n'a jamais été thread-safe :) Néanmoins, je doute que le problème vienne de malloc, mais plutôt de son utilisation (sous une appli multi-threadé, une erreur de synchronisation peut provoquer des erreurs très loin de la "vraie" erreur).

De plus, la librairie externe a été compilée avec Cygwin, alors que l'application finale QT est compilée avec MinGW32. Peut être une incompatibilité...

Pas sur que cygwin et mingw32 aient des compilateurs à jour, ni qu'ils aient les mêmes versions. As-tu tenté de compiler la bibliothèque externe et le programme principal à l'aide du même compilateur ? (Normalement, tant que l'ABI est compatible, ça ne devrait pas être un soucis, mais c'est toujours une piste à tenter.)

Autre question: Fais-tu des allocations dans la bibliothèque externe ?

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
16 sept. 2011 à 17:27
Merci pour cette réponse.

Je passe le programme dans Valgrind dès mon retour chez moi, c'est vrai que Linux est plus tolérant avec les allocations mémoire "foireuses".

Le malloc n'a jamais été thread-safe :)


Aie. J'ai bien essayé de désactiver le multi-thread, car je n'en ai pas vraiment besoin, mais je me suis dit que peut être était-ce nécessaire pour une appli QT ? De toute façon le retrait de l'option -mthreads dans le Makefile n'a rien donné.

Je vais par ailleurs essayer de tout compiler avec MinGW32 comme suggéré.

Fais-tu des allocations dans la bibliothèque externe ?


Si tu entends par la des allocations dynamiques avec new, non, pas dans la bibliothèque externe, mais certaines sont faites dans le programme principal.
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
16 sept. 2011 à 17:39
Je passe le programme dans Valgrind dès mon retour chez moi, c'est vrai que Linux est plus tolérant avec les allocations mémoire "foireuses"


Il n'y a pas plus de tolérance d'un côté que de l'autre. Jsute quelques différences internes. L'important est de bien comprendre que tant que tu n'écris pas sur une page non ouverte, il n'y a pas de d'erreur même en cas de dépassement de borne.
(D'où le nom segfault, qui devrait plutôt s'appeler pagefault).
On peut forcer les allocations en fin de page avec la bibliothèque DUMA. (En revanche ne pas mélanger vlagrind et DUMA).

J'ai bien essayé de désactiver le multi-thread, car je n'en ai pas vraiment besoin,

Au vu de cette remarque, quelque chose me dit que tu as des connaissances très superficielles en multi-threading, je me trompe :p ?
En fait, tant que tes données sont locales et qu'il n'y a pas deux threads d'exécution qui veulent accéder à une variable partagée, il n'y a aucun problème. Aucun mutex n'est nécessaire.

Si tu entends par la des allocations dynamiques avec new, non,

Ok, tant mieux.

Si ton programme principal, en dehors de Qt n'est pas trop gros, n'hésite pas à le poster. Il y a peut être une subtilité que tu n'as pas perçu, et qui pourrait être la vraie cause du problème. (C'est du C ou du C++ que tu fais ?).

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
16 sept. 2011 à 18:18
quelque chose me dit que tu as des connaissances très superficielles en multi-threading


Bien vu ! Mais comme j'utilise quelques objets QT, je me suis dit que ceux-ci nécessitaient peut être l'utilisation de threads, étant donné que je n'ai aucune idée sur la manière dont ils sont implémentés.

C'est du C ou du C++ que tu fais ?


La bibliothèque est codée en C, mais mon programme est en C++ pour pouvoir utiliser les objets QT.

Je vais voir s'il est intéressant de poster mon programme, ou du moins la fonction qui pose problème. De toute façon elle n'est pas très longue, ça devrait pouvoir se faire.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
18 sept. 2011 à 02:53
Voilà, désolé pour le retard.
J'ai isolé la partie de code problématique en assembleur. Je ne pense pas que le code C++ soit révélateur de quoi que ce soit concernant ce problème, je me permets donc de poster de l'assembleur (toute mes excuses) !

Donc, ci-dessous l'appel du Malloc par le prog principal :

0x6a6453d1  <+0x0001>         mov    %esp,%ebp
0x6a6453d3  <+0x0003>         sub    $0x28,%esp
0x6a6453dd  <+0x000d>         mov    %ebx,-0xc(%ebp)
0x6a6453e0  <+0x0010>         mov    %esi,-0x8(%ebp)
0x6a6453e3  <+0x0013>         mov    0xc(%ebp),%esi
0x6a6453e6  <+0x0016>         mov    %edi,-0x4(%ebp)
0x6a6453e9  <+0x0019>         mov    0x10(%ebp),%edi
0x6a6453d6  <+0x0006>         movl   $0xd0,(%esp)
0x6a6453ec  <+0x001c>         call   0x6a646b90 (malloc)
0x6a6453f3  <+0x0023>         mov    %eax,%ebx


Le problème apparaît donc un peu plus loin, dans l'implémentation du malloc :

0x610c34f9          push   %ebx
0x610c34fa  <+0x0001>         push   %edx
0x610c34fb  <+0x0002>         mov    %fs:0x4,%ebx
0x610c3502  <+0x0009>         mov    $0x1,%eax
0x610c3507  <+0x000e>         xchg   %eax,-0x24d8(%ebx)
0x610c350d  <+0x0014>         mov    %eax,-0x24dc(%ebx)
0x610c3513  <+0x001a>         test   %eax,%eax
0x610c3515  <+0x001c>         je     0x610c351e <_sigfe+37>
0x610c3517  <+0x001e>         call   0x61077680 (yield)
0x610c351c  <+0x0023>         jmp    0x610c3502 <_sigfe+9>
0x610c351e  <+0x0025>         mov    $0x4,%eax
0x610c3523  <+0x002a>         xadd   %eax,-0x24d4(%ebx)
0x610c352a  <+0x0031>         lea    0x610c3545,%edx
0x610c3530  <+0x0037>         xchg   %edx,0xc(%esp)
0x610c3534  <+0x003b>         mov    %edx,(%eax)
0x610c3536  <+0x003d>         incl   -0x24e0(%ebx)
0x610c353c  <+0x0043>         decl   -0x24d8(%ebx)
0x610c3542  <+0x0049>         pop    %edx
0x610c3543  <+0x004a>         pop    %ebx
0x610c3544  <+0x004b>         ret


Et c'est ici que tout se complique, l'instruction #10 (le jump) est exécutée en boucle ! Le test (ligne #7) se révèle faux à chaque passage. Et l'appel de "yield" ne donne rien. Je vous mets le code de "yield" ci-dessous pour information :

0x61077680          sub    $0xc,%esp
0x61077683  <+0x0003>         call   0x61159f38 (SwitchToThread@0)
0x61077688  <+0x0008>         call   0x61159f38 (SwitchToThread@0)
0x6107768d  <+0x000d>         add    $0xc,%esp
0x61077690  <+0x0010>         jmp    0x61159f38 (SwitchToThread@0)
0x61077695  <+0x0015>         lea    0x0(%esi,%eiz,1),%esi
0x61077699  <+0x0019>         lea    0x0(%edi,%eiz,1),%edi


Ici, les appels au thread@0 sont un peu étranges...

J'imagine que tout ça est un peu rébarbatif, mais si un courageux a une option, je lui serais vraiment reconnaissant de la proposer.

Et encore une fois, ceci n'est peut être qu'un symptôme d'un problème situé ailleurs...
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
18 sept. 2011 à 13:55
Je ne pense pas que regarder le code de malloc en assembleur t'aidera. Si malloc était buggé, ça se saurait depuis le temps...

L'erreur est induite par une erreur en amont. Il suffit d'avoir une erreur mémoire ou une erreur de synchronisation pour immédiatement obtenir un comportement indéterminé.

Que donne un passage de valgrind ? De helgrind ? De DUMA ?

Peux-tu poster ton code C ?

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
18 sept. 2011 à 16:59
Effectivement il y a un gros problème, Valgrind me renvoie un nombre incalculable d'erreurs de ce genre :

4,080 bytes in 1 blocks are possibly lost in loss record 5,805 of 5,874
4617==    at 0x4C27870: memalign (vg_replace_malloc.c:581)
4617==    by 0x4C278C9: posix_memalign (vg_replace_malloc.c:709)
4617==    by 0x737233B: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
4617==    by 0x7373860: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
4617==    by 0x7CE4186: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7CE4238: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7D05A6A: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7D08361: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7D0C17F: g_type_add_interface_static (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0xE3C9C65: atk_no_op_object_get_type (in /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.9.1)
4617==    by 0xE3C9C8D: atk_no_op_object_new (in /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.9.1)
4617==    by 0xD706C8D: ??? (in /usr/lib/libgtk-x11-2.0.so.0.2400.4)
4617== 
4617== 4,080 bytes in 1 blocks are possibly lost in loss record 5,806 of 5,874
4617==    at 0x4C27870: memalign (vg_replace_malloc.c:581)
4617==    by 0x4C278C9: posix_memalign (vg_replace_malloc.c:709)
4617==    by 0x737233B: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
4617==    by 0x7373860: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
4617==    by 0x7373915: g_slice_alloc0 (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.2800.6)
4617==    by 0x7D0C398: g_type_create_instance (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7CEA76B: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7CED388: g_object_newv (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7CEE320: g_object_new_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x7CEE620: g_object_new (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.2800.6)
4617==    by 0x5375FD9: ??? (in /usr/lib/libQtGui.so.4.7.2)


Leur quantité est proportionnelle au temps d'exécution du programme.
Je vous mets mon code C++. D'abord, le constructeur de la fenêtre :

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /***************************************************************************** on charge la liste ****/
    liste = new QComboBox();
    liste->setParent(this);
    liste->setGeometry(20,20,350,30);
    liste->setEnabled(true);
    liste->addItem(QString("fichiers disponibles"));

    struct dirent *lecture;
        
DIR *rep;
        rep = opendir("./files" );
        while ((lecture = readdir(rep))) {
            std::string ext = lecture->d_name;

            if(ext != "." && ext != "..")
                if(strcmp(ext.substr((int)strlen(ext.c_str())-3,3).c_str(),"url") == 0)
                    liste->addItem(QString(get_string(lecture->d_name)));
        }
        closedir(rep);

    liste->show();

    /***************************************************************************** on charge la zone texte ****/
    infos = new QTextEdit();
    infos->setParent(this);
    infos->setGeometry(20,60,350,180);
    infos->setText(QString(get_string("Liste chargée.")));
    infos->show();

    /***************************************************************************** on charge le bouton ****/
    press = new QPushButton();
    press->setParent(this);
    press->setGeometry(20,250,70,30);
    press->setText(QString(get_string("Charger")));
    press->show();

    stop = new QPushButton();
    stop->setParent(this);
    stop->setGeometry(20,250,70,30);
    stop->setText(QString(get_string("Stop")));
    stop->hide();


    /***************************************************************************** on charge la barre ****/
    barre = new QProgressBar();
    barre->setParent(this);
    barre->setGeometry(100,250,270,30);
    barre->setValue(0);
    barre->show();

    /***************************************************************************** déclarations signaux/slots ****/
    connect(press, SIGNAL(clicked()), this, SLOT(s_clicked()));
    connect(stop, SIGNAL(clicked()), this, SLOT(stop_clicked()));
    connect(liste,SIGNAL(currentIndexChanged(QString)), this, SLOT(s_liste_select(QString)));
    selectedProgram = new QString("");
    statut = false;
    bar_value = 0;

}


Et le slot où apparaît le problème lorsque l'on clique sur le bouton "Charger". Il essaye de se connecter au flux mms, et entre en boucle infinie. Je précise que la toute première instruction de la fonction mmsx_connect() est le malloc en question.

void MainWindow::s_clicked()
{
    statut = true;
    press->hide(); //bouton
    stop->show();  //bouton
    infos->setText(QString(get_string("Etablissement de la connexion...")));
    this->update();
    this->repaint();


    // la boucle infinie apparaît lors de l'appel de la fonction mmsx_connect()
    mmsx_t * tmp = mmsx_connect(0, 0, "mms://site.com/video.wmv", 8*1024*300);
    
    if( inst == NULL )
    {
        QMessageBox message(QMessageBox::Information, "alerte", "connexion échouée");
        message.exec();
        std::cout << "Erreur lors de la connexion au flux." <<std::endl;
        return;
    }
}

Il y a d'autres instructions après le test if(), mais comme elles ne sont jamais exécutées je ne les ai pas inclues pour que ce soit plus léger à lire.


En espérant que ça aide !
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
18 sept. 2011 à 17:08
Je peux surement t'aider mais je n'ai besoin que d'une seule chose: Le code C, en dehors de Qt.
Si tu me le postes, je pourrais le tester sur ma machine, et peut être voir le potentiel souci.
(Avant d'embarquer ton code dans Qt, il faut déjà tester le module C tout seul dans valgrind).

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
18 sept. 2011 à 18:11
Le code sans QT est le suivant :

#include <libmms/mmsx.h>
#include <stdio.h>
#include 
#include <string>
#include <sys/types.h>
#include <dirent.h>
#include <math.h>
#include <time.h>
#include <sstream>

int main()
{

float bar_value = 0;
mmsx_t * inst =  mmsx_connect(NULL, NULL, "mms://a988.v101995.c10199.e.vm.akamaistream.net/7/988/10199/3f97c7e6/ftvigrp.download.akamai.com/10199/cappuccino/production/publication/Autre/Autre/2011/S37/J5/313308_desparolesetdesactes_20110915.wmv", 8*1024*300);

    if( inst == NULL )
    {
            std::cout << "Erreur lors de la connexion au flux." <<std::endl;
            return 1;
    }

    std::cout << "Maintenant connecté." << std::endl;

    FILE * pFile;
    pFile = fopen ("file.wmv","w");
    if (pFile==NULL)
    {
            std::cout << "Erreur lors de la création du nouveau fichier." <<std::endl;
            return 1;
    }


    int remaining =  mmsx_get_length(inst) - mmsx_get_current_pos(inst);
    int remaining_old = mmsx_get_length(inst);
    int ret;
    char * data = new char[1024];

    while(bar_value<100)
    {
            ret = mmsx_read(0, inst, data, 1024);

            if(ret < 0)
            {
                    std::cout << "Le fichier a été trouvé mais ne peut être lu." << std::endl;
                    return 1;
            }


            fwrite (data , 1 , 1024 , pFile );

            bar_value = ((float)(mmsx_get_length(inst) - remaining)/mmsx_get_length(inst)*100);
            int total = mmsx_get_length(inst);
            int parcouru = mmsx_get_current_pos(inst);

            // créer un flux de sortie
                std::ostringstream oss1;
                std::ostringstream oss2;
             // écrire un nombre dans le flux
                oss1 << total/(1024*1024);
                oss2 << parcouru/(1024*1024);
             // récupérer une chaîne de caractères
                std::string total2 = oss1.str();
                std::string parcouru2 = oss2.str();

            remaining_old = remaining;
            remaining = mmsx_get_length(inst) - mmsx_get_current_pos(inst);

    }

    delete [] data;

    mmsx_close (inst);
    std::cout << "Connexion terminée." << std::endl;
    fclose (pFile);

    return 0;
}



Vous aurez peut être besoin de la bibliothèque libmms disponible à cette adresse : libmms
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
18 sept. 2011 à 18:47
Je t'ai fait quelques optimisations (comme ne pas appeler deux fois la même fonction), quelques corrections (comme ne pas allouer de la mémoire quand on peut déclarer un tableau directement en statique), et je t'ai fait du C++ propre, sans C.
A noter qu'il n'y a plus de close, puisque celui-ci est automatique avec les fstream.

Je n'ai pas trouvé d'erreur dans ton code. Ni de mémoire, ni de synchronisation. Il doit donc y avoir un souci au niveau de l'interface.
Je n'ai pas non plus testé libxmms sous Windows.

#include <libmms/mmsx.h>
#include 
#include <sstream>
#include <fstream>

int main()
{
  mmsx_t* inst = mmsx_connect(NULL, NULL, "mms://a988.v101995.c10199.e.vm.akamaistream.net/7/988/10199/3f97c7e6/ftvigrp.download.akamai.com/10199/cappuccino/production/publication/Autre/Autre/2011/S37/J5/313308_desparolesetdesactes_20110915.wmv", 8 * 1024 * 300);

  if (inst == 0)
  {
    std::cerr << "Erreur lors de la connexion au flux." << std::endl;
    return 1;
  }

  std::cout << "Maintenant connecté." << std::endl;

  std::ofstream file("file.wmv");
  if (!file)
  {
    std::cerr << "Erreur lors de la création du nouveau fichier." << std::endl;
    return 1;
  }

  int remaining_old = mmsx_get_length(inst);
  int remaining =  remaining_old - mmsx_get_current_pos(inst);
  float bar_value = 0;
  char data[1024];

  while (bar_value < 1)
  {
    if (mmsx_read(0, inst, data, 1024) < 0)
    {
      std::cerr << "Le fichier a été trouvé mais ne peut être lu." << std::endl;
      return 1;
    }

    file.write(data, 1024);
    float length = mmsx_get_length(inst);

    bar_value = (length - remaining / length) * 100;
    remaining_old = remaining;
    remaining = length - mmsx_get_current_pos(inst);

    std::cout << bar_value << std::endl;
  }

  mmsx_close(inst);
  std::cout << "Connexion terminée." << std::endl;

  return 0;
}


________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
18 sept. 2011 à 18:50
A noter: Le bar_value dans le while n'est pas à la bonne valeur. Je l'ai descendu pour tester plus vite.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
macburn Messages postés 7 Date d'inscription jeudi 15 septembre 2011 Statut Membre Dernière intervention 18 septembre 2011
18 sept. 2011 à 19:33
Merci, j'ai fait les modifs et c'est effectivement plus propre.

Mais le problème demeure... Je vais investiguer sur le code en QT, mais je commence à me demander si c'est à ma portée :s Je vais essayer de changer les bibliothèques GNU par celles du projet GnuWin32 qui sont probablement à jour.

Je croise les doigts... c'est ma dernière cartouche.
0