Интеграция ReSanity StubIt в OpenTTD (Часть №1)

Ну что же, на этот раз мы решили серьезно подойти к вопросу подготовки демо-интеграций нашей Системы Защиты и Лицензирования ПО ReSanity StubIt.
Первой «жертвой» решили выбрать проект OpenTTD. Он подходит по многим причинам: 1) широко известен, 2) с открытыми исходными текстами, что позволит нам провести интеграцию с использованием StubIt Runtime API и StubIt Installer API, то есть позволит показать все наши возможности и «няшки».
Тем более, у нас уже есть в арсенале более серьезный инструмент — выбор регионов кода на основе профилирования, который не входит пока в пакет альфа-версии ReSanity StubIt, но уже в бета-версии будет присутствовать в StubIt Monetization Studio.

Итак, начали…

Сюрпризы не заставили себя долго ждать — уже при попытке анализа OpenTTD с использованием нашего профайлера, мы получили замечательный крэш при выходе из игры. Два дня пришлось потратить на то, чтобы разобраться, в чем истинная причина данного, весьма нестабильно-воспроизводящегося крэша.
В общем-то, как и ожидалось, причина крэша — ошибка в OpenTTD, если быть точным, то в функции класса FlowEdgeIterator:

  void SetNode(NodeID source, NodeID node)
  {
    static const FlowStat::SharesMap empty;
    const FlowStatMap &flows = this->job[node].Flows();
    FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
    if (it != flows.end()) {
      this->it = it->second.GetShares()->begin();
      this->end = it->second.GetShares()->end();
    } else {
      this->it = empty.begin();
      this->end = empty.end();
    }
  }

Строка «static const FlowStat::SharesMap empty;» и порождает проблему. Внешне вроде бы все верно, но на самом деле тут кроется недобрая бага типа multithreaded «race conditions» при инициализации статического объекта «empty». Причем, как и подобает большинству multithreaded-багов, эта бага НЕ проявляется при нормальной работе OpenTTD, а только под «нагрузкой», которую создает наш профайлер, основанный на DBI DynamoRIO.

Ошибка исправляется достаточно просто — путем переноса строки «static const FlowStat::SharesMap empty;» из функции void SetNode(NodeID source, NodeID node) непосредственно перед классом FlowEdgeIterator, что-то типа этого:

static const FlowStat::SharesMap empty;

class FlowEdgeIterator {
private:
  LinkGraphJob &job; ///< Link graph job we're working with.
...

После исправления ошибки, OpenTTD перестал крэшить по окончании профилирования и мы смогли двигаться дальше… О чем я напишу позже…

Запись опубликована в рубрике Моя рубрика. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован.