14:06 

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

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

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

1. Первым делом я зашёл на сервер посмотреть, в каком состоянии находится процесс данной vnc-сессии этого пользователя. Для этого используются команды top и ps. Они показывал аномальную 100% загрузку CPU - такого быть не должно. Нормальный режим работы серверного процесса vnc - 3-5% потребления CPU: он ждёт команды что-то отрисовать, переместить - когда такая команда поступает, формируется и передаётся по сети новое изображение, а процесс снова засыпает в ожидании новой команды извне. 100% загрузка означает, что процесс постоянно занят вычислительной работой и не может перейти в режим прослушивания команд и поэтому он никак не реагирует на попытки подключиться к нему.

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

Сделал strace процесса Xvnc, увидел следующую картину (зацикливание):



strace — это утилита, отслеживающая системные вызовы от приложения к операционной системе. Эти вызовы могут быть перехвачены и прочитаны. Это позволяет лучше понять, что процесс пытается сделать в заданное время. Системных вызовов много, приложение управляет ресурсами операционной системы (памятью, открытыми файлами, потоками, буферами). Здесь случайно так получилось, что в отчёт попал только вызов select.

В отчёте только эти повторяющиеся строчки select в большом количестве, и стало понятно, что процесс зациклился на какой-то функции.
В описании системного вызова select сказано, что EBADF возвращается тогда, когда один из файловых дескрипторов (в отчёте они в квадратных скобках), отправленных в select - недопустим. На лицо программная ошибка программистов, которые не предусмотрели проверку входных параметров в этом месте приложения.

3) Ок, смотрим /proc/123456/fd (список файловых дескрипторов процесса), видим, что 111 отсутствует в списке. (что с ним произошло - не в курсе). Каталог /proc/идентификатор_процесса - виртуальный каталог, где в текстовом виде можно найти ВСЮ актуальную информацию про ВСЕ текущие процессы, очень удобная сущность для исследования.

4) Далее мне понадобился отладчик gdb, присоединяемся к процессу и в нём выполняем команду "catch syscall select" - gdb покажет точки вызова select процессом, а команда отладчика "bt" - показала стек вызовов, таким образом я определил функцию в Xvnc и её адрес, которая вызывает ущербный select. Ставим точку останова на вход в select, анализируем входные параметры для функции (info r - показать содержимое всех регистров)
5) затираем регистр, в котором передавался недействительный ф.д. 111 (команда отладчика set $edx=0), после этого вызов функции прошёл успешно

Таким образом подменил входные данные в функцию в действующем процессе, убрал упоминание недействительного файлового дескриптора. Процесс сразу же вышел из этой функции совсем и возобновилась нормальная работа приложения. Повезло, что сработало. Можно было действовать иначе: после возврата из системного вызова заменить в eax значение -1 на 0 (успешный выход).

Спасибо за внимание, вы сейчас прочитали про несколько часов трудовой жизни Тротила. Жалко, что с такими интересными задачами сталкиваешься редко, хотелось бы почаще.

URL
Комментарии
2015-05-27 в 14:15 

Стороной
Техническая причина
Круто! :crazy:

2015-05-27 в 18:52 

Bats
Trotil, вау.

2015-05-28 в 08:00 

Trotil
Dana Eilian, любопытно, сколько процентов этого текста понятно неспециалисту? Я сам оценить не могу, поэтому интересен взгляд со стороны.

URL
2015-05-28 в 20:47 

Bats
Первое впечатление от текста, конечно, что Ты крутой профи и знаешь очень какие-то сложные штуки. Перечитав второй раз, в принципе, понятно, в чём там получилась проблема и как Ты её налаживал. Несмотря на то, что становится понятна суть произошедшего, сохраняется чувство, что Ты крутой профи и знаешь офигенно сложные штуки.

Дневник Тротила

главная