Я хотел бы начать свой рассказ с небольшой автобиографической вставки. Более 14 лет я занимаюсь разработкой платформ в нашей компании, из них примерно три года я проработал на стыке разработки и поддержки (в группе поддержки продукта отдела разработки). Это был бесценный опыт, который многому меня научил, и многие примеры, которые я хочу привести в этой статье, корнями уходят в этот период времени. Решение проблем поддержки требует хорошего знания инструмента разработки, операционной системы, используемого сервера баз данных, а также хорошей интуиции и творческого мышления.
Цель этой статьи - показать, насколько обширна область по работе с проблемами в программном обеспечении, а также просто собрать и обобщить некоторые стратегии решения проблем, которые применяют в работе разработчики, тестировщики и специалисты поддержки при устранении этих проблем.
В нашей компании выстроена поэтапная технология работы с внесениями изменений в исходный код продукта и поэтому любое исправление дефекта проходит, по существу, следующие этапы:
- воспроизведение
- анализ кода и отладка
- внесение исправления
- самостоятельное тестирование
- рецензирование
- тестирование силами тестировщика
- опытная эксплуатация
- промышленная эксплуатация в системе клиента
И хотя проблемы могут возникать и выявляться на самых разных этапах этого процесса, я хочу сосредоточиться по большей части на этапе воспроизведения проблемы. Особенность устранения проблем в ПО на этапе поддержки состоит в том, что система уже реально используется. Мы не можем просто так взять и подключиться к продуктивному серверу с помощью отладчика, чтобы понять причину ошибки. Не всегда есть возможность даже установить отладочную версию для сбора отладочных логов. Понятное дело, инциденты могут быть различной степени тяжести: либо это невозможность выполнить какое-то не самое важное действие в системе на каком-нибудь одном рабочем месте с возможностью обходного решение (не очень критично), либо это недоступность всей системы или ошибки при выполнении действий руководством компании-клиента (очень критично). В любом случае надо быть готовым к тому, что проблему воспроизвести не удастся и надо будет вносить в нее изменения, опираясь только на интуицию и догадки.
Снижение критичности
На первом этапе работы с критичной проблемой, воспроизвести которую не удается, важно сосредоточиться на уменьшении ее критичности для заказчика. Если из-за какой-то функциональности перестает быть доступной вся система, надо попытаться отключить именно эту функциональность. Например, замечено, что после включения пользователя в группу система начинает с разных рабочих мест выполнять перестроение таблицы полных прав и в результате пользователи не могут зайти в систему или выполнять в ней какие-либо действия.
Для снижения воздействия проблемы на бизнес заказчика можно с помощью организационных мер: запретить администраторам включать пользователей в группы, либо выполнять это действие в нерабочее время, когда минимальны риски остановить какой-нибудь критически важный бизнес-процесс заказчика. Если задачи на службе Workflow периодически начинают падать из-за системных проблем, то можно попробовать настроить автоматическое возобновление таких задач и автоматизировать перезапуск службы или перазагрузку сервера. Если функция преобразования документа в PDF приводит к тому, что сервер приложений "отжирает" много памяти и потом аварийно завершается, то можно вынести эту функцию в отдельный процесс, который, может, и будет иногда аварийно завершаться, но это не будет мешать работе остальных пользователей системы.
Обходной вариант
В идеальной картине мире разработчики в плановом порядке вносят изменения системы, реализуя как новый функции системы, так и решая проблемы заказчиков. И хотя в реальной жизни так бывает не всегда, мы, конечно же, пытаемся приблизиться к этой модели. Поэтому важно предоставить клиенту обходной вариант решения проблемы.
Хорошо, когда в системе одну и ту же задачу можно решить разными способами. Если один из вариантов откажет, есть возможность воспользоваться другим. Не всегда специалист службы поддержки может самостоятельно придумать удовлетворяющее заказчика обходное решение проблемы. Поэтому для решения этой задачи требуется участие разработчиков.
В некоторых случаях обходным решением может быть отказ от автоматизации какого-то процесса и переход к ручному или "бумажному" варианту. Например, если проблемы возникают при подписании документа, то можно временно считать его согласованным без установки подписи. Если не работает автоматическое создание пользователей из службы каталогов, то можно временно перейти к ручному заведению их в информационных системах. Если проблема возникает при редактировании документа в сервисе совместного редактирования, то можно попробовать временно с помощью организационных мер организовать работу через веб-агента. Если же невозможна работа через веб-агента, можно воспользоваться функциями выгрузки и загрузки документа напрямую через веб-клиента. То, что клиенту приходится работать с обходным решением, конечно, сказывается на репутации нашей компании, но надо понимать, что в некоторых случаях это будет наиболее правильным решением, так как разработчикам все же необходимо время для того, чтобы проанализировать ситуацию и подготовить полноценное исправление.
Анализ и "воспроизведение" проблемы
Если нам удалось снизить критичность проблемы для заказчика, либо найти обходное решение для его проблемы, то на следующем этапе необходимо поработать над воспроизведением проблемы, так как если проблему удастся воспроизвести, то значительно выше вероятность того, что разработчикам удастся ее исправить. Процесс воспроизведения проблем может быть очень творческим, хотя и удача тут тоже не помешает. К сожалению, не всегда проблему удается воспроизвести, поэтому приходится вносить изменения в систему с целью уменьшить вероятность возникновения определенных категорий ошибок, но это отдельная тема.
Первоначально я задумывал эту статью именно как обобщение вариантов воспроизведения проблем, но получилось копнуть немного шире. Так или иначе, для воспроизведения проблем можно использовать следующие стратегии:
- Анализ лог-файлов системы. Система часто генерирует большой объем файлов логов, по которым можно косвенно понять, какие действия в системе предшествуют возникновению проблемы. Если проблема проявляет себя именно как ошибка, то в ошибке можно будет увидеть стек вызова, класс исключения. Во многих случаях (но не всегда) этой информации может быть достаточно для устранения проблемы. Также полезными могут быть не только сообщения об ошибках, но и просто информационные сообщения о выполнении той или иной активности в системе. В этом плане логи DirectumRX, например, более информативны, чем логи DIRECTUM 5. По этим логам мы можем даже понять, как одна команда проходила при своем выполнении через разные сервисы.
- Анализ дополнительных протоколов. Помимо основных лог-файлов системы, мы зачастую можем отслеживать ее функционирование с помощью отладочных инструментов, которые также генерируют свои протоколы. Сюда можно отнести журнал событий Windows, протоколы утилит мониторинга сетевых пакетов, протоколы файловой активности, протоколы чтения или записи в реестр, протоколы передачи оконных сообщений и т.д. Например, в системе КАС НПО была настроена постоянная запись трейсов. В результате была возможность в любой момент проанализировать причины ошибок, касающихся выполняемых SQL-запросов (deadlock, блокировки).
- Дополнительное логирование. В некоторых случаях для воспроизведения ошибки бывает недостаточно того логирования, которое выполняется системой. В таком случае могут помочь отладочные логи. Например, я помню, как занимался устранением нестабильных ошибок в контроле мультивыбора в рамках разработки IS-Builder 7. Тогда было принято решение добавить расширенные отладочные логи во все действия, выполняемые при редактировании в этом контроле. В итоге при выполнении действий в соответствии с полученными логами удалось воспроизвести эту ошибку, а потом и исправить ее.
- Имитация окружения. В некоторых случаях ошибка воспроизводится только на специфичном окружении. Это могут быть какие-то конкретные версии операционной системы, браузера, плагинов, приложения-редактора, наличие в системе определенных шрифтов. К сожалению, не всегда есть возможность установить необходимое сторонее ПО на стенде разработчика (например, оно может быть платным).
- Поиск аналогий в других программах. Иногда необходимая функциональность хорошо работает в одних программах и не работает в других. Например, функциональность предпросмотра в проводнике системы, реализованную с помощью обработчиков предпросмотра, мы при разработке сравнивали с аналогичной функциональностью в проводнике Windows. Поведение с путями к файлам (сокращенные названия папок, сетевой/локальный путь) можно проверять в проводнике Windows и FAR. Функциональность подписания можно проверять в общедоступных сервисах подписания и сторонних продуктах, предназначенных для подписания (например, КриптоАРМ). Не всегда поведение другой программы более корректно, чем поведение нашей системы, но свериться с ним никогда не будет лишним. Бывает так, что в стороннем ПО ошибка тоже воспроизводится. Тогда, может быть, проблема вообще не на нашей стороне.
- Влияние механизмов защиты ОС. Бывает так, что система устанавливается в среду с другим антивирусом, где не настроены необходимые исключения. Либо работа идет с файлом, который блокируется механизмом защиты операционной системы (например, запуск неподписанного исполнимого файла или попытка открытия файла, скачанного из ненадежного сайта в интернете). В некоторых случаях такие проблемы могут решаться настройками на стороне клиента (добавление некоторых папок в исключения для антивируса, снижение настроек безопасности в операционной системе).
- Влияние кэша и перестроение кэша. В системе на базе IS-Builder 7 достаточно часто ошибки возникали из-за некорректных файлов с кэшами (например, они могли быть некорректно сформированы, либо хранили уже неактуальные данные, но еще не обновились). Зачастую в таких случаях помогает рекомендация сбросить кэша. Только надо помнить о том, что кэш может быть организован на разных уровнях - на клиенте в памяти, на клиенте на диске или на сервере.
- Выполнение в контексте системной службы. На Windows есть ряд ограничений на процесс, который выполняется как системная служба. Такое процесс может иметь собственные ограничения на объекты рабочего стола, которые он создает, использует отдельную специальную папку профиля. На память приходит эпичная проблема с утечкой атомов на службе Workflow, которую мы решали в рамках разработки IS-Builder 7. В двух словах: атом - это некоторая сущность ядра Windows, которая выделяется при создании окон. На каждый перезапуск процесса службы отъедалась какая-то часть атомов. Процесс службы Workflow часто перезапускался, поэтому через некоторое время все атомы заканчивались, и лечилось это только перезапуском сервера. Вообще говоря, создавать визуальные объекты в системной службе - это нехорошо, но в силу особенностей архитектуры IS-Builder 7 избежать этого было крайне сложно. Так или иначе, проблема с атомами была решена.
- Разработка тестового приложения. Проблемы с десктоп-приложениями и сервисами можно пытаться моделировать с помощью небольших тестовых приложений. Это помогает абстрагироваться от остального контекста и создать минимально необходимые условия для воспроизведения проблемы. При необходимости к тестовому приложению можно подключить другие библиотеки тестируемого приложения. Тестовое приложение позволяет отделить проблему от нашей системы и локализовать подсистему, с которой есть проблемы. Например, часто приходилось практиковать написание тестовых приложений для проверки сетевого соединения, интеграции со службой каталогов, проверки соединения с SQL-сервером. С переходом на веб-клиент этот подход стал менее актуален.
- Проверка состава установленных файлов. Эта проблема тоже больше актуальна для десктоп-приложений (в DirectumRX такое может быть с веб-агентом). Иногда проблема с приложением может быть просто из-за того, что состав устанавливаемых файлов нарушен, не хватает каких-либо файлов, либо они не той версии, либо они повреждены.
- Искусственная генерация исключения. Иногда в системе обнаруживаются места, не покрытые автоматическими тестами, где генерация исключения может приводить к печальным последствиям. При этом не всегда возможно повторить исходные причины ошибки, которые имели место быть у данного конкретного клиента. В таком случае может помочь искусственная генерация ошибки в проблемном месте. Напимер, частой причиной AppCrash для IS-Builder 7 была генерация ошибки в каком-нибудь фоновом потоке.
- Обновление зависимостей. Если проблема возникает в сторонней библиотеке, то есть вероятность, что она исправлена в более новой версии этой библиотеки (также есть вероятность, что внесены новые проблемы). Можно детально проанализировать списки изменений этой библиотеки и при необходимости перейти на новую версию.
- Имитация нагрузки на систему. Некоторые проблемы начинают проявляться при высокой нагрузке на систему. И хотя для моделирования поведения системы в условиях высокой нагрузки предназначено нагрузочное тестирование, для воспроизведения ситуации на стенде разработчика может понадобится имитация высокой нагрузки. Например, таким образом можно проверить, утилизирует ли все ресурсы доступные процессора какой-нибудь сервис, как он себя ведет при отжирании большого количества оперативной памяти, встроены ли в него какие-нибудь методы самодиагностики для таких ситуаций. На моей памяти есть случай, когда мы "расшивали" блокировку на службе файловых хранилищ с учетом идентификатора документа (предварительно мы воспроизвели ошибку долгого ожидания в критической секции с помощью имитации нагрузки на сервис).
- Выполнение одновременно из нескольких потоков. Некоторые проблемные ситуации начинают проявляться только при высокой степени параллелизма. Если ошибка не воспроизводится при обычным выполнении действий в системе, есть вероятность воспроизвести ее путем выполнения действия параллельно из нескольких потоков.
- Отладочный менеджер памяти. Даже в наше время господства языков с автоматическим управлением памятью иногда приходится разрабатывать функциональность, где применяется ручное управление памятью. В платформе IS-Builder 7 такого кода было, конечно, гораздо больше. В DirectumRX в рамках веб-агента также есть код, который написан на C++ и использует ручное управление памятью. С таким кодом есть потенциальная опасность, что где-то произойдет выход за границы массива, память повредится и потом это аукнется какой-нибудь ошибкой типа Access Violation. В свое время в плагинах шифрования/подписания в IS-Builder 7 нам удалось отловить некорректную работу с памятью с помощью отладочного менеджера памяти FastMM. Для C++ тоже есть подобные инструменты.
- Случайные данные. Иногда ошибка не воспроизводится на простой последовательности действий. В таком случае можно попробовать воспроизвести ее на группе случайным образом сгенерированных данных. Буквально несколько дней назад я пользовался этим подходом для воспоизведения одной нестабильной ошибки.
- Анализ исходников. Речь не только про исходники самой системы, но также про исходники сторонних библиотек, фреймворков, и даже декомпиляция исходников операционной системы (приходилось заниматься и таким: когда мы искали отличия между механизмом предпросмотра проводника Windows и нашей системы, использовали декомпиляцию исходников проводника Windows и нашли ряд отличий в нашей реализации). Также стоит помнить о том, что доступны для анализа исходники .NET. Основная проблема с анализом причин проблем в нашей системе, когда требуется анализ исходников сторонних продуктов, это непонятные, ни о чем не говорящие сообщения об ошибках. Анализ исходников в таком случае позволяет сузить область поиска, либо найти обходное решение.
На этом завершаю свое повествование с надеждой, что содержимое статьи оказалось для кого-нибудь полезным.
С удовольствием выслушаю другие, не упомянутые мной в этой статье, стратегии воспроизведения ошибок, которыми вы, возможно, пользуетесь в своей работе.
Авторизуйтесь, чтобы написать комментарий