Сеанс термоядерной отладки для админов или эффективные приемы борьбы с пожирателями производительности
Крис Касперски
При падении производительности сервера на базе Win2k3 и возникновении проблем со стабильностью его работы администратору нужно заглянуть во множество уголков операционной системы. Для выявления узких мест им могут быть использованы различные решения, в том числе и хардкорные, например, отладчик уровня ядра Soft-Ice.
Содержание:
- Введение
- Сеанс термоядерной отладки
- Заключение
- Мини-статья: Почему счетчики производительности ненадежны
- Блок-врезка
- Боковые выносы
Операционные системы семейства NT поддерживают развитую систему мониторинга счетчиков производительности, отображающих в реальном времени, сколько «тиков» ушло на ту или иную операцию. Не отстают от них и процессоры, позволяющие регистрировать практически любые события: от количества переключений контекста до интенсивности кэш-промахов. Специальные программы-профилировщики обобщают эту информацию, выявляя так называемые «горячие точки», в которых система проводит наибольшую часть своего времени, однако точность измерений невелика, и прежде чем приступать к профилировке (или снятию показаний счетчиков производительности), требуется устранить все побочные факторы, способные ввести профилировщик в заблуждение. В противном случае приходится исходить из предположения, что все приложения и драйвера работают правильно, чего в жизни, увы, практически никогда не наблюдается.
Soft-Ice, будучи отладчиком уровня ядра, позволяет остановить систему в любой момент и посмотреть, какими интересными делами она сейчас занимается. С Soft-Ice в руках поиск «бутылочного горлышка» занимает буквально считанные минуты, причем результат абсолютно надежен. От него не скрыться ни «кривым» драйверам, ни некорректно работающему аппаратному обеспечению. Кстати, знания ассемблера для этого не потребуется. Невероятно, но факт! Достаточно освоить несколько несложных команд, а все остальное Soft-Ice сделает за нас!
Как узнать, на что уходит львиная доля системного времени? Очень просто — установить Soft-Ice, вызывать его комбинацией <CTRL-D>, посмотреть, чем занимается сервер, выйти из Soft-Ice по <CTRL-D> или команде «x;<ENTER>», затем тут же вызывать его вновь: чем занимается система на этот раз? Повторяя данную операцию в цикле, мы достаточно быстро соберем богатый статистический материал. Совершенно очевидно: истинное процессорное время, потребляемое каждым компонентом, прямо пропорционально частоте его появления при вызове отладчика.
Когда сервер вообще ничем не нагружен, свыше 90% «всплытий» приходится на превдопроцесс «Idle», означающий, что текущие операции выполняются так быстро, что мы их просто не засекаем, т.к. они занимают ничтожные доли процессорного времени, при этом сервер может обслуживать достаточно большое количество пользователей, дефрагментировать жесткий диск в фоновом режиме и заниматься прочими делами. Вот так парадокс! Сервер работает, а процессор — отдыхает, это означает, что конфигурация сервера имеет определенный избыток по мощности, и узких мест в ней нет.
При дальнейшем росте загрузки мы получаем более или менее «гладкое» распределение, попадая то в интерпретатор PHP, то в процесс, обслуживающий SQL, то в другой сервис. Это вполне нормальное явление, однако доминирование одного процесса над другим — плохой признак, указывающий на дефекты проектирования программы. В нормальной ситуации свыше 90% машинного времени уходит на ввод/вывод, в течение которого процессы, инициировавшие запрос ввода/вывода, спят как младенцы независимо от того, справляется дисковая подсистема/сетевая карта со своей работой или нет.
Повышение активности процессов свидетельствует о «тяжелых» операциях типа криптографии, упаковке/распаковке потока данных и прочих «наукоемких» задачах, большинство из которых, кстати говоря, сугубо опционально. В частности, шифрование можно либо вообще отключить, либо выбрать более «легкие» алгоритмы. Аналогичным образом обстоит и с упаковкой. Естественно, ситуацию с вещанием цифрового аудио/видео мы в расчет не берем. Тут, понятное дело, требуется мощный процессор с емкой кэш-памятью. А как узнать, что кэш-памяти достаточно для решения поставленной задачи? Очень просто! Если Soft-Ice останавливается на произвольных машинных инструкциях — все ОК, а вот если доминируют инструкции чтения/записи в память (MOV плюс что-то в квадратных скобках, а так же MOVSx, CMPSx и STOSx), то установка процессора с более емкой кэш-памятью существенно поднимет производительность!
Но это все теория, переходим к практике и вещам далеко не очевидным даже для тех, кто давно использует Soft-Ice. Имя процесса, исполняемого в данный момент, отображается в правом нижнем углу, а текущий исполняемый код — в окне CODE, расположенном посередине экрана. В правильно сконструированной программе процесс останавливается каждый раз в случайном месте. То есть колонка адресов будет каждый раз разная. Это означает, что в исследуемом процессе нет горячих точек, или они выражены крайне слабо. Напротив, если какие-то диапазоны адресов встречаются чаще остальных — мы поймали горячую точку, которую разработчики программы должны были устранить еще на стадии проектирования, ну или в крайнем случае в процессе оптимизации своего продукта перед выбросом его на рынок. Впрочем, если они ее не устранили, это не повод для паники, ибо, как мы уже говорили, ресурсоемкие операции типа криптографии занимают намного больше времени, чем, например, отдача пользователю файла в том виде, в котором он записан на диске.
Однако достаточно часто приходится сталкиваться с горячими точками, не имеющими ничего общего с вычислениями. Как правило, это циклы, ожидающие наступления определенных событий. Правильно спроектированная программа никогда не прибегает к такому тупизму, она просто вгоняет поток в сон, указывая, при каких обстоятельствах он должен проснуться. Конечно, распознать циклы, не имея опыта дизассемблирования, не так-то просто, но их можно запеленговать и по диапазону принадлежащих им адресов — если они появляются с огромным отрывом от всех остальных, отправляем программу на свалку, благо практически для всего ПО можно подобрать аналог.
В качестве наглядного примера рассмотрим простую программу, мотающую холостой цикл, исходный код которой укладывается в одну строку на Си: «main(){while(1);}». Откомпилируем ее без опций оптимизации (в случае MS VC это: «cl.exe hCPUl.c«), чтобы оптимизатор не выбросил ненужный с его точки зрения цикл while, и запустим ее на выполнение. На однопроцессорных машинах мы получим 100% загрузку по индикатору, на двухпроцессорных (или однопроцессорных с двумя ядрами) — 50%, соответственно, 4 процессора/ядра дадут всего лишь 25%, хотя Soft-Ice при каждом вызове будет всплывать в одном и том же процессе — hCPUl, исполняющемся в одном и том же месте — в цикле while, то есть, согласно Soft-Ice, загрузка процессора близка к 100%. Это одновременно верно и нет. С одной стороны поток, в котором исполняется цикл while, нагружает всего лишь один процессор/ядро из всех имеющихся, совсем не препятствуя исполнению потоков других процессов на оставшихся процессорах (ядрах), однако реальное торможение намного больше, чем это следует из простых арифметических расчетов. Системный планировщик распределяет очереди потоков равномерно между всеми процессорами (ядрами), и если один из процессоров (ядер) захватывается «неправильным» потоком, планировщик начинает оптимизировать очередь потоков, но эффективность такой оптимизации невелика, в чем нетрудно убедиться, измерив реакционноспособность сервера с запущенной программой hCPUl и без нее.
Теперь перейдем к более сложным вопросам, зарывшись в недра драйверов. Soft-Ice всегда отображает имя процесса в правом нижнем углу, вне зависимости от того, находится ли система на прикладном (user-land) или ядерном (kernel-space) уровне. А что у нас в ядре? Ну, например, драйвера, которые могут вообще не иметь никакого отношения к отображаемому процессу. Просто драйвер получил управление (например, по прерыванию, поступившему извне) именно в тот момент, когда система переключила контекст на данный процесс. А может быть, совсем другой процесс прямо или косвенно инициировал вызов драйвера и тут же заснул, система переключила контекст на следующий процесс, а драйвер… как бы выразиться поделикатнее… если не завис, то конкретно застрял. Соответственно, при вызове отладчика мы будет оказываться в нем чаще, чем в остальных, но имя процесса, отображаемое в нижнем углу, тут не причем.
Определить, что система находится в kernel-space, очень просто. В конфигурации по умолчанию нижняя половина адресного пространства принадлежит прикладным программам, а верхняя — ядру. Соответственно, если мы наблюдаем колонку адресов < 80000000h, отладчик находится внутри прикладных программ, в противном случае — это ядро или один из его драйверов. Селектор в прикладном режиме равен 1Bh и 08h в режиме ядра.
Примечание: при указании ключа 3GB в boot.ini ядро с драйверами «ужимается» до 1 Гб, соответственно, граница ядерных земель отодвигается на 40000000h, во всяком случае, в 32-битном режиме все происходит именно так, а в 64-битном все совсем не так, но поскольку Soft-Ice работает только в 32-битном режиме, оставим проблемы 64-битных серверов за рамками данной статьи.
Допустим, Soft-Ice остановится в некотором процессе, скажем, VMwareService, но столбец адресов выходит за пределы 80000000h, и текущая исполняемая команда, выделенная инверсной строкой, расположена по BFF0ACE2h. Как узнать, какому из драйверов она принадлежит?! Нет ничего проще! Даем команду «DRIVER» или «MOD» и смотрим на базовые адреса загрузки драйверов.
К сожалению, Soft-Ice не сортирует их по списку возрастания, но этот недостаток легко исправить. Достаточно выйти из Soft-Ice, запустить поставляемый вместе с ним Symbol Loader и сохранить историю команд в текстовой файл, а затем посредством MS Word или MS Excel преобразовать ее в таблицу.
Драйвер, в чьих границах находится обозначенный адрес, и есть «виновник». В данном случае — это NDIS, низкоуровневый сетевой драйвер. При интенсивной работе сетевой карты его появление в Soft-Ice вполне объяснимо, приемлемо и понятно, однако это должна быть действительно очень высокая нагрузка, в противном случае — это кривой драйвер или неправильная сетевая карта, смена которой (вместе с драйвером) ощутимо повысит общую производительность сервера, причем (внимание!) факт подобной «кривизны» не определяет ни системный монитор, ни легион программ, предназначенных для тюнинга сервера, поскольку все они работают на более высоком уровне.
Другая широко распространенная ситуация — Soft-Ice начинает часто всплывать на команде чтения (реже — записи) в/из порт ввода-вывода, обычно представленный машинными командами IN Ax DX/OUT DX,Ax соответственно. Это плохой признак! Нормальное железо, управляемое правильными драйверами, работает по прерыванию, обмениваясь данными через DMA. Обращения к портам лишь инициируют ту или иную операцию, а потому занимают ничтожное время.
Интенсивная работа с портами ввода/вывода нагружает системную шину, снижая производительность всех подсистем компьютера в целом, а потому такую ситуацию необходимо избегать любой ценой. Легко сказать — избегать! А для этого, как минимум, необходимо определить, к какому именно оборудованию обращается система. No problem, сейчас определим!
Смотрим на значение, содержащееся в регистре DX (младшие 16-бит регистра EDX или, выражаясь человеческим языком, крайние 4 цифры справа), выходим из отладчика, запускаем «Диспетчер устройств» в меню «Вид» выбираем сортировку устройств по ресурсам и в портах ввода/вывода тут же находим наш порт, равный в данном случае 379h и принадлежащий LPT1 – порту принтера, который вообще не установлен на обозначенной системе. Хм, странно, принтера нет, а обращение к порту происходят с чрезвычайно высокой степенью интенсивности, причем не по прерыванию, а по опросу (готовности устройства).
Лезем в очередь печати и видим, что в ней каким-то чудом оказался документ, отправленный на печать, и какой-то левый принтер (в смысле его драйвер) «мистическим» образом повешен на LPT1, хотя физического принтера нет. Очищаем очередь печати — не очищается! Сносим драйвер — обращение к порту исчезает, а машина летит вперед с такой производительностью, что буквально отрывается от асфальта. Другим путем обнаружить «бутылочное горлышко» не удавалось — системный монитор упорно твердил, что все нормально, никаких косяков.
Наконец, Soft-Ice может останавливаться не только внутри драйверов, но и ядерных функций, лежащих в области адресов 8xxxxxxxh, где находится ntoskrnl.exe. Само ядро тут, понятное дело, не причем. Это просто какой-то противный драйвер напрягает его вызовом тех или иных функций. Что же это за драйвер такой?
Даем команду «STACK» и смотрим на левую колонку адресов, по которой и определяем драйвер описанным выше способом. В данном случае им оказался драйвер DVD-привода, автоматически установленный системой. Тщательное расследование выяснило, что к драйверу никаких претензий нет, а вот дефект проектирования DVD-привода привел к тому, что драйвер начинал обмениваться с ним какими-то командами (к слову, они так и не были найдены в стандарте на ATAPI-команды), даже если в лотке отсутствовал диск. Смена DVD-привода «утихомирила» драйвер, «мистический» обмен прекратился, а общая производительность системы опять-таки возросла.
Мы убедились, что поиск «узких» мест сервера при помощи Soft-Ice – это простой, но чрезвычайно точный и надежный способ, охватывающий абсолютно все уровни: аппаратный, ядерный и программный. Конечно, не стоит ждать от Soft-Ice точных «телеметрических» показаний, выраженных в численном виде. Да этого, в общем-то, и не нужно. Если в системе имеется «бутылочное горлышко», то Soft-Ice обнаружит его практически сразу после пяти-шести вызовов.
К недостаткам описанного способа следует в первую очередь отнести то, что на Win2k3 в 32-разрядном режиме он еще работает (да и то не со всеми видеокартами и чипсетами), но более новые системы ему не по зубам. К тому же Soft-Ice способен обрушивать систему, вызывая голубые экраны смерти, зависания (от которых порой не спасает и RESET, а только «передерживание» питания) и перезагрузки.
Последние два случая очень опасны — сброса дисковых буферов не происходит, и раздел, с которым ведется активная работа на чтение/запись, рискует отправиться к праотцам. Но даже если «полет нормальный», в момент вызова Soft-Ice сервер останавливает системные часы (которые потом нам придется корректировать) и прекращает весь сетевой обмен, что при продолжительном пребывании в отладчике ведет к обрыву TCP/IP соединений, вызывая естественное недовольство пользователей.
Мини-статья: Почему счетчики производительности ненадежны
Почему же ошибаются счетчики производительности и, в частности, индикатор загрузки процессора? Дело в том, что под «загрузкой процессора» создатели NT понимают отнюдь не загрузку процессора, как таковую, а готовность потока поделиться остатками кванта, отпущенного ему процессорного времени. На хакерских конференциях было продемонстрировано большое количество exploit’ов, отдающих ~3%-5% от полного кванта времени и заставляющих счетчик производительности отображать загрузку близкую к нулевой, чем с успехом пользуются многие зловредные программы, использующие распределенные сети для взлома паролей, например.
Другой источник ошибок — асинхронные операции ввода/вывода, поддерживаемые ядром. При чтении данных с диска или файла подкачки поток «замораживается» системой, передающей управление потокам этого же или другого процесса, в результате чего поток, инициирующий процесс чтения, показывает намного более низкое потребление процессорного времени, чем происходит в действительности. Строго говоря, индикатор загрузки не лжет. Поток действительно «спит» во время чтения с диска, но ведь нас интересует отнюдь не формальная сторона проблемы, а полное время, включающее в себя длительность всех операций ввода/вывода, поскольку интенсивный ввод/вывод — отличное средство торможения!
Наконец, счетчик загрузки ЦП не учитывает время, потребляемое драйверами. «Загрузка ядра» более или менее корректно вычисляется только для некоторых системных вызовов, да и то с кучей оговорок и ограничений.
Производительность сервера зависит не только от мощности аппаратной части, но и программной оснастки. Один «кривой» драйвер (приложение) способен сожрать все ресурсы, затормозив систему до уровня асфальтового катка, и системный монитор не покажет ровным счетом ничего! Как найти виновника?! Из множества программ самым доступным, точным и надежным инструментом, как ни странно, оказывается отладчик Soft-Ice.
Сплошь и рядом приходится сталкиваться с ситуацией, когда при нулевой (по индикатору) загрузке процессор работает во всю мощь или же наоборот, загрузка вплотную приближается к 100%, но процессор не выполняет никакой полезной работы, например, мотая холостой цикл или ожидая сигнала от внешних устройств. В такой ситуации необходимо менять не процессор, а криво написанное ПО.
INFO
-
С Soft-Ice в руках поиск «бутылочного горлышка» занимает буквально считанные минуты, причем результат абсолютно надежен.
-
Затор возникает, когда определенный узел компьютера (процессор, память, жесткий диск и т.п.) ограничивает поток данных, это можно сравнить с тем, как узкое горлышко ограничивает вытекающую из бутылки воду.
-
Увеличение объема оперативной памяти может быть действенным решением для некоторых видов заторов, но не решает всех проблем.
-
Заторы сети являются самыми сложными для обнаружения, т.к. сеть обычно состоит из каналов связи, коммутаторов, маршрутизаторов, серверов, клиентов, кучи протоколов и т.д.
Статья опубликована в июльском номере журнала «Xakep» за 2008 год.





