[Перевод] Как отладить программу, к которой у тебя нет доступа

5gpb_tdlqr7rne12wizdzydbwde.jpeg
Фото: Intricate Explorer, Unsplash

Сегодня я вспомнил один из любимых «программистских мифов», который вполне может быть городской легендой, и свою собственную версию «чёрного ящика», который требовал отладки.

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


«Чёрный ящик» — это популярная концепция программирования, предполагающая, что мы находимся снаружи системы или компонента, не имея прямого доступа к коду. Это может быть вызвано различными факторами:
  • Вы работаете со сторонним ПО, разработчики которого просто не раскрывают код.
  • Вы взаимодействуете с API, внутренняя логика которого абстрагирована.
  • У вас нет необходимых полномочий для доступа к Git-репозиторию.
  • Даже система с полным доступом может де-факто стать «чёрным ящиком» из-за своей сложности.
  • Сотрудник, обладавший всеми ключами и знаниями, внезапно уволится/пропал/умер.
  • Легаси-система состоит из .dll, которая «всегда работала» на сервере, и не была подключена к системе контроля версий. Чтобы просто посмотреть на код, её нужно декомпилировать, если это возможно, конечно.

Все эти факторы сводятся к тому, что у нас есть проблема, которую мы не можем устранить мгновенно, и мы не знаем, в чём заключается ошибка. Поэтому нам нужно браться за работу.
Список перечисленных выше факторов, вероятно, не описывает все ситуации, потому что это непосредственный список факторов, повлиявших на нашу ситуацию. У нас был только что уволен разработчик, сложная система, распределённая по нескольким серверам, с основной .dll, которая «делала своё дело», хотя никто и не знал, почему и какое именно.

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

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

Это один из тех случаев, когда я испытал благодарность за хаотический мир, в котором вырос, потому что большинство компаний не столкнётся с настолько серьёзными проблемами, и не назначит решать их недавнего выпускника. Мне в этом смысле повезло, и ещё больше повезло, потому что у меня была поддержка опытных разработчиков, помогавших мне на протяжении всего процесса. Итак, вот каким было наше решение.


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

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

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

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


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

Под этим я подразумеваю, что вам требуются две константы:

  • Используемые данные не должны изменяться другими системами
  • Возможный урон должен быть максимально ограничен

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

Поэтому мы собрались и задались вопросом: «что самое худшее может произойти, если мы это испортим?», а потом написали скрипт базы данных, переписывающий все результаты в ошибочное состояние, чтобы последующие системы не обрабатывали их.

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


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

В нашем случае мы обладали роскошью в виде достаточно качественно отформатированных файлов JSON от предыдущей системы, от которых зависели наши входящие данные. После того, как мы всё подготовили, оставалось в буквальном смысле начать сравнивать пару текстовых файлов в Notepad++, пока не найдём сходства между файлами, вызывающими ошибки, а затем различия между ними и правильно работающими файлами.

Тут нам повезло — мы быстро смогли выяснить, что баг вызывает конкретное сочетание флагов заказчика, и нам сразу же удалось их обойти, потому что этот случай можно было «имитировать» похожими, но другими флагами. Поэтому поскольку мы уже знали, что систему будут переписывать (на самом деле, у нас и не было выбора), было решено обойти этот баг вместо декомпиляции и его устранения.


Очевидно, что менять «живые» данные в базе данных в продакшене, надеясь, что всё будет работать без реального тестирования — плохая идея, но у нас не было другого выбора.

Всё сработало отлично, ведь количество случаев было низким, а первые несколько тестов мы провели ручную и оказалось, что они в точности создают требуемые результаты.

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

Вот такое приключение.


Меня этот способ отладки и поиска ошибок восхищает, я люблю, когда программирование сочетается с всплеском адреналина.

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


На правах рекламы


Закажите и сразу работайте! Создание VDS любой конфигурации в течение минуты, в том числе серверов для хранения большого объёма данных до 4000 ГБ. Эпичненько :)

Подписывайтесь на наш чат в Telegram.

8p3vz47nluspfyc0axlkx88gdua.png

© Habrahabr.ru