0

Context: I have a set of file like that:

    alias
USER = usr_name
PASSWORD = pass
...

    alias2
...

I have a combobox to choose which file to display in a qtableview. The below is the slot called when the value of the combobox changes.

void paramTableModel::switchTable(QString env)
{
    // is there an environment defined
    // --------------------------------------
    if(env.compare("") != 0)
    {
        // does the param file exist ?
        // ---------------------------
        QString file_path = "/path/to/" + env + "/path/to/file;
        QFile param_file(file_path);
        if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
        {

            QString program_decrypt = "/path/to/decrypt";
            QStringList args;
            args << file_path;

            QProcess* process = new QProcess();
            process->setReadChannel(QProcess::StandardOutput);
            process->start(program_decrypt,args);
            process->waitForFinished(-1);

            QTextStream in(&process->readAll());

            //start resetting model (notify the view )
            beginResetModel();
            this->table.clear();
            std::set<QString> uniq;

            // parse file to model
            // -------------------
            while( !in.atEnd() )
            {
                QString line = in.readLine();

                // some new UT ?
                // -------------------
                if( !line.isNull() && line.trimmed().compare("") != 0 && !line.contains("="))
                {
                    line = line.trimmed();
                    std::vector<QString> row;
                    if(uniq.find(line) == uniq.end())
                    {
                        uniq.insert(line);
                        row.push_back(line);

                        QString user_line  = in.readLine().trimmed();
                        QString password_line  = in.readLine().trimmed();
                        QString server_line  = in.readLine().trimmed();
                        if( user_line.startsWith("USER") )
                        {
                            user_line = user_line.split('=').at(1).trimmed();
                        }
                        if( password_line.startsWith("PASSWORD") )
                        {
                            password_line = password_line.split('=').at(1).trimmed();
                        }
                        if( server_line.startsWith("SERVER") )
                        {
                            server_line = server_line.split('=').at(1).trimmed();
                        }
                        row.push_back(user_line);
                        row.push_back(password_line);
                        row.push_back(server_line);
                        this->table.push_back(row);
                    }
                    else
                    {
                            QMessageBox msgBox;
                            msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
                            msgBox.exec();
                    }
                }
            }
            param_file.close();
            endResetModel();
            delete process;
        }
    }
}

Most of the time, my view is properly refreshed. And from time to time, this line segfaults:

QString line = in.readLine();

I have found no pattern to reproduce the bug in a systematic way. I can click back and forth between two files in the combobox, it will work ten, twelve, twenty times and then segfault.

EDIT:

This does work.

void paramTableModel::switchTable(QString env)
{
    // is there an environment defined
    // --------------------------------------
    if(env.isEmpty())
        return;

    // does the param file exist ?
    // --------------------------------
    QString file_path = "/path/to/env/" + env + "/path/to/file.dat";
    QFile param_file(file_path);
    if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
    {

        // Call QProcess, fx_decrypt, read standard output
        QString program_decrypt = "/path/to/decrypt";
        QStringList args;
        args << file_path;

        QProcess process;
        process.setReadChannel(QProcess::StandardOutput);
        process.start(program_decrypt,args);
        process.waitForFinished(-1);

        //QTextStream in(&process->readAll());
        QString out = process.readAll();
        QStringList list_out = out.split("\n",QString::SkipEmptyParts);

        //start resetting model (notify the view )
        beginResetModel();
        this->table.clear();
        std::set<QString> uniq;

        // parse file to model
        // -----------------------
        //while( !in.atEnd() )
        int counter = 0;
        while(counter < list_out.size())
        {
            //QString line = in.readLine();
            QString line = list_out.at(counter);

            // some new UT ?
            // -------------------
            if( !line.isNull() && !line.trimmed().isEmpty() != 0 && !line.contains("="))
            {
                line = line.trimmed();
                std::vector<QString> row;
                if(uniq.find(line) == uniq.end())
                {
                    uniq.insert(line);
                    row.push_back(line);

                    /**QString user_line  = in.readLine().trimmed();
                        QString password_line  = in.readLine().trimmed();
                        QString server_line  = in.readLine().trimmed();*/

                    QString user_line  = list_out.at(++counter).trimmed();
                    QString password_line  = list_out.at(++counter).trimmed();
                    QString server_line  = list_out.at(++counter).trimmed();

                    if( user_line.startsWith("USER") )
                    {
                        user_line = user_line.split('=').at(1).trimmed();
                    }
                    if( password_line.startsWith("PASSWORD") )
                    {
                        password_line = password_line.split('=').at(1).trimmed();
                    }
                    if( server_line.startsWith("SERVER") )
                    {
                        server_line = server_line.split('=').at(1).trimmed();
                    }
                    row.push_back(user_line);
                    row.push_back(password_line);
                    row.push_back(server_line);
                    this->table.push_back(row);
                }
                else
                {
                    QMessageBox msgBox;
                    msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
                    msgBox.exec();
                }
            }
            ++counter;
        }
        endResetModel();
    }
}

Trying to get the why of the bug, following @Kuba Ober suggestion

I've tried to copy the first version into a main function looping indefinitely over all my environments:

But it does work fine!

#include <QtCore/QCoreApplication>
#include <QProcess>
#include <QTextStream>
#include <set>
#include <vector>
#include <QDebug>
#include <QFile>
#include <deque>
#include <QDir>
#include <QStringList>

std::deque< std::vector < QString > > table;

void f( QString file_path )
{
    // does the param file exist ?
    // --------------------------------
    QFile param_file(file_path);
    if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
    {

        // Call QProcess, fx_decrypt, read standard output
        QString program_decrypt = "/path/to/decrypt";
        QStringList args;
        args << file_path;

        QProcess* process = new QProcess();
        process->setReadChannel(QProcess::StandardOutput);
        process->start(program_decrypt,args);
        process->waitForFinished(-1);

        QTextStream in(&process->readAll());

        //start resetting model (notify the view )
        table.clear();
        std::set<QString> uniq;

        // parse file to model
        // -----------------------
        while( !in.atEnd() )
        {
            QString line = in.readLine();

            // some new UT ?
            // -------------------
            if( !line.isNull() && line.trimmed().isEmpty() != 0 && !line.contains("="))
            {
                line = line.trimmed();
                std::vector<QString> row;
                if(uniq.find(line) == uniq.end())
                {
                    uniq.insert(line);
                    row.push_back(line);

                    QString user_line  = in.readLine().trimmed();
                    QString password_line  = in.readLine().trimmed();
                    QString server_line  = in.readLine().trimmed();
                    if( user_line.startsWith("USER") )
                    {
                        user_line = user_line.split('=').at(1).trimmed();
                    }
                    if( password_line.startsWith("PASSWORD") )
                    {
                        password_line = password_line.split('=').at(1).trimmed();
                    }
                    if( server_line.startsWith("SERVER") )
                    {
                        server_line = server_line.split('=').at(1).trimmed();
                    }
                    row.push_back(user_line);
                    row.push_back(password_line);
                    row.push_back(server_line);
                    table.push_back(row);
                }
                else
                {
                    qDebug() << QString("%1 is already defined, ingnoring").arg(line);
                }
            }
        }
        param_file.close();
        delete process;
    }
}

QStringList getEnvList()
{
    QDir dir("/path/to/env/");
    QStringList list_env;
    foreach(QString folder, dir.entryList(QStringList(),QDir::AllDirs | QDir::NoDotAndDotDot))
    {
        QFile app_file( dir.absolutePath()  + '/' +  folder + '/' + "file.dat" );
        if (app_file.exists())
        {
            list_env.push_back(folder);
        }
    }
    return list_env;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int counter = 0;

    while(true)
    {
        foreach ( QString s, getEnvList() )
        {
            qDebug() << counter++;
            qDebug() << s;
            f(s);
        }
    }

    return a.exec();
}

I have no idea, why the two below codes never crash while the first one does...

user2346536
  • 1,464
  • 2
  • 21
  • 43
  • 2
    I would replace the first line of the implementation with `if (env.isEmpty()) return;`. You'll similarly avoid the rest of the indentation hell by returning as soon as a condition doesn't hold, and relying on RAII. The `QProcess` can be on the stack, you don't need to put it on the heap, and in any case you should use `QScopedPointer` or `std::unqiue_ptr`. – Kuba hasn't forgotten Monica Jul 02 '14 at 11:46
  • 2
    Just drop that `.compare("") != 0` idiom, it's silly. Use `isEmpty()`. `QString` has decent documentation :) – Kuba hasn't forgotten Monica Jul 02 '14 at 11:47
  • 2
    Further leveraging RAII, note that explicitly closing the file is unnecessary. – Kuba hasn't forgotten Monica Jul 02 '14 at 11:48
  • @KubaOber right for the isEmpty. I'll do so. My QProcess was on the stack, but the warning "taking the address of a temporary" ennoyed me... Though I agree as well. – user2346536 Jul 02 '14 at 11:48
  • 1
    "it will work ten, twelve, twenty times and then segfault" I'll believe it when you put it into a separate example, into a loop, and show that it, you know, in fact does it if you loop it enough times. – Kuba hasn't forgotten Monica Jul 02 '14 at 11:49
  • @KubaOber , indeed, I can't "isolate" the bug (see edit). I've found a workarround which does not crash. I really do not get why it does not work the first time. ( I only modified the given function ) – user2346536 Jul 02 '14 at 12:53

0 Answers0