Russian Qt Forum

Qt => Общие вопросы => Тема начата: Nalsur1982 от Ноябрь 08, 2007, 09:58



Название: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 09:58
Начал заниматься Qt, написал програмку, все перепроверил. Пробовал и так и сяк, но все равно куда-то утекает память. Помогите пожалуйста найти баг?
-------------------------------------------------------------------------------------------------
Код:
main.cpp

#include <QApplication>
#include <QDebug>
//
#include "tcpserver.h"
//
int main(int argc, char **argv)
{
QApplication app(argc, argv);
TcpServer server;

return app.exec();
}
--------------------------------------------------------------------------------------------------------
Код:
tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H
//
#include <QTcpServer>
#include <QTimer>
//
#include "serverconnection.h"
//
class TcpServer : public QObject
{
Q_OBJECT
public:
TcpServer(QObject *parent = 0);
~TcpServer();

private slots:
void incomingConnection();

protected:
QTcpServer *tcpServer;
ServerConnection *conn;
};
#endif
----------------------------------------------------------------------------------------
Код:
tcpserver.cpp

#include "tcpserver.h"
//
TcpServer::TcpServer( QObject *parent )
: QObject(parent)
{
qDebug() << "TcpServer::TcpServer()";
setObjectName("TcpServer");

tcpServer = new QTcpServer(this);
tcpServer->setObjectName("QTcpServer");
connect(tcpServer, SIGNAL(newConnection()), SLOT(incomingConnection()));
tcpServer->listen(QHostAddress::Any, (quint16)5406);
}

TcpServer::~TcpServer()
{
qDebug() << "TcpServer::~TcpServer()";
delete tcpServer;
}

void TcpServer::incomingConnection()
{
qDebug() << "TcpServer::incomingConnection()";
conn = new ServerConnection(tcpServer->nextPendingConnection(), this->tcpServer);
connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
dumpObjectTree();
}
--------------------------------------------------------------------------------------------------------
Код:
serverconnection.h

#ifndef SERVERCONNECTION_H
#define SERVERCONNECTION_H
//
#include <QThread>
#include <QtNetwork>
//
class ServerConnection : public QThread
{
Q_OBJECT
public:
ServerConnection(QTcpSocket *socket, QObject *parent = 0);
~ServerConnection();
void run();

signals:
void receiveDataFromClient(QHostAddress host);

private slots:
void receiveData();

private:
QTcpSocket *m_pTcpSocket;
quint8 m_startByte;
quint8 m_blockSize;

};
#endif
--------------------------------------------------------------------------------------------------
Код:
serverconnection.cpp

//
#include "serverconnection.h"
//
ServerConnection::ServerConnection( QTcpSocket *socket, QObject *parent)
: QThread(parent), m_pTcpSocket(socket)
{
qDebug() << "ServerConnection::ServerConnection()";
setObjectName(QString("ServerConnection%1").arg(socket->peerPort()));

m_pTcpSocket->setParent(this);
m_pTcpSocket->setObjectName(QString("Socket%1").arg(socket->peerPort()));
connect(m_pTcpSocket, SIGNAL(disconnected()), SLOT(quit()));
connect(m_pTcpSocket, SIGNAL(readyRead()),SLOT(receiveData()));
dumpObjectTree();
start();
}

ServerConnection::~ServerConnection()
{
qDebug() << "ServerConnection::~ServerConnection()";
delete m_pTcpSocket;
}

void ServerConnection::run()
{
qDebug() << "ServerConnection::run()";
exec();
}

void ServerConnection::receiveData()
{
// действия
}



Название: Re: Помогите найти leaks?
Отправлено: Kainit от Ноябрь 08, 2007, 10:02
conn = new ServerConnection(tcpServer->nextPendingConnection(), this->tcpServer);

1. я не нашёл где делается delete (возможно потому что не очень качественно глядел)
особенно при фразе connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
2. "this->" можно было бы и опустить для красоты
3. Раз вы обнаружили л лик, неужели та софтина что его находит не говорит на какой new он произошёл? Может стоит перейти на BoundChecker?


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 10:19
Я пок еще чайник в этом деле. Лик обнаружил с помощью менеджера задач, создавая с помщью програмки множественные соединения и разрывая их. По 1 метру на 100 подключений примерно.
 А разве
Цитировать
connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
не удаляет conn. Хотя я и явно пробовал, деструктор вызывается точно.


Название: Re: Помогите найти leaks?
Отправлено: Mike от Ноябрь 08, 2007, 10:21
Для ловли утечек памяти (для MS Visual Studio) очень хорошая весчь Visual Leak Detector (http://www.codeproject.com/tools/visualleakdetector.asp (http://www.codeproject.com/tools/visualleakdetector.asp))/
Он не такой тяжеловесный как Bounds Checker, да и возможностей по-меньше, но он бесплатный и очень маленький.
Для его использования просто в любой свой заголовочный файл указываешь #include "vld.h", который расположен в его папке include и запускаешь приложение. И в окне Output пишется, где произошло выделение памяти, которая потом не была освобождена. Так же имеется возможность при щелчке по этой строке переходить к данному месту кода.
Вобщем удобная вещь.
Попробуй, может поможет.


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 10:26
А в QDeveloper с MinGW ее можно использовать?


Название: Re: Помогите найти leaks?
Отправлено: Mike от Ноябрь 08, 2007, 10:28
Сам не пробовал, поэтому не знаю. Посмотри в его документации. Попробуй, может получиться. :)


Название: Re: Помогите найти leaks?
Отправлено: ритт от Ноябрь 08, 2007, 11:15
нельзя


Название: Re: Помогите найти leaks?
Отправлено: span от Ноябрь 08, 2007, 11:52
Для ловли утечек памяти (для MS Visual Studio) очень хорошая весчь Visual Leak Detector (http://www.codeproject.com/tools/visualleakdetector.asp (http://www.codeproject.com/tools/visualleakdetector.asp))/
Он не такой тяжеловесный как Bounds Checker, да и возможностей по-меньше, но он бесплатный и очень маленький.
Для его использования просто в любой свой заголовочный файл указываешь #include "vld.h", который расположен в его папке include и запускаешь приложение. И в окне Output пишется, где произошло выделение памяти, которая потом не была освобождена. Так же имеется возможность при щелчке по этой строке переходить к данному месту кода.
Вобщем удобная вещь.
Попробуй, может поможет.

Давно хотел начать использовать профайлер, или хоть что-то для отлова утечек. Стараюсь, конечно, сразу писать правильный код. Но случается всякое.
Поставил я себе этот Visual Leak Detector. Запустил отладку программы, дождался первого диалога (самописный, в нем я у пользователя акк к базе спрашиваю). Закрыл диалог, программа ругнулась и завершилась.

Вот сижу теперь, пишу сюда. А мне в аутпуте до сих пор сыпятся  собщения, дампы памяти и.т.д.)) Уже 30 000 строк. И как тут понять, где твоя утечка? Большинство сообщений, естественно, указывают на qt\src\... У меня QT собрана в Debug версии.
Пытался использовать встроенный АПИ (VLDDisable / VLDEnable). Не помогает...



Название: Re: Помогите найти leaks?
Отправлено: Kainit от Ноябрь 08, 2007, 12:36
Народ что-то чудовищно отдалился от темы. Вроде вопрос был про лик, а не про способы его детектирования.

Цитировать
Я пок еще чайник в этом деле. Лик обнаружил с помощью менеджера задач, создавая с помщью програмки множественные соединения и разрывая их. По 1 метру на 100 подключений примерно.
 А разве
Quote
connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
не удаляет conn. Хотя я и явно пробовал, деструктор вызывается точно.
Я сам чайник, я просто хотел сказать что у тебя finished() непонятно когда вызывается... Такое ощущение что у тебя треды не завершаются (или просто в приведённом фрагменте этого не было)... И в коде треда увидел только exec() из которого ты нигде не выходишь что странно...
Цитировать
You can start the event loop by calling exec(); you can stop it by calling exit() or quit()


Название: Re: Помогите найти leaks?
Отправлено: Icoz от Ноябрь 08, 2007, 12:48
меня смущает строка
Код:
connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
Посмотрел доку, там:
Код:
Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() was called.
Короче, тебе надо, чтобы закончился разбор сообщений. Только не врубился в каком потоке: твой qtread-потомок или поток основной...
Еще там есть приписка:
Код:
See also destroyed() and QPointer.
 
Посмотри сигнал destroyed() (он вызывается, когда объект уже присмерти...) - убедись, что проходит удаление объекта.


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 13:11
Выхожу из треда когда обрывается соединение
Цитировать
connect(m_pTcpSocket, SIGNAL(disconnected()), SLOT(quit()));
, а finished() вызывается как раз когда тред завершается. Причем все сообщения из конструкторов и деструкторов приходя как надо.
Я пробовал и без DeleteLater(). Делал слот и удалял указатель сендера.


Название: Re: Помогите найти leaks?
Отправлено: Kainit от Ноябрь 08, 2007, 13:43
Цитировать
connect(m_pTcpSocket, SIGNAL(disconnected()), SLOT(quit()));
И правда... Я попой видимо смотрел.

Собственно,значит всё правильно.
наверное dumpObjectTree(); написано не случайно и наверное вы внимательно глядели на него. Так что именно не удаляется? По логике вещей должно всё быть в порядке...
Можно предположить что по каким-то неведомым причинам сокету может не прийти disconnected(), может каким-то сокетам не вызывается close ()...

Наверное тогда лик живёт в receiveData() в той части что обозначена "// действия"


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 08, 2007, 13:52
1) Такой вопрос, зачем нужен ServerConnection (неосновные потоки), если он реально ничего не делает? Толку от него 0!
2) Явно удалять объекты порожденные от QObject у которых есть парент ненужно (delete tcpServer; , etc - это лишнее)
3) Замечание: ServerConnection::ServerConnection( QTcpSocket *socket, QObject *parent). Передавайте дескриптор вместо указателя на сокет. Сам сокет создавайте в run() при этом не указывая парента.

Цитировать
The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object (this) as the parent of an object created in the thread (since the QThread object itself was created in another thread).


4) Зачем это наследование class TcpServer : public QObject. Можно сразу наследоваться от QTcpServer


Советую для начала пересмотреть примеры по работу с потоками и сетью. Почитать соответствующме развелы в ассистанте.

ЗЫ: Кстате вот неплохой пример, examples\network\threadedfortuneserver\. Советую с ним разобраться.


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 14:19
Проверял программу именно с пустой процедурой receiveData(), чтобы не было "наводок".
Сначала реализовывал связь с использованием дескрипторов и как в асистенте и как в примере на этом форуме в теме про QTcpSocket и QThread, но утечка была еще больше. В этом варианте ее поменьше.

ServrConnection используеться для приема, отправки и проверки прав доступа удаленного хоста к серверу. Далее информация как QByteArray поступает на вход класса-анализатора протокола (хочу чтобы класс не зависил от физики связи TCP/IP или COM)

Цитировать
Явно удалять объекты порожденные от QObject у которых есть парент ненужно (delete tcpServer; , etc - это лишнее)
явно я их и не удалял, но из-за ликов перестраховался.

Уже мозг дымиться :)


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 08, 2007, 15:30
В том коде что вы привели ServrConnection не выполняет абсолютно никаких полезных действий. Поток просто создаеться и весит. Ничего более. Работа с потоками поставлена неверно.

Экзампл examples\network\threadedfortuneserver выполнят аналогичные действия вашим. Советую переделать ваш код по такому же принципу.

Следующий момент, можно ли превести код метода dumpObjectTree()? Возможно проблема именно в нем. Этот метод вызываеться в нескольких местах, интересно на него взглянуть.

И наконец, попробуйте закоментировать все в методе void TcpServer::incomingConnection() и посмотреть, будел ли течь память илинет.


Название: Re: Помогите найти leaks?
Отправлено: Kainit от Ноябрь 08, 2007, 16:33
pastor,
1. Вы зря, dumpObjectTree() - очень приятный метод QObject-а и код его вы можете посмотреть в исходниках Qt...
2. Если бы Nalsur1982 привёл полный код, мы бы зарылись читая километры ненужного нам материала. Вполне достаточно знания того, что приведённый код порождает лики (пусть он даже и не несёт особого смысла).

Nalsur1982
1. Мне стало интересно. А можно готовый проектик куда-нибудь скинуть, чтобы его погонять можно было? Ну не вижу я здесь мест для ликов.


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 08, 2007, 16:50
pastor,
1. Вы зря, dumpObjectTree() - очень приятный метод QObject-а и код его вы можете посмотреть в исходниках Qt...
2. Если бы Nalsur1982 привёл полный код, мы бы зарылись читая километры ненужного нам материала. Вполне достаточно знания того, что приведённый код порождает лики (пусть он даже и не несёт особого смысла).

2 Kainit

1) Я упустил из внимания, что dumpObjectTree это метод QObject. Я подумал, что это самописный метод
2) Возможно, остальной код и порождает лики, а не тот код, что приведён. Извините, но телепаты в отпуске, чтобы без ТОЧНОГО кода определить лик.

имхо, код проги необходимо пересмотреть, ибо уже в преведённом коде есть проблемные места  и совсем непонятна роль некоторого кода... Если я неправ, прошу меня поправить


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 08, 2007, 17:17
ServerConnection будет выполнять другие действия, просто для облегчения понимания я их не привожу и при отладке тоже не использую. То есть это и есть весь рабочий код. Можно просто вставить в пустой проект.  (Исправлю баг и займусь остальным)

Вот именно, пока просто висит, а память уже куда-то утекает. И не пойму в чем дело.

Могу куда-нибудь выложить или по почте лучше отослать.


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 08, 2007, 18:56
Попробуйте закоментить тело метода void TcpServer::incomingConnection(). Будет ли в таком случае течь память?


Название: Re: Помогите найти leaks?
Отправлено: Kainit от Ноябрь 08, 2007, 21:16
Цитировать
Могу куда-нибудь выложить или по почте лучше отослать.
да на любой файлошарер... на тот же http://www.ifolder.ru/ (http://www.ifolder.ru/)


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 09, 2007, 09:55
Без incomingConnection() утечки конечно не будет. Так как вся утечка происходит при коннекте клиентов.

http://webfile.ru/1587628 (http://webfile.ru/1587628) ссылка на сорцы. + клиентская часть которая коннектится к серверу. (~10к)

http://webfile.ru/1587641 (http://webfile.ru/1587641) сорцы и бинари


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 09, 2007, 11:34
Сразу вопрос. Вот код:

Код:
TcpClient::TcpClient( QObject *parent) 
: QObject(parent)
{
qDebug() << "TcpClient::TcpClient()";
socket = new QTcpSocket();
socket->connectToHost(QHostAddress("127.0.0.1"), (quint16)5406);
}

TcpClient::~TcpClient()
{
qDebug() << "TcpClient::~TcpClient()";
socket->disconnectFromHost();
}

а где же удаление socket? Или укажи парента в конструкторе или удаляй явно в деструкторе. Вот лик уже найден в Client


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 09, 2007, 11:42
Ещё раз хочу сосредоточить ваще внимание на ServerConnection. Вы хотите добиться мультипотоковости? Читать данные в отдельном потоке? Увы у вас этого нет. ServerConnection::receiveData() не будет выполнена в отдельном потоке! Поток "сосредоточен" в методе run() . Даже если код из receiveData() перенести в run() это не будет безопасно, т.к. QTcpSocket являеться reentrant а не
thread-safe. Если будут вопросы, задавайте


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 09, 2007, 12:10
Насчет TcpClient - это просто класс для теста, чтобы ускорить процесс обнаружения ошибки. Я также пробовал с Putty коннектится - та же проблемма.

В методе ServerConnection::run() будет создан экземпляр класса с функциональностью протокола, создан connect(..) сигнала ServerConnection (пока его нет) который будет вызываться receiveData() после принятия всего блока данных из сети. И через QByteArray передаваться в слот класса протокола для обработки.

А может все перемудрил, может можно проще? Идея такова: к серверу коннектятся удаленные хосты и по стандартному протоколу запрашивают данные. Сервер их будет собирать с других машин (возможно по другим протоколам) и хранить данные в общей базе. Короче, конвертор протоколов с возможностью хранения и обраьотки.


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 09, 2007, 14:19
Скомпилил ваши примеры. Проверил LinkManager. Увеличение объёма используемой памяти имхо связано с методом
nextPendingConnection ().

Цитировать
The socket is created as a child of the server, which means that it is automatically deleted when the QTcpServer object is destroyed. It is still a good idea to delete the object explicitly when you are done with it, to avoid wasting memory.


Я переделал на дискрипторы, проблема сразу пропала


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 09, 2007, 14:48
Вот код:

Код:
class TcpServer : public QTcpServer
{
Q_OBJECT
public:
TcpServer(QObject *parent = 0);
~TcpServer();

protected:
void incomingConnection(int socketDescriptor);
};

Код:
TcpServer::TcpServer( QObject *parent ) 
: QTcpServer(parent)
{
qDebug() << "TcpServer::TcpServer()";
listen(QHostAddress::Any, 5406);
}

TcpServer::~TcpServer()
{
qDebug() << "TcpServer::~TcpServer()";
}

void TcpServer::incomingConnection(int socketDescriptor)
{
qDebug() << "TcpServer::incomingConnection()";
ServerConnection *conn = new ServerConnection(socketDescriptor, this);
connect(conn, SIGNAL(finished()), conn, SLOT(deleteLater()));
conn->start();
}

Код:
class ServerConnection : public QThread
{
Q_OBJECT
public:
ServerConnection(int socketDescriptor, QObject *parent = 0);
~ServerConnection();

void run();

private:
int socketDescriptor;
};

Код:
ServerConnection::ServerConnection( int socketDescriptor, QObject *parent) 
: QThread(parent), socketDescriptor(socketDescriptor)
{
qDebug() << "ServerConnection::ServerConnection()";
}

ServerConnection::~ServerConnection()
{
qDebug() << "ServerConnection::~ServerConnection()";
}

void ServerConnection::run()
{
qDebug() << "ServerConnection::run()";
QTcpSocket tcpSocket;
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        return;
    }
            //we're in the new thread! ;)
//here we can read or write data

tcpSocket.disconnectFromHost();
    if (tcpSocket.state() != QAbstractSocket::UnconnectedState )
tcpSocket.waitForDisconnected();
}


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 09, 2007, 15:22
2 pastor

Может я торможу где-то? Откомпилил твой код и запустил - все нормально.
Затем закомментировал  строчку tcpSocket.disconnectFromHost() (чтобы клиент разрывал связь), в итоге за 10 мин утекло 4метра. Может я что-то не понимаю?


Название: Re: Помогите найти leaks?
Отправлено: pastor от Ноябрь 09, 2007, 15:35
Я закоментил тоже эту строку, все нормально:

Код:
void ServerConnection::run()
{
qDebug() << "ServerConnection::run()";
QTcpSocket tcpSocket;
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        return;
    }


    //tcpSocket.disconnectFromHost();
    //if (tcpSocket.state() != QAbstractSocket::UnconnectedState )
    // tcpSocket.waitForDisconnected();
}

Память стоит на месте 6,4 Мб. Qt 4.3.2


Название: Re: Помогите найти leaks?
Отправлено: Nalsur1982 от Ноябрь 09, 2007, 15:47
Сорри я поторопился с выводами!
На 2Мб увеличилась память и больше вроде не поднимается. С 9.3Мб до ~11.3Мб и остановилась вроде. Подожду еще часик.

Спасибо всем кто откликнулся!


Название: Re: Помогите найти leaks?
Отправлено: pl_mix от Июль 17, 2011, 21:09
Кому интересно могу поделиться своим опытом с данной проблемой. Кодил клиент-сервер, при каждом конекте утекало до 6 метров, как оказалось хоть я и слал когда нужно было finished он не убивал нить, поэтому и была утечка. Нашёл эту тему на форуме, натолкна на мысль (за что Вам огромное спасибо) обратить внимание на то  как я при этом убиваю сокет. Покумекал и  решил перед вызовом deleteLater() вызывать слот закрытия конекта:
Код:
void ServerCore::incomingConnection(int socketDescriptor)
   {

     
       UserSession* user = new UserSession; // нить

        user->moveToThread(user);
        connect(user,SIGNAL(finished()),    //важен порядок
              user,SIGNAL(signalClose()));
        connect(user,SIGNAL(finished()),
               user,SLOT(deleteLater()));
в самой нити:
Код:
void UserSession::run()
{
 _client = new nSocket(_nsock,this) //потомок QTcpSocket
......//нужные связки сигналов и слотов для обратотки
connect(this, SIGNAL(signalClose()),_client,SLOT(slotCloseSession()));
connect(_client,SIGNAL(disconnected()),this, SLOT(quit()));
exec();
и на последок слот в сокете:
Код:
void nSocket::slotCloseSession()
{
   if( state() == QTcpSocket::ConnectedState)
           close();

}
и о чудо, всё сухо...утечки нет))
 Конечно можна найти и более гибкое решение... через создание отдельной функции которая предварительно шлёт сигнал для закрытия сокета , а потом чтоб посылала finished() и тд и тп. но просто у меня в коде во всех проверках  и защитах стоит просто сигнал  finished() для закрытия при некоретностях и для переделки надо время, а там на быстрою руку для проверки сойдёт.