Перейти к содержимому

Донат
На хостинг
ISK за переводы
до 75kk за 1000зн.
Хроники EVE
Сборник
Новичкам
Полезная информация
Фотография

[Девблог] Спящая красавица - как один из старейших багов EVE был найден и исправлен

Девблог баг

  • Авторизуйтесь для ответа в теме
15 ответов в теме

#1
Garret

Garret

    Clone Grade Alpha

  • Tech II Pilots
  • Pip
  • 34 сообщений
172
  • Client:Eng

*
Одобрено
сообществом!

SleepingBeauty550.jpg
 
Иногда история о баге получается такой интригующей, что ты просто обязан её записать. Если у вас технический склад ума или вам просто любопытно узнать реалии разработки EVE Online  присоединяйтесь. Мы окунёмся в технические глубины исходного кода EVE Online в моих воспоминаниях о том, как один из старейших дефектов в игре был найден и исправлен.
 
Мы всегда работаем над тем, чтобы улучшить EVE Online и наш исходный код. Я надеюсь, что вы получите удовольствие от рассказа о том, что из себя предстваляет эта работа.
 
Как это всё началось
 
Не так давно CCP karkur попросила меня помочь ей в работе над исправлением проблемы с окном управления дронами, когда оно переставало обновляться после того, как дроны были отозваны. Её расследование не выявило никаких багов в коде связанных с дронами или пользовательским интерфейсом. На протяжении нескольких часов она смогла воспроизвести баг с дронами всего три раза  каждый раз добавляя ещё больше логирования там, где, как казалось, находилась проблема. Во время последней попытки стало ясно, что проблема была в той строчке кода, где мы меньше всего ожидали её найти  окно управления дронами начинало обновляться, потом засыпало и никогда не просыпалось. Казалось, что тасклеты просто, в случайный момент, не выходили из сна.
 
Оригинальный баг было довольно сложно воспроизвести, но CCP karkur написала скрипт, который генерировал 500,000 тасклетов, которые должны просто спать определённое время. Потом скрипт искал те из них, которые никогда не заканчивали своё исполнение. Скрипт помог надёжно воспроизводить это расстройство сна. Скрипт указал на тасклет, который не просыпался, и после дальнейшего анализа стало ясно, что этот тасклет не был ни запланирован, ни блокирован  он был в каком-то лимбе.
 
Немного деталей
 
Тасклеты (см. Stackless Python для информации) запускаются внутри метода PyScheduler::Run посредством вызова метода PyStackless_RunWatchdogEx. Этот метод принимает таймаут в качестве аргумента и запускает любые тасклеты из расписания до тех пор, пока переданное время не прошло. Когда тасклет создаётся, он вносится в расписание и будет запущен во время вызова PyScheduler::Run. Когда тасклет спит, он вызывает Synchro::SleepWallclock, реализация которого представляет собой создание канала и выполенения метода receive этого канала. Эта логика блокирует канал до тех пор, пока кто-то не пошлёт какие-либо данные на этот канал. Объект Synchro содержит кучу (heap) слиперов. Слипер  это простой объект, который содержит канал и время до пробуждения тасклета. Метод Synchro::Tick извлекает слипер с вершины кучи и отправляет значение по определённому каналу. Это, в свою очередь, разблокирует тасклет и заносит его в расписание.
 
Первой моей идеей было то, что, возможно, присутствует баг в реализации кучи. Это наш собственный код, он старый и для него нет юнит тестов. Я думал над тем, чтобы написать пару тестов или вообще найти замену существующему коду, когда понял, что симптомы просто не соответствуют ситуации, когда тасклет никогда не извлекается из кучи. Если бы дело было в этом, то тасклет вечно оставался бы заблокированным. Кстати, у меня до сих пор в to-do добавить тесты к этому коду.
 
Моей следующей идеей было посмотреть в документацию PyStackless_RunWatchdogEx. Эта функция может вернуть тасклет, который нельзя ни убить, ни перенести в расписание. Это оказалось тупиковой идеей  такое может случится только при работе в упреждающем режиме (preemptive mode), когда тасклет может быть прерван.
 
Покажи мне код
 
У меня было чутьё, что уничтожение тасклетов может играть роль во всём этом, но я не видел ничего странного в коде связанным с уничтожением. Я не смог убедить себя, что подсчёт ссылок канала был написан корректно, так что я решил немного почистить код. Мне было стрёмно от того, как он был написан.

PyObject* Synchro::SleepWallclock(int ms, const int64_t &due)
{
    Sleeper sl;
    sl.channel = PyChannel_New(NULL);
    if (!sl.channel)
        return 0;
    PyChannel_SetPreference(sl.channel, 0); //просто чтобы работало во время пробуждения
    sl.due = due;

    mWallclockSleepers.Insert(sl);
     
    // Усни и проснись! *(отправитель разблокирует канал)
    PyObject *ret = PyChannel_Receive(sl.channel);

    if (!ret) {
        //нас убили, давайте поищем нас в очереди чтобы высвободить ресурсы
        RemoveSleeper( mWallclockSleepers, sl );
    }
    return ret;
}
 
Функция SleepWallclock создаёт канал, добавляет его в слипер, помещает слипер в кучу и вызывает метод receive канала. Метод receive возвращает результат после того, как Synchro::Tick вызывает метод send для этого канала. Вызов send разблокирует канал и, в последующем, добавляет его в расписание. Также, тасклет может быть уничтожен, в этом случае метод receive возвращает nullptr. Давайте взглянем на фрагмент года из Synchro::Tick:

//собрать слиперы для пробуждения
std::vector sleepers;
while (mWallclockSleepers.size()) {
    if (mWallclockSleepers.front().due > now)
        break;
    sleepers.push_back(mWallclockSleepers.Shift());
}
if (sleepers.size()) {
    Be::Time nnow = BeOS->GetActualTime();
    for(unsigned int i = 0; i < sleepers.size(); i++) {
        Sleeper &s = sleepers[i];
        if (PyChannel_GetBalance(s.channel)) {
            int res = PyChannel_Send(s.channel, Py_None);
            if (res)
                PyOS->PyError();
        }
        Py_DECREF(s.channel);
    }
}
 
Всё это выглядит корректно  канал нельзя вытащить из-под тасклета до тех пор, пока не произошёл вызов метода Send. А что происходит если убить тасклет?
 
void Synchro::RemoveSleeper( Heap &sleepers, Sleeper &sl )
{
    SleeperIt it;
    for( it = sleepers.begin(); it != sleepers.end(); ++it )
    {
        if( it->channel == sl.channel )
        {
            break;
        }
    }

    if( it != sleepers.end() )
    {
        //Ок, нас нашли. Пора нас удалить.
        sleepers.Remove( it );
        Py_DECREF( sl.channel );
    }
}
 
Снова всё выглядит нормально  тасклет убирается из кучи и канал освобождается. Обратите внимание, что если тасклета нет в куче, то это не ошибка  он может проснутся и быть добавлен в расписание и, в тот же самый момент, быть убитым. Но в этом случае канал будет освобождён.
 
Давайте всё же проанализируем это немного дальше. Имейте ввиду, что Py_DECREF освобождает память если количество ссылок равно нулю. Вызов PyChannel_New внутри SleepWallclock возвращает объект типа PyChannelObject. Новых ссылок не добавляется, так что объект канала удаляется в тике, когда вызывается Py_DECREF. Это кажется вполне нормальным ведь после вызова receive в SleepWallclock ссылок на канал больше нет. Не считая...
 
Внутри функции RemoveSleeper, которая вызывается после уничтожения тасклета, мы сравниваем каналы. На самом деле, на этом месте мы сравниваем тасклеты, но т.к. между каналом и тасклетом у нас всегда связь один к одному, и мы всё равно вынуждены следить за каналами чтобы пробуждать тасклеты, то мы просто используем для сравнения каналы. Я добавил ссылку на тасклет внутри слипера и получил возможность проверить, что этот тасклет действительно есть, и вдруг, о чудо, когда CCP karkur запустила скрипт, я увидел несоответствие  судя по данным из канала тасклет был в куче, но он не совпадал с тасклетом из слипера.
 
Эврика
 
Когда канал удаляется, память, которую он занимал, высвобождается и становится доступна для повторного использования. Из этого следует, что тасклет, который засыпает в тот же тик, что и тасклет, который уничтожается в тот же самый момент, когда он собирался проснуться могут получить один и тот же указатель на канал. В этом случае RemoveSleeper удалит неправильный тасклет (тот, который только вызвал SleepWallclock) из кучи и убьёт его канал. Этот тасклет не будет занесён в расписание и не будет заблокированным.
 
Исправить очень просто  переместить вызовы Py_DECREF внутрь SleepWallclock. Это гарантирует, что каналы не будут уничтожены раньше положенного срока. Это, по моему мнению, даже лучше ведь весь учёт ссылок будет сконцентрирован в одной функции.
 
Никогда не предполагай
 
Еве уже почти 14 лет и она видела много битв за свою жизнь. Можно сказать, что код был тщательно проверен в бою. В тоже время, хоть это и не значит, что скрытые баги не прячутся внутри, но найти баг в такой фундаментальной функции было, мягко говоря, удивительно. Я думаю, что мы никогда не сможем утверждать, что любая часть нашего исходного кода на 100% корректна. CCP karkur и другие потратили существенное количество времени выслеживая эту ошибку, так что мы были достаточно уверены в более высокоуровневом коде. Именно это позволило мне начать это исследование на основе предположения, что метод Sleep был сломан, и быть достаточно упрямым чтобы воспроизвести этот редкий сценарий.
 
У нас есть основание полагать, что и несколько других багов это лишь симптомы одной и той же основной проблемы. Например, овервью не обновляется, не обновляются полоски щитов, брони и структуры. Эти баги было почти нереально воспроизвести, но несмотря на это, они периодически давали о себе знать. Особенно после массовых тестов или больших битв на Транквилити.
 
-CCP Snorlax

 


  • 25

#2
Fine

Fine

    Clone Grade Iota

  • Tech III Pilots
  • PipPipPipPipPip
  • 1796 сообщений
359
  • EVE Ingame:Vinegarroon
  • Client:Eng

А когда наведут порядок с очередью изучения навыков, не сказали? 


  • 0

#3
Vollhov

Vollhov

    Clone Grade Omicron

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPipPip
  • 18529 сообщений
2601
  • EVE Ingame:Vollhov Jr (Vollhov)
  • DUST Ingame:Такой игры не существует
  • EVE Alt:-
  • Corp:НПС Бомж
  • Ally:Амарычи
  • Client:Рус

АЛИЛУЯ !!! :mp_pop: :mp_pop: :mp_pop:


  • 0

ihnLXpl.jpg

Скорость нужна, а поспешность вредна. (С) Суворов.

 


#4
Minnesota

Minnesota

    Clone Grade Epsilon

  • Tech III Pilots
  • PipPipPip
  • 275 сообщений
3
  • EVE Ingame:Andrej Minni
  • Corp:STCTH
  • Ally:<BOT>
  • Channel:SuCo
  • Client:Eng

Нихрена не понял, но за порно плюсик!


  • 0

#5
EVE is real

EVE is real

    Clone Grade Delta

  • Tech III Pilots
  • PipPip
  • 229 сообщений
30
  • EVE Ingame:Elsa Eoner
  • Corp:BCC
  • Client:Eng

лучше бы баги пофиксили

ой


  • 2
 

   ЕВА это жизнь, которой у меня нет.

 

 


#6
vmarkelov

vmarkelov

    Clone Grade Omicron

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPipPip
  • 43421 сообщений
7193
  • EVE Ingame:Kej Lacitis
  • EVE Alt:Kej Al'tos
  • Corp:Space Mechanics
  • Ally:Circle of Hell
  • Client:Рус
Годно. Люблю технопорно :)
  • 0

Не сожалей о том что было, не думай о том что могло быть.


#7
Sir Zak

Sir Zak

    меня трудно найти легко потерять и невозможно забыть

  • Tech III Pilots
  • PipPipPipPipPip
  • 2844 сообщений
2176
  • EVE Ingame:DJ BASIL
  • Corp:X-OPS
  • Ally:N/A
  • Channel:Baraboom
  • Client:Eng

Пора бы уже эту вашу еву на гитхаб

 

И да, полюбому добавил новых багов в этот старый код


Сообщение отредактировал Sir Zak: 01 March 2017 - 16:43

  • 1

0cd6077657ba7f6fe393e83750b9a449.png


#8
Korvin

Korvin

    продолжаю наблюдение

  • Tech III Pilots
  • PipPipPipPipPipPipPip
  • 7066 сообщений
2605
  • EVE Ingame:Korvin
  • DUST Ingame:RIP
  • EVE Alt:Alpha Korvin
  • Corp:Shadow Kingdom
  • Ally:Best Alliance
  • Client:Eng

Круто, теперь мне вернут мой Мегатрон, слитый из-за этого бага в 2007 году? :ninja:


  • 0

#9
Smokyline

Smokyline

    Clone Grade Lambda

  • Tech III Pilots
  • PipPipPipPipPipPip
  • 4068 сообщений
2477
  • EVE Ingame:Vedja Scrappy

побольше бы таких блогов про движок и внутренний мир евы

даже жалко стало что я продал трупик каркур :trololo:


Сообщение отредактировал Smokyline: 01 March 2017 - 23:19

  • 0

youtube

[18:20:58] Lotara Sarrinn > надоело. потом они говорят - ПОЧЕМУ ОНЛАЙН ИГРЫ УПАЛ на 20к. Да потому что какие-то м***** держат пол евы лун, другие м***** на любую попытку генерить контент - обольют грязью на форуме. у третьих м****** чсв овер 9к

 


#10
DeGrif

DeGrif

    Clone Grade Omicron

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPipPip
  • 18581 сообщений
2751
  • EVE Ingame:DeGrif
  • Client:Рус

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


  • 0

#11
Aurum_Gallente

Aurum_Gallente

    Clone Grade Ksi

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPip
  • 11134 сообщений
1567
  • EVE Ingame:Aurum Gallente
  • Client:Eng

Пора бы уже сделать окно игрейм скриптов как в google Adwords и засечь через сколько минут после патча умельцы ломанут бд евы. До сих пор не понимаю, почему "скрипты" для модулей на самом деле обычный патрон. Когда я только начинал играть, я думал, что даётся некая скриптовая песочница, где обладатели всяких дамперов и средств ЕМ-вредительства пишут атакующие скрипты под свой фит/навыки, а обладатели сенсор-бустеров и трекеров пишут скрипты на их обход исходя из своих навыков, фита и предполагаемой ситуации.


  • 1
я всегда знаю, как надо лучше

#12
vmarkelov

vmarkelov

    Clone Grade Omicron

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPipPip
  • 43421 сообщений
7193
  • EVE Ingame:Kej Lacitis
  • EVE Alt:Kej Al'tos
  • Corp:Space Mechanics
  • Ally:Circle of Hell
  • Client:Рус

Пора бы уже сделать окно игрейм скриптов как в google Adwords и засечь через сколько минут после патча умельцы ломанут бд евы. До сих пор не понимаю, почему "скрипты" для модулей на самом деле обычный патрон. Когда я только начинал играть, я думал, что даётся некая скриптовая песочница, где обладатели всяких дамперов и средств ЕМ-вредительства пишут атакующие скрипты под свой фит/навыки, а обладатели сенсор-бустеров и трекеров пишут скрипты на их обход исходя из своих навыков, фита и предполагаемой ситуации.


А вот в такое я бы поиграл, да!
  • 0

Не сожалей о том что было, не думай о том что могло быть.


#13
Ev Xetvvvi

Ev Xetvvvi

    Clone Grade Theta

  • Tech III Pilots
  • PipPipPipPip
  • 1363 сообщений
379
  • Client:Eng

Я так понимаю этот баг связан как то с server working fine?


  • 0

#14
Minnesota

Minnesota

    Clone Grade Epsilon

  • Tech III Pilots
  • PipPipPip
  • 275 сообщений
3
  • EVE Ingame:Andrej Minni
  • Corp:STCTH
  • Ally:<BOT>
  • Channel:SuCo
  • Client:Eng

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

Полностью согласен, куски кода вообще не переведены, что за халтура!
  • 0

#15
Tir

Tir

    Clone Grade Omicron

  • Tech III Pilots
  • PipPipPipPipPipPipPipPipPipPip
  • 18223 сообщений
2744

Пора бы уже сделать окно игрейм скриптов как в google Adwords и засечь через сколько минут после патча умельцы ломанут бд евы. До сих пор не понимаю, почему "скрипты" для модулей на самом деле обычный патрон. Когда я только начинал играть, я думал, что даётся некая скриптовая песочница, где обладатели всяких дамперов и средств ЕМ-вредительства пишут атакующие скрипты под свой фит/навыки, а обладатели сенсор-бустеров и трекеров пишут скрипты на их обход исходя из своих навыков, фита и предполагаемой ситуации.

Может проще пойти устроиться погромиздом?
  • 0

#16
Torius Enaka

Torius Enaka

    Clone Grade Nu

  • Tech III Pilots
  • PipPipPipPipPipPipPipPip
  • 8358 сообщений
347
  • EVE Ingame:Torius Enaka
  • Corp:Caldari Provisions
  • Client:Eng
Лучше бы лаги пофиксили
  • 0




0 посетителей читают тему

0 members, 0 guests, 0 anonymous users