Повреждение памяти в glibc с помощью подготовленного оператора libmysqlcppconn - PullRequest
0 голосов
/ 28 марта 2012

В настоящее время я пишу веб-сканер / паук на C ++ для Linux, и у меня возникли некоторые проблемы с обновлением базы данных. Я довольно новичок в C / C ++, просто к вашему сведению.

Обновления базы данных выполняются отдельным потоком (с использованием pthreads), но такая же проблема существует, если выполняется в main (), поэтому я, возможно, наивно, отбросил потоки как причину чего-либо.

Я использую libmysqlcppconn для API базы данных.

Я компилирую с gcc версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) с -O2 -Wall -pedantic, и он компилируется чисто.

Тем не менее, когда ниже вызывается функция commitChangesToDatabase (), она в основном выбирает элементы из std :: map (url_queue), выбрасывает их в std :: vector (updates) и удаляет указанный элемент из исходного std: : map, затем переходит к итерации по std :: vector, выполняя подготовленный MySQL оператор для каждого элемента в векторе. Вот где это терпит неудачу.

Случайно либо:

  • Сбои без вывода ошибок (без segfault, без трассировки стека, без ничего)
  • Сбои с обнаруженным повреждением памяти glibc (см. Вывод здесь: http://pastie.org/private/wlkuorivq5tptlcr7ojg)
  • Сообщает, что сервер MySQL пропал (перехватил исключение), но продолжает попытки (не вылетает)

Я пытался переключить подготовленный оператор на простой executeUpdate (), но безрезультатно. Я попытался исключить этот шаг, выбрав элементы и просто выполняя обновления всякий раз, когда я нахожу элемент для обновления, в первом цикле над url_queue.

Другие функции в этом приложении также используют подготовленные операторы (еще одно ОБНОВЛЕНИЕ), и это прекрасно работает. Эти функции также выполняются отдельными потоками.

Я бы запустил приложение через valgrind, но, честно говоря, я не понимаю большую часть выходных данных, так что это не сильно мне поможет - но если кто-то захочет получить от него выходные данные, дайте мне знать, какие варианты запустить с, и я предоставлю это.

Понятия не имею, как поступить отсюда. Кто-нибудь знает, что не так?

struct queue_item_t {
    int id;
    int sites_id;
    int priority;
    int depth;
    int handler;
    int state;  // 0 = Pending, 1 = Working, 2 = Completed, 3 = Checked
    double time_allowed_crawl;

    bool status;
    bool was_redirected;

    double time;
    double time_end;
    double time_curl;
    double size;

    std::string hash;
    std::string url;
    std::string file;
    std::string host;
};

void commitChangesToDatabase()
{
    map< string, queue_item_t >::iterator it, end;
    sql::PreparedStatement *pstmt;
    int i = 0;

    if (!url_queue.size()) {
        return;
    }

    pthread_mutex_lock(&dbCommitMutex);
    pthread_mutex_lock(&itemMutex);

    cout << "commitChangesToDatabase()" << endl;
    pstmt = dbPrepareStatement("UPDATE crawler_queue SET process_hash = NULL, date_crawled = NOW(), url = ?, hash = ? WHERE id = ?");

    for (it = url_queue.begin(); it != url_queue.end();)
    {
        if (it->second.state == 2)
        {
            pstmt->setString(1, it->second.url);
            pstmt->setString(2, it->second.hash);
            pstmt->setInt(3, it->second.id);

            try {
                pstmt->executeUpdate();
                ++i;

            } catch (sql::SQLException &e) {
                cerr << "# ERR: SQLException in " << __FILE__;
                cerr << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
                cerr << "# ERR: " << e.what();
                cerr << " (MySQL error code: " << e.getErrorCode();
                cerr << ", SQLState: " << e.getSQLState() << " )" << endl;
            }

            url_queue.erase(it++);
        }
        else {
            ++it;
        }
    }

    delete pstmt;

    cout << "~commitChangesToDatabase()" << endl;

    pthread_mutex_unlock(&itemMutex);
    pthread_mutex_unlock(&dbCommitMutex);
}

// this function is defined in another file but is written here just to show the contents of it
sql::PreparedStatement *dbPrepareStatement(const std::string &query)
{
    return con->prepareStatement(query);
}

Edit:

Некоторые, похоже, считают, что проблема в итерации по коллекции url_queue, однако я исключил это, закомментировав все, что работает с базой данных, но не итерацию. Кроме того, итерация здесь представляет собой упрощенную (но работающую) версию оригинала, которая выбирает элементы с карты, добавляет вектор и стирает с карты, как показано ниже, и эта часть программы работает нормально - это only падает при использовании базы данных.

for (it = url_queue.begin(); it != url_queue.end();)
{
    if (it->second.state == 2)
    {
        update_item.type = (!it->second.was_redirected ? 1 : 2);
        update_item.item = it->second;

        updates.push_back(update_item);

        url_queue.erase(it++);
    }
    else {
        ++it;
    }
}

Редактировать 2:

Вывод из valgrind --leak-check=yes: http://pastie.org/private/2ypk0bmawwsqva3ikfazw

Ответы [ 2 ]

0 голосов
/ 28 марта 2012

Я не думаю, что будет хорошей идеей связываться с итераторами.заменить:

else {
        ++it;
    }

на:

else continue;

или просто удалить его.

0 голосов
/ 28 марта 2012

Кажется, итератор увеличивается без необходимости;сначала в теле цикла, а также в для утверждения.В этом коде можно увеличить итератор end, это проблематичная операция и может быть источником проблемы.

Для этого случая больше подходит следующая структура петли:
it = url_queue.begin();
while( it != url_queue.end() ){ //loop body }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...