Использование расширения отладки SOS в приложении ASP.NET

В данной статье, на простейшем примере, постараюсь показать использование данного расширения, для получения сведений о работающем приложении ASP.NET. И о тех полезных возможностях, которые получит веб-разработчик используя расширение SOS в повседневной жизни. О том, что представляет собой SOS и как его использовать можно почитать тут. На самом деле термин "в повседневной жизни", звучит очень громко, так как можно быть хорошим веб-разработчиком и вообще не знать о SOS. Но если вы занимаетесь в основном разработкой Back-end части приложения, то нужно знать о таких вещах и иметь их на воружении. Хотя применение данных возможностей бывает необходимо не часто. В данной статье я не буду создавать тестовых приложений, но для того, чтобы попробовать это у себя, всё равно вам нужен запущенный процесс в котором будет хоститься приложение ASP.NET. Это может быть как процесс приложения ASP.NET Development Server, IIS Express или процесс w3wp.exe полноценного сервера IIS. Именно на последнем типе приложения я продемонстрирую возможности SOS. Причём в этот раз мы не будем использовать 32-битные приложения. Вместо этого подключимся к 64-битному процессу приложения ASP.NET. А поскольку Visual Studio не имеет возможности загружать 64-битную SOS, будем использовать Windows Debugging Tools. В частности отладчик WinDbg. Последнюю версию Windows SDK можно скачать отсюда (для Windows 8). Загрузив веб-установщик, можно выбрать один из двух вариантов: или установить сразу или загрузить, а потом только установить.



Можно установить только отладчик WinDbg, чтобы не устанавлиать весь пакет. Замечу сразу, что для того чтобы установить данный SDK вовсе не нужен Windows 8. У меня он установлен на машине c Windows 7 x64 SP1. На моей машине в данный момент хостятся несколько крупных проектов ASP.NET Web Forms 4.5, как можно догадаться работают они в IIS 7.5. Над одним из них я работаю в настоящее время (не я один, а целаяя команда разработчиков), и именно к процессу этого приложения (точнее к процессу пула в котором хостится это приложение) я подключу отладчик. Чтобы заставить приложение работать в режиме x64, нужно пул приложения настроить следующим образом:



Если конечно приложение скомпилировано в режиме Any CPU.



Как видно из нижнего рисунка приложение потребляет больше 200 МБ физической памяти, и примерно столько из файла подкачки.



Запускаем отладчик WinDbg от имени администратора и подключаемся к процессу w3wp.exe.



После загружаем SOS.



выполнив следующую команду.
.loadby sos clr
Основное преимущество использованяя WinDbg (кроме того, что можно подключаться к 64 битным приложениям) заключается в том, что не нужно ставить точки останова в исходном коде, подключаться к процессу приложения и уже только во время останова загружать SOS. Достаточно присоедениться к процессу и просто загрузить расширение SOS. Но стоит ещё добавить то, что в этот момент приложение не будет доступно. То есть не будет отвечать на запросы. Но если отсоеденится от процесса приложения, оно продолжит свою работу в нормальном режиме. Теперь посмотрим содержимое кучи, выполнив следующую команду.
!DumpHeap
Как видно из нижнего рисунка в куче порядка миллиона объектов. Показываются полные названия типов, общий размер и количество. Таким образом можно посмотреть общую картину использования памяти.




Теперь давайте посмотрим содержимое сессии. Для этого выполним команду.
!DumpHeap -type SessionStateItemCollection
После этого получим следующий вывод:



Рассмотрим содержимое объекта сессии поподробней, для этого выполним команду указав адрес объекта полученный высше.
!DumpObj 00000000ff7402e0
При этом получим такой вывод.



То есть содержимое сессии хранится в коллекции SessionStateItemCollection, которая хранит сами значения в хеш-таблице
private volatile Hashtable _entriesTable;
представленной закрытым полем базового класса _entriesTable. Но это всего лишь вершина айсберга, т.е. простая ссылка. Продвигаясь по адресам (ссылкам) можно пойти ещё дальше, и рассмотреть более детально содержимое объекта, но мы этого делать не будем. А чтобы получить размер объекта SessionStateItemCollection со всеми внутренними объектами, можем воспользоваться командой:
!ObjSize 00000000ff7402e0
указав адрес который имеем. Получим следующее.



Выходит размер сессии у меня не очень большой, и это так, потому что я туда ничего не записываю. Ещё одна полезная команда: !ThreadPool, выводящая информацию о количестве потоков в пуле, которые обслуживают (или выполняют) запросы к приложению.
0:027> !ThreadPool
CPU utilization: 22%
Worker Thread: Total: 3 Running: 0 Idle: 3 MaxLimit: 32767 MinLimit: 2
Work Request in Queue: 0
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 1 Free: 1 MaxFree: 4 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 2
Потоков в пуле тоже мало, так как у меня запущен всего один сеанс, но это важная информация. На этом всё. Это сново была демонстрация нескольких наиболее полезных команд, остальные можно посмотреть здесь. Да, есть много разных навороченных инструментов для анализа и профайлинга приложений, но вот о таких простых (но не в использовании) вещах не нужно забывать или не знать. Просто знайте, что подобные возможности есть и когда необходимо используйте их.