Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Борьба с Bad Pointer

408
17 августа 2008 года
Lei fang
265 / / 01.10.2005
Здравстуйте, столкнулся с такой проблемой - приложение вылетает с ошибкой и предложением отправить отчет, не отправить и дебагить

 
Код:
set<LogonCommServerSocket*>::iterator itr, it2;
    LogonCommServerSocket * s;
    for(itr = m_serverSockets.begin(); itr != m_serverSockets.end();)
    {
        s = *itr;
        it2 = itr;
        ++itr;

        if(s->last_ping < t && ((t - s->last_ping) > 240))
        {

В m_serverSockets есть один указатель видимо уже на удаленный сокет, но оставленный в наборе...
Краш происходит здесь: s->last_ping

Соответсвенно вопрос, можно ли как-то узнать что s "плохой указатель" и как узнать (с примером пожалуйста)?
я смотрел функции IsBadCodePtr и IsBadReadPtr, но что-то не понял как ими пользоваться.
в случае если пробовать IsBadCodePtr(s), то пишет что неможет сконвертировать s к нужному типу, а в случае с readprt, мне не известен размер блока памяти в байтах, ну и наверное тоже будут проблемы с конвертацией.
39K
17 августа 2008 года
kupec
17 / / 11.08.2008
вообще-то в таких случаях используют всякие try..catch
3
17 августа 2008 года
Green
4.8K / / 20.01.2000
Цитата: Lei fang

Соответсвенно вопрос, можно ли как-то узнать что s "плохой указатель" и как узнать (с примером пожалуйста)?


Нет, узнать в общем случае нельзя.

Цитата: Lei fang

я смотрел функции IsBadCodePtr и IsBadReadPtr, но что-то не понял как ими пользоваться.


Это не поможет.

Объект у тебя же не сам исчезает, он же где-то удаляется в коде. Вот и исключай там из контейнера указатель на уничтоженный объект или обнуляй его.

Цитата: kupec
вообще-то в таких случаях используют всякие try..catch


Вообще-то, это здесь как пятая нога...

408
18 августа 2008 года
Lei fang
265 / / 01.10.2005
Ага, я уже разобрался с этими IsBad___Ptr, не помогает :)

А на счет того что обект сам не исчезает, это еще не факт :lol:
Я просмотрел весь код и везде все нормально. Ну и еще, ведь этот краш не постоянный, он может быть в любое время, через 2 минуты или 6 часов работы приложения. Думаю что баг связан с реальным временем. типа где-то обьект уже уничтожается а указатель удаляется после этого (Собственно проверял, везде сперва указатель удаляется %) но какое-то обяснение должно быть), в другом потоке пробегаются по m_serverSockets и о чудо, указатель есть, а объекта нет :)

трай кетч кстати тоже не помогает, все равно вылетает ошибка с предложением отправить отчет мс.

В общем я так понял, что если ничего не поможет, то надо сушить ласты
3
18 августа 2008 года
Green
4.8K / / 20.01.2000
Цитата: Lei fang

А на счет того что обект сам не исчезает, это еще не факт :lol:


Т.е. ты склонен к мысли о мистическом происхождении этого явления... :)

Цитата: Lei fang

Я просмотрел весь код и везде все нормально. Ну и еще, ведь этот краш не постоянный, он может быть в любое время, через 2 минуты или 6 часов работы приложения. Думаю что баг связан с реальным временем. типа где-то обьект уже уничтожается а указатель удаляется после этого (Собственно проверял, везде сперва указатель удаляется %) но какое-то обяснение должно быть), в другом потоке пробегаются по m_serverSockets и о чудо, указатель есть, а объекта нет :)


Объяснение:
1) указатель не удаляется из контейнера;
2) если приложение многопоточное, то не осуществляется синхронизация доступа к контейнеру;
3) ошибка может быть и наведенной.

Цитата: Lei fang

трай кетч кстати тоже не помогает, все равно вылетает ошибка с предложением отправить отчет мс.


Разберись, как правильно работать с try/cath, тогда и диалог появляться не будет.
Но в данной ситуации try/cath тебе не нужен и не поможет.

Цитата: Lei fang

В общем я так понял, что если ничего не поможет, то надо сушить ласты


По твоему изложению понятно, что ты совсем не владеешь кодом. Ты не сам его писал? У тебя нет доступа к исходникам?
Не ласты надо сушить, а разобраться в коде и действовать продуманно, а не научными тыками.

408
18 августа 2008 года
Lei fang
265 / / 01.10.2005
Цитата: Green
Т.е. ты склонен к мысли о мистическом происхождении этого явления... :)


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


Цитата: Green
Объяснение:
1) указатель не удаляется из контейнера;
2) если приложение многопоточное, то не осуществляется синхронизация доступа к контейнеру;
3) ошибка может быть и наведенной.


Склоняюсь ко второму варианту, но разобраться во всем этом ужасе не так уж и просто, но судя по попыткам разобраться все нормально


Цитата: Green
Разберись, как правильно работать с try/cath, тогда и диалог появляться не будет.
Но в данной ситуации try/cath тебе не нужен и не поможет.


Я немного переписал функцию в которой краш. Трай кетч вроде нормально расположен и отлавливает все ошибки. Может конечно с ним надо как-то по другому работать, но как научили, так и научили

Код:
void InformationCore::TimeoutSockets()
{
    if(!usepings) return;

    serverSocketLock.Acquire();

    uint32 t = uint32(time(NULL));

    for(set<LogonCommServerSocket*>::iterator itr = m_serverSockets.begin(); itr != m_serverSockets.end(); ++itr)
    {
try
{
        if((*itr)->last_ping < t && ((t - (*itr)->last_ping) > 240))
        {
            sLog.outString("Closing socket due to ping timeout...");
            (*itr)->removed = true;

            for(set<uint32>::iterator i = (*itr)->server_ids.begin(); i != (*itr)->server_ids.end(); ++i)
                UpdateRealmStatus((*i), 2);

            m_serverSockets.erase(itr);
            (*itr)->Disconnect();
        }
} catch(...) {m_serverSockets.erase(itr); break;}
    }

    serverSocketLock.Release();
}


Цитата: Green
По твоему изложению понятно, что ты совсем не владеешь кодом. Ты не сам его писал? У тебя нет доступа к исходникам?
Не ласты надо сушить, а разобраться в коде и действовать продуманно, а не научными тыками.


Код писал не я, его писали много разных людей начиная с 2006 года. Доступ к исходникам есть. В общем что досталось, с тем и разбираюсь потихоньку, как могу. Но это не тектовый редакор чтобы взять и разобраться в нем сразу, тут много страшных и ужасных вещей, назначение которых известно только написавшему их

Не знаю, во что перестает эта беседа, во флуд или оффтоп, но дабы не получить тут предупреждений, я лучше пойду дальше разбираться вов сем этом ужасе, а то даже за благодарности грозились дать предупреждение

341
18 августа 2008 года
Der Meister
874 / / 21.12.2007
Блин, да мне кажется, что за ошибками и ходить далеко не надо... А итератор там во время/после удаления переприсваивать не надо?
 
Код:
m_serverSockets.erase(itr);
(*itr)->Disconnect();
// в начало цикла и ++itr
Неужели прокатывает?!
В изначальном же варианте у тебя вообще объявлено два итератора одного типа, которые, по-видимому, изменяются в циклах разной степени вложенности...
408
18 августа 2008 года
Lei fang
265 / / 01.10.2005
Это первоначальная версия функции. Я ее переписал для того чтобы упростить, ибо предназначение it2 и s мне не понятно, если можно обойтись одним itr (на мой взгляд, я не эксперт).
Код:
void InformationCore::TimeoutSockets()
{
    if(!usepings)
        return;

    /* burlex: this is vulnerable to race conditions, adding a mutex to it. */
    serverSocketLock.Acquire();

    uint32 t = uint32(time(NULL));
    // check the ping time
    set<LogonCommServerSocket*>::iterator itr, it2;
    LogonCommServerSocket * s;
    for(itr = m_serverSockets.begin(); itr != m_serverSockets.end();)
    {
        s = *itr;
        it2 = itr;
        ++itr;

        if(s->last_ping < t && ((t - s->last_ping) > 240))
        {
            // ping timeout
            printf("Closing socket due to ping timeout.\n");
            s->removed = true;
            set<uint32>::iterator itr = s->server_ids.begin();
            for(; itr != s->server_ids.end(); ++itr)
                UpdateRealmStatus((*itr), 2);
            m_serverSockets.erase(it2);

            s->Disconnect();
        }
    }

    serverSocketLock.Release();
}


Но факт есть фактом, в m_serverSockets каким-то чудом остается указатель на удаленный сокет что в старой версии, что в новой. Причем как он там появляется не ясно. Во всех местах где дисконнектятся сокеты все нормально. И приложение ведь работает нормально некоторое время

"А итератор там во время/после удаления переприсваивать не надо?"
Нет, достаточно просто извлеч его из набора

"Неужели прокатывает?!"
Хм... что-то мне сомнительным это показалось, но работает все точно так же без новых проблем и крашится что с кетчем, что без него, с аптаймом от 2 минут до 8 часов

---- Updated after some time
Что-то совсем меня засмущало это, попробую лучше так:
 
Код:
LogonCommServerSocket * s = *itr;
m_serverSockets.erase(itr);
s->Disconnect();

Но врятли это что-то исправит, если старый вариант функции подвержен крашу
341
19 августа 2008 года
Der Meister
874 / / 21.12.2007
Цитата:
Хм... что-то мне сомнительным это показалось, но работает все точно так же без новых проблем и крашится что с кетчем, что без него, с аптаймом от 2 минут до 8 часов

По крайней мере, у меня твой вариант точно не работает и даже вид не делает. Всегда думал, что надо так:

 
Код:
set<type> s;
// ...
set<type>::iterator i = ++s.begin();
i = s.erase(i);
правда в цикле инкремент после итерации нужно делать условным
288
19 августа 2008 года
nikitozz
1.2K / / 09.03.2007
Цитата: Lei fang
Я немного переписал функцию в которой краш. Трай кетч вроде нормально расположен и отлавливает все ошибки.



Я бы на вашем месте вообще не стал использовать try/catch. Да, в большинстве случаев обращение по указателю к уже удаленному объекту вызовет access violation. Но не всегда, а если по этому адресу уже успел разместиться новый объект того же класса, тогда исключение выкидываться не будет (по крайней мере в этом месте), но логика программы уже скорее всего будет нарушена. А локализавать такую ошибку будет намного сложнее.

408
19 августа 2008 года
Lei fang
265 / / 01.10.2005
nikitozz, я уже не использую, действительно толку нет... Мне просто любой ценой надо остановить зависание приложения, так как его процесс с помощью рестартера даже нельзя убить, если он с этим окошком вылетит. Он так и провисит до тех пор пока кто-то не нажмет кнопку "не отправлять". Поэтому и пытался использовать всякие IsBad___Ptr и try/catch.

Сейчас перенес удаление из контейнера в саму функцию Disconnect(), может так хоть проблема решится с исчезновением объекта.

Der Meister, Хм... Во всем коде это делается так:
 
Код:
set<Player*>::iterator itr = m_players.begin();
        for(; itr != m_players.end(); itr++)
        {
            Player * plr = (Player *)(*itr);
...

 
Код:
/* push gates into world */
    for(set<GameObject*>::iterator itr = m_gates.begin(); itr != m_gates.end(); ++itr)
        (*itr)->PushToWorld(m_mapMgr);

 
Код:
for(list<uint32>::iterator itr = m_queuedGroups.begin(); itr != m_queuedGroups.end(); )
        {
            if((*itr) == grp->GetID())
                itr = m_queuedGroups.erase(itr);
            else
                ++itr;
        }

И как-то работает, так что лучше я не буду менять все это на то, чего не понимаю

itr = m_queuedGroups.erase(itr);
Это похоже н ато что в твоем примере, но ++ перед begin() нигде нет.
Я конечно не исключаю что я вообще ничего не понял что ты имел ввиду :)
255
19 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Скажите пожалуйста, а что проанализировать файл дампа не судьба? Насколько мне известно, то последние версии студии очень хорошо умеют с этим справлятся. Достаточно только скомпилировать программу со всей нужной отладочной информацией, добиться краша, взять файл дампа, который автоматически создается где-то в temp\ (у меня на висте это в AppData\Local\) и скормить студии. Она сразу показывает строку в которой произошел краш и содержимое стека.

По-крайней мере, если ошибка действительно в плохом указателе, почему бы не переписать управление подключениями на один объект-фабрику? А если нет, то дебаг поможет + если совсем плохо - логи и профилирование кода.
408
19 августа 2008 года
Lei fang
265 / / 01.10.2005
Проблема в том что это не совсем краш. Приложение как бы и продолжает работу (другие потоки) и висит с ошибкой одновременно. Соответсвенно никаких краш дампов не создается.

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

С учетом того, что не я писал все это, а код достаточно большой и ужасный, то переписать здесь что-то сложно мне. Пока я ищу легкие пути решения проблемы. Мне бы сделать что угодно, только чтоб приложение так и не оставалось в таком состоянии. Либо чтоб завершало работу, либо чтоб продолжало работать удалив плохой казатель, либо еще что-то. Лучше конечно исправить эту проблему :)

Сейчас добавил извлечение из контейрера прямо в Disconnect() (не стандартный конечно), который занимается многими вещами и уничтожением обекта в том числе. Впринципи проблема должна решиться, но чудеса всегда бывают
341
19 августа 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Lei fang]Это похоже н ато что в твоем примере, но ++ перед begin() нигде нет.[/QUOTE]Это просто инициализация для примера, что типа не первый элемент удаляем, хотя разницы нет. Ну вот оно
 
Код:
for(list<uint32>::iterator itr = m_queuedGroups.begin(); itr != m_queuedGroups.end(); )
{
    if((*itr) == grp->GetID())
        itr = m_queuedGroups.erase(itr);
    else
        ++itr;
}
[QUOTE=Lei fang]...Лучше конечно исправить эту проблему :)[/QUOTE]Тогда[QUOTE=Dart Bobr]По-крайней мере, если ошибка действительно в плохом указателе, почему бы не переписать управление подключениями на один объект-фабрику?[/QUOTE]Ты уже начинаешь к этому подходить
Цитата:
Сейчас добавил извлечение из контейрера прямо в Disconnect() (не стандартный конечно), который занимается многими вещами и уничтожением обекта в том числе. Впринципи проблема должна решиться, но чудеса всегда бывают

То есть, ты пытаешься сделать единый механизм уничтожения подключений. Только не сам объект должен себя "увольнять", а его "начальник", и не абы как (уйди, противный), а одним регламентированным и универсальным способом. Тогда и трудовые никто забывать не будет :) С "приёмом на работу" всё должно быть аналогично. Пока этого нет, результат так и будет оставаться в стиле стройки молдован.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог