Чтение крэш-дампов с WinDbg + SOS ── практическое введение в анализ после сбора
· Го Комура · WinDbg, SOS, Дамп сбоя, .NET, CSharp, Отладка, PDB, Расследование сбоев, Техническая консультация
В предыдущей статье «Введение в сбор крэш-дампов Windows» мы разобрали, как «собрать» дамп с помощью WER LocalDumps, ProcDump и MiniDumpWriteDump. Но сам по себе собранный дамп ничего не рассказывает. Он становится материалом для расследования только тогда, когда вы реально засучиваете рукава и разбираетесь, какой поток упал, почему упал или что именно удерживает память.
В этой статье, как продолжение сбора, мы сосредоточимся исключительно на том, как реально читать собранный дамп в WinDbg с расширением SOS. Речь пойдёт об установке и настройке символов, о загрузке расширения SOS, обязательного для .NET-приложений, о том, «на что смотреть и как делать выводы» с помощью характерных команд вроде !clrstack и !dumpheap -stat, о !analyze -v для нативных сбоев, а также о том, когда вместо WinDbg стоит использовать dotnet-dump analyze.
1. Сначала вывод
- Главный инструмент анализа дампов - WinDbg (актуальная версия, ранее называвшаяся WinDbg Preview). Его можно получить через
winget install Microsoft.WinDbgили из Microsoft Store, он работает на x64/ARM64 начиная с Windows 10 Anniversary Update (1607) и на Windows 11.1 - Даже без загруженных символов (PDB) команды
!clrstack,!dumpheap -stat,!gcrootи подобные работают напрямую с метаданными CLR и данными кучи. Теряются только имена исходных файлов и номера строк managed-кода, а также имена символов для нативных фреймов. Впрочем, если нужно дойти до конкретной строки исходника, это уже другой разговор, и стандартная практика - указать в_NT_SYMBOL_PATHи публичный сервер символов Microsoft, и расположение собственных PDB.2 - Для дампов .NET-приложений (Framework / Core / 5+) managed-информация становится видна только после загрузки расширения SOS. Одной лишь нативной командой
k(вывод стека) код на C# не отследить.3 - Есть три типовых сценария расследования. При падении из-за исключения -
!clrstack→!pe, при постоянном росте памяти -!dumpheap -stat→!gcroot, при нативном сбое -!analyze -v. - Если хочется обойтись без WinDbg, есть вариант
dotnet-dump analyze. Большинство команд SOS работают в нём как есть, но нативные стековые фреймы не обрабатываются. Для чисто managed-расследований, где не задействованы нативные DLL или COM, это более лёгкий в установке вариант.4 - Описанные здесь шаги - это «как читать», а не «как собирать». О способах получения дампа (WER / ProcDump /
MiniDumpWriteDump) - в статье про сбор, а о проектировании сопоставления логов и дампа в момент сбоя - в статье «Проектирование, при котором логи и дампы сохраняются в момент сбоя».
2. Ставим WinDbg и настраиваем символы
2.1 Установка
Актуальную версию WinDbg можно поставить одним из следующих способов.1
winget install Microsoft.WinDbg
Через Microsoft Store устанавливается тот же самый движок с идентичными командами, расширениями и рабочим процессом. После установки обновление происходит автоматически (в фоне - для установки через Store или напрямую, либо командой winget upgrade Microsoft.WinDbg - при установке через winget), так что разница в поведении между версиями почти не беспокоит.1
2.2 Открываем дамп
windbg -z C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
-z - опция, указывающая файл дампа для открытия при запуске. То же самое можно сделать из GUI через «File > Open Dump File».
2.3 Настраиваем путь к символам
Место, где отладчик Windows ищет файлы символов (PDB), задаётся переменной окружения _NT_SYMBOL_PATH либо командой .sympath внутри сессии.2 На практике базовый вариант - указать одновременно публичный сервер символов Microsoft и расположение собственных PDB.
.symfix C:\Symbols\Microsoft
.sympath+ C:\Symbols\MyApp
.reload
.symfix- это ярлык, задающий путь к публичному серверу символов Microsoft (https://msdl.microsoft.com/download/symbols) вместе с указанным локальным кэшем. Символы для стандартных DLL ОС загружаются оттуда автоматически.5.sympath+дописывает к существующему пути расположение собственных PDB. PDB для своего кода нужно готовить самостоятельно - на сервере символов Microsoft их нет..reloadперезагружает символы и позволяет проверить их состояние в списке модулей.
Для постоянной настройки через переменную окружения используется такая форма. Она удобнее для автоматического анализа на CI или сборочных серверах.
set _NT_SYMBOL_PATH=srv*C:\Symbols\Microsoft*https://msdl.microsoft.com/download/symbols;C:\Symbols\MyApp
Правильно ли читаются символы, можно проверить командой lm (loaded modules), выведя список модулей и посмотрев, стоит ли у нужного модуля статус pdb symbols. Если он остался deferred, символы ещё не разрешены.
3. Загружаем расширение SOS
Для дампов .NET-приложений одних только нативных команд WinDbg недостаточно, чтобы увидеть содержимое managed-кучи, стековые фреймы C# и содержимое объектов исключений. Этот пробел закрывает расширение SOS (Son of Strike). Через команды SOS можно исследовать кучу, обнаруживать повреждение кучи, выводить внутренние типы данных runtime и оценивать состояние выполняющегося managed-кода.3
3.1 Разница между рантаймами
От того, .NET Framework перед вами или .NET (Core) / .NET 5+, зависит, какой рантайм нужно загрузить и откуда берётся SOS.
| Цель | Модуль рантайма | Команда загрузки |
|---|---|---|
| .NET Framework | clr.dll |
.loadby sos clr |
| .NET Core / .NET 5+ | coreclr.dll |
.loadby sos coreclr |
.loadby - команда, которая ищет DLL расширения (sos.dll) в той же директории, что и указанный модуль (clr или coreclr), и загружает её оттуда. Преимущество в том, что она надёжно подхватывает версию SOS, соответствующую окружению, в котором был снят дамп, без необходимости вводить полный путь.6
Начиная с версии 10.0.18317.1001 WinDbg и cdb, если отладчик обнаруживает, что целевой процесс загрузил coreclr.dll (или libcoreclr.so на Linux/macOS), расширение для .NET загружается автоматически из Microsoft Extension Gallery.6 Команда .loadby, приведённая выше, нужна для случаев, когда автозагрузка не срабатывает, или при использовании более старой версии отладчика.
3.2 Если SOS не находится
В окружениях, где автозагрузка не работает, SOS можно установить локально с помощью инструмента dotnet-sos.
dotnet tool install --global dotnet-sos
dotnet-sos install
После установки его также можно загрузить вручную прямо в WinDbg следующим образом (для старых отладчиков это может потребоваться).7
.load %USERPROFILE%\.dotnet\sos\sos.dll
3.3 Проверяем, что загрузка прошла
!sos.help
Либо можно попробовать !Threads для целей на базе Core или !sosstatus для целей на базе Framework - если команда не завершается ошибкой и что-то возвращает, загрузка прошла успешно. Если команда здесь падает с ошибкой вроде Unable to find module, почти наверняка причина - несовпадение пути к символам или рантайма (например, разница разрядности или версии между окружением, где снимался дамп, и локальным рантаймом). Застрять именно здесь, ещё до команд из следующей главы, - не редкость и на практике.
4. Читаем исключения и стек ── !clrstack и !pe
Первый шаг для дампа, упавшего из-за необработанного исключения.
!threads
Сначала через !Threads (в среде lldb - псевдоним clrthreads) выводим список managed-потоков и проверяем колонку Exception для каждого.8 Если у какого-то потока есть исключение, переключаемся на него.
~5s
!clrstack
!CLRStack выводит трассировку стека только для managed-кода.9 Если нужно увидеть ещё и аргументы с переменными, добавляется -a (ярлык, объединяющий -l и -p).
!clrstack -a
- Если видны методы собственного кода, по ним сразу читается, «где» и «по какому пути вызовов» произошло падение. Появление имени исходного файла и номера строки зависит от того, правильно ли загружены символы (глава 2).
CLRStackперечисляет managed-фреймы напрямую из метаданных CLR, поэтому наличие или отсутствие символов никак не влияет на то, отобразится ли фрейм. При нехватке символов теряются только имя исходного файла и номер строки - сами фреймы никогда не пропускаются.9 - Если не видно ни одного фрейма собственного кода, дело не в нехватке символов, а в одном из следующего: выбран не тот поток, у которого нет исключения (ошибка выбора потока); сбой произошёл целиком на нативной стороне, и managed-фреймов там попросту нет; либо тип дампа (например, Mini) не содержит на этот момент достаточно информации о стеке.
Далее смотрим сам объект исключения.
!pe
!PrintException (сокращённо !pe) без указанного адреса выводит последнее исключение, брошенное в текущем потоке. Можно получить имя типа, сообщение, внутренние исключения (отображаются через -nested) и даже строку трассировки стека.10 Чем меньше говорит само по себе имя типа - как у System.NullReferenceException, - тем сильнее нужно сверять его со значениями локальных переменных, видимыми через !clrstack -a.
5. Отслеживаем кучу и утечки ── !dumpheap -stat и !gcroot
Эти команды - центральные для расследований в духе «память понемногу растёт, и через часы или дни всё падает». Предварительный этап - отличение ожидания GC от настоящей утечки - подробно разобран в статье «Как отличить ожидание GC от утечки памяти в .NET». Эта статья - её продолжение, в части, где мы читаем один дамп и докапываемся до того, что именно удерживает память.
!dumpheap -stat
Опция -stat выводит только статистическую сводку по managed-куче. Типы выводятся примерно в порядке убывания количества экземпляров и суммарного размера, так что сначала можно выявить «тип, который давит объёмом».11 На практике часто встречаются два варианта:
- Растёт сам бизнес-класс (например, сотни тысяч экземпляров
MyApp.Models.Customer) - где-то есть сильная ссылка, продолжающая его удерживать - Аномально много только
System.Stringили массивов - часто это внутренние данные бизнес-класса, стоящего выше в списке; обычно быстрее сначала заподозрить сам бизнес-класс, чем сразу разбирать отдельные экземпляры
После того как цель сужена, берём адреса отдельных экземпляров.
!dumpheap -type MyApp.Models.Customer
И затем выясняем, почему этот объект так и не был собран сборщиком мусора.
!gcroot 000001a2b3c4d5e0
!GCRoot ищет по всей managed-куче и таблице хендлов и перечисляет корни (переменные на стеке, статические поля, GC-хендлы и т. д.), через которые можно дойти до указанного объекта.12 Если в выводе видно статическое поле, используемое как кэш, или подписку на обработчик события, это первый подозреваемый на незакрытую подписку. Типичный пример такого рода кода - «оставлено в кэше без освобождения»:
public static class CustomerCache
{
// Статический словарь без пути очистки — только растёт
private static readonly Dictionary<int, Customer> _cache = new();
public static void Add(Customer c) => _cache[c.Id] = c;
}
Если в выводе !gcroot появляется статический контейнер вроде CustomerCache, это повод на стороне кода задуматься о сроке жизни записей, ограничении числа элементов или переходе на WeakReference.11
6. Автоматический анализ нативных сбоев ── !analyze -v
Для нативных сбоев (нарушения доступа к памяти и т. п.), связанных с DLL на C++, COM или SDK стороннего производителя, начинать нужно с этой команды.
!analyze -v
!analyze - команда расширения, выполняющая автоматический анализ сбоя или исключения; -v включает подробный вывод.13 В выводе стоит обратить внимание в первую очередь на три поля:
EXCEPTION_CODE/BUGCHECK_STR: какого рода аномалия произошла (нарушение доступа, переполнение стека и т. п.)FAULTING_IP/FOLLOWUP_IP: адрес инструкции, на которой реально произошло падение, и соответствующий модуль/имя функцииMODULE_NAME/IMAGE_NAME: собственный ли это модуль или DLL стороннего производителя
Если падение происходит не в собственном модуле, а внутри DLL вендора, чтобы продвинуться дальше, нужен PDB этого вендора (обычно недоступный). На практике реалистичный итог расследования - подняться до места вызова (последних аргументов, переданных собственным кодом) и проверить, не были ли переданные значения некорректными. Поле STACK_TEXT в выводе !analyze -v покажет, что именно было вызвано из собственного кода непосредственно перед падением.
!analyze можно применять и к дампам, не связанным с исключением. Если подозревается зависание, выбрав нужный поток, выполните следующую команду - она проанализирует отношения блокировки между потоками.
!analyze -hang
7. Вариант без WinDbg ── dotnet-dump analyze
Если нужно исследовать только managed-код .NET Core / .NET 5+ (без нативных DLL и COM), есть более лёгкий, чем WinDbg, вариант - dotnet-dump.
dotnet tool install --global dotnet-dump
dotnet-dump analyze C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
Подкоманда analyze открывает интерактивную сессию с предустановленным SOS, где большинство представленных здесь команд - clrstack, dumpheap, gcroot и другие - можно использовать напрямую, без префикса !.4
Ориентир для выбора между инструментами такой:
| Аспект | WinDbg + SOS | dotnet-dump analyze |
|---|---|---|
| Нативные стековые фреймы | Видны | Не видны (только managed)4 |
Автоматический нативный анализ через !analyze -v |
Доступен | Недоступен |
| Дампы с Linux | Можно анализировать через WinDbg на Windows (для x64-дампов - x64-версия, для Arm64-дампов - x64-версия, для x86-дампов - x86-версия) | Поддерживается (нужен инструмент той же разрядности, что и платформа)14 |
| Дампы с macOS | Не поддерживается (поддержка Linux-дампов в WinDbg не включает macOS) | Поддерживается (.NET 5 и новее)4 |
| Лёгкость установки | Инсталлятор или winget | Одна команда dotnet global tool |
| Встраивание в кросс-платформенный CI | Требует усилий | Проще |
Практическая граница выбора: если «может быть замешан COM, P/Invoke или нативные DLL» - WinDbg; если это «расследование утечки памяти в чисто managed-коде, которое также должно работать в CI и на разных платформах» - dotnet-dump analyze. Оба инструмента используют общий набор команд SOS, поэтому команды, освоенные в одном, почти без изменений переносятся в другой. Учтите: если приходится иметь дело с дампом, снятым на macOS, WinDbg вообще не вариант, так что остаётся только dotnet-dump (или LLDB).
8. Без читаемых символов ничего не начинается
Часть описанных выше шагов вполне работоспособна и без корректно загруженных символов (PDB). Как отмечалось в главе 4, !clrstack, !dumpheap -stat и !gcroot читают метаданные CLR и данные кучи напрямую, поэтому сами фреймы и информация о типах отображаются даже без PDB. При отсутствии PDB теряются имена исходных файлов и номера строк managed-кода, а также имена символов для нативных фреймов и нативных модулей (вместо имени функции отображается только адрес). В ситуации, когда под рукой только дамп и исполняемый файл и хочется для начала просто понять, что случилось, вполне нормально начать с !threads → !clrstack, не увязая заранее в поисках PDB. Тем не менее, если нужно дойти до строки исходника, чтобы точно локализовать причину, это уже другой разговор. В главе 2 мы добавили путь к собственным PDB через .sympath+, но на практике простое «неизвестно, где лежит PDB, соответствующий распространённому EXE/DLL» чаще, чем можно предположить по материалу статьи про сбор, останавливает расследование до того, как удаётся дойти до строки исходника.
О том, что именно содержит и не содержит PDB, о Portable PDB и о Source Link (механизме, который встраивает в сборку метаданные системы контроля версий, позволяя отладчику напрямую получать исходный код на момент соответствующего коммита), подробно рассказано в отдельной статье «Что такое PDB».15 Если анализ дампов встраивается в постоянную эксплуатацию, хранение PDB для каждой сборки и включённый Source Link - подготовка, не менее важная, чем сама настройка сбора дампов. Пренебрежёте этим - и вывод !clrstack вообще не покажет строк исходника, оставив вас с адресами и именами типов в качестве единственных зацепок.
9. Практический пример ── анализ дампа при расследовании утечки хендлов
Ранее написанная статья «Расследование краша промышленной камеры при длительной работе — часть про утечку хендлов» описывает расследование приложения управления промышленной камерой, которое внезапно падало после долгих часов работы, и главным виновником там оказалась не утечка памяти, а утечка хендлов. В расследованиях такого рода дамп оказывается полезен при следующем сочетании:
- Через
!dumpheap -statподтверждаем, что managed-куча в порядке (количество и размер по типам не растут бесконтрольно) - Если при этом количество хендлов процесса всё равно растёт, можно заключить, что утечка - это не managed-объекты, а OS-хендлы (файлы, события, хендлы, которые SDK камеры выделяет внутри себя, и т. п.)
- Если ещё жив managed-объект-обёртка, держащий
SafeHandle, через!gcrootотслеживаем его GC-корень и находим место, где ссылка сохраняется там, где её следовало бы освободить
Иными словами, !dumpheap -stat работает как точка ветвления - определяет, идёт ли речь о росте managed-кучи, - а как только выясняется, что это не так, центр расследования смещается к инструментам обнаружения аномалий на нативной границе, вроде Application Verifier. О том, как строить такую базу для тестирования аварийных сценариев, рассказано в статье «Строим базу для тестирования аварийных сценариев Windows с Application Verifier». Анализ дампа отвечает за «состояние, в котором всё находится прямо сейчас», а Application Verifier - за «воспроизведение аномалии заранее»; использование обоих вместе - стандартная практика при расследовании сбоев после длительной работы.
10. Итог
Читать крэш-дамп учатся дольше, чем собирать, но типовых сценариев не так уж много.
- Ставим WinDbg и прописываем в пути к символам одновременно публичный сервер символов Microsoft и собственные PDB (глава 2)
- Для .NET-приложения загружаем расширение SOS (
.loadby sos clr/.loadby sos coreclr, либо полагаемся на автозагрузку. Глава 3) - Начинаем с
!clrstack→!peпри исключении,!dumpheap -stat→!gcrootпри росте памяти,!analyze -vпри нативном сбое (главы 4-6) - Если задача - чисто managed-расследование, стоит рассмотреть более лёгкий
dotnet-dump analyze(глава 7)
И фундаментом для всего этого служит управление PDB и символами. Если решить вопрос хранения PDB для каждой сборки и включения Source Link одновременно с настройкой сбора дампов, время расследования при реальном инциденте изменится очень сильно. Если самостоятельный анализ затруднён или на него нет времени, вы всегда можете прислать нам дампы и логи целиком - мы проведём анализ сами.
Похожие статьи
- Введение в сбор крэш-дампов Windows - WER/ProcDump/WinDbg
- Проектирование, при котором логи и дампы сохраняются в момент сбоя Windows-приложения
- Как отличить ожидание GC от утечки памяти в .NET
- Что такое PDB (Program Database)
- Расследование краша промышленной камеры при длительной работе — часть про утечку хендлов
- Строим базу для тестирования аварийных сценариев Windows с Application Verifier
Смежные направления консультаций
Komura Software LLC занимается расследованием причин сбоев на стыке дампов и логов, локализацией отказов, возникающих только после длительной работы, а также консультациями по проектированию самого процесса хранения и анализа дампов и PDB.
Справочные ссылки
-
Microsoft Learn, Install the Windows debugger. Об установке WinDbg через winget или Microsoft Store, поддерживаемых версиях ОС (Windows 10 1607 и новее, Windows 11) и архитектурах (x64, ARM64), а также о поведении автообновления. ↩ ↩2 ↩3
-
Microsoft Learn, Symbol path for Windows debuggers. О настройке пути к символам через переменную окружения
_NT_SYMBOL_PATHи о задании пути по умолчанию к публичному серверу символов командой.symfix. ↩ ↩2 -
Microsoft Learn, SOS debugging extension. О том, что расширение SOS используется для сбора информации о managed-куче, обнаружения повреждений кучи и вывода внутренних типов данных рантайма, а также о том, что синтаксис в WinDbg -
![command]. ↩ ↩2 -
Microsoft Learn, Dump collection and analysis utility (dotnet-dump). О том, что
dotnet-dump analyzeпредоставляет интерактивную сессию, в которой команды SOS работают напрямую, что из-за отсутствия статуса нативного отладчика он не может отображать нативные стековые фреймы, и что поддержка macOS требует .NET 5 или новее. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Microsoft public symbol server. О синтаксисе пути к символам вида
srv*DownstreamStore*https://msdl.microsoft.com/download/symbolsи о настройке с локальным кэшем через.symfix. ↩ -
Microsoft Learn, Debugging Managed Code Using the Windows Debugger. О том, что рантайм .NET Framework - это
clr.dll, а рантайм .NET Core/.NET 5+ -coreclr.dll, о загрузке расширений из соседней директории через.loadbyи об автозагрузке в WinDbg версии 10.0.18317.1001 и новее. ↩ ↩2 -
Microsoft Learn, SOS installer (dotnet-sos). Об установке расширения SOS локально через
dotnet-sos installи о команде ручной загрузки для старых версий отладчика. ↩ -
Microsoft Learn, SOS debugging extension - Commands. О том, что команда
Threads(с псевдонимомclrthreadsв среде lldb) выводит список ID каждого потока, домена, последнего брошенного исключения и прочего. ↩ -
Microsoft Learn, SOS debugging extension - Commands. О том, что команда
CLRStackвыводит трассировку стека только для managed-кода, что опция-aвыводит и локальные переменные, и аргументы, и что символы (SYMOPT_LOAD_LINES) влияют только на отображение имени исходного файла и номера строки, но не на отображение самих фреймов. ↩ ↩2 -
Microsoft Learn, SOS debugging extension - Commands. О том, что команда
PrintException(pe) без указания адреса выводит последнее исключение, брошенное в текущем потоке, и что-nestedтакже выводит вложенные исключения. ↩ -
Microsoft Learn, Debug a memory leak in .NET и Dump collection and analysis utility (dotnet-dump) - Analyze memory leaks and allocations. О статистическом выводе количества и суммарного размера по типам через
dumpheap -statи о том, как строить расследование от этой точки. ↩ ↩2 -
Microsoft Learn, SOS debugging extension - Commands. О том, что команда
GCRootищет по всей managed-куче и таблице хендлов и перечисляет ссылки (корни) на указанный объект. ↩ -
Microsoft Learn, Using the !analyze Extension и !analyze (WinDbg). Об автоматическом анализе сбоя/исключения через
!analyze -v, о значении полей вывода вродеFAULTING_IPиMODULE_NAME, а также о!analyze -hangдля расследования зависаний. ↩ -
Microsoft Learn, Debug Linux dumps. О том, что дампы Linux можно анализировать на Windows с помощью WinDbg или dotnet-dump, и что нужно использовать версию инструмента, соответствующую разрядности (x64/Arm64/x86) окружения, в котором был снят дамп. ↩
-
Microsoft Learn, Source Link. О механизме, при котором в момент создания NuGet-пакета в сборку встраиваются метаданные системы контроля версий, позволяя отладчику напрямую обращаться к исходному коду на момент сборки. ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Спящий режим, гибернация, Modern Standby и долго работающие приложения — как проектированием предотвратить «остановилось ночью»
Разбираем, почему долго работающее Windows-приложение оказывается «остановленным к утру», начиная с различий между спящим режимом S3, гиб...
MAX_PATH и подводные камни путей/имён файлов в Windows — лимит 260 символов, зарезервированные имена, конечная точка, регистр
Разбираем ограничения путей и имён файлов, которые часто стоят за классической ошибкой «файл не найден». Рассматриваем состав лимита MAX_...
Подводные камни сетевых дисков и UNC-путей ── как бизнес-приложения работают с файловым сервером (общей папкой)
Разбираем типичные проблемы, возникающие при выводе данных и мониторинге общей папки из бизнес-приложения: почему буква диска (Z:) не вид...
Практическое руководство по Process Monitor (ProcMon) — как за 10 минут выяснить, почему «настройки не читаются» или возникает ACCESS DENIED
«Исправил конфигурационный файл, но изменения не применяются», «вчера всё работало, а сегодня приложение не запускается» — прежде чем лез...
Интернационализация приложений WinForms/WPF — resx, сателлитные сборки и переключение культуры на практике
Разбираем интернационализацию десктопных приложений Windows на практике: различие между CurrentCulture и CurrentUICulture, устройство рес...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- С какой команды начинать чтение крэш-дампа .NET-приложения?
- Есть три типовых сценария. Если приложение упало из-за необработанного исключения, сначала через !threads находим поток с исключением, затем через !clrstack выводим managed-стек и через !pe смотрим тип, сообщение и внутренние исключения объекта. Если расследуется постоянный рост памяти, начинаем с !dumpheap -stat, чтобы найти тип, занимающий больше всего места, а затем через !gcroot выясняем, какие корни (статические поля, подписки на события) удерживают объект. При нативном сбое отправная точка - автоматический анализ !analyze -v.
- Как загрузить расширение SOS в WinDbg?
- Для .NET Framework - .loadby sos clr, для .NET Core/.NET 5+ - .loadby sos coreclr. Начиная с версии 10.0.18317.1001 WinDbg автоматически загружает SOS, как только обнаруживает, что целевой процесс загрузил coreclr.dll. Если автозагрузка не срабатывает, можно установить SOS локально через инструмент dotnet-sos и загрузить его вручную командой .load. Успешность загрузки проверяется тем, что !sos.help или !Threads отрабатывают без ошибки и что-то возвращают.
- Можно ли анализировать дамп без PDB (символов)?
- В какой-то мере да. Команды !clrstack, !dumpheap -stat и !gcroot читают метаданные CLR и данные кучи напрямую, поэтому фреймы и информация о типах отображаются даже без PDB. Теряются только имена исходных файлов и номера строк managed-кода, а также имена символов для нативных фреймов. Если нужно дойти до конкретной строки исходного кода, в путь к символам нужно добавить и публичный сервер символов Microsoft, и расположение собственных PDB. Для регулярной работы важно хранить PDB для каждой сборки и включать Source Link.
- Как выбирать между dotnet-dump analyze и WinDbg?
- Если может быть замешан COM, P/Invoke или нативные DLL - WinDbg, если расследование касается чисто managed-кода - ориентир на dotnet-dump analyze. dotnet-dump устанавливается одной командой dotnet global tool, и в нём напрямую доступно большинство команд SOS вроде clrstack и dumpheap, но нативные стековые фреймы он не обрабатывает и !analyze -v не поддерживает. Дампы, снятые на macOS, WinDbg анализировать не может, так что там остаётся только dotnet-dump или LLDB. Оба инструмента используют общий набор команд SOS, поэтому освоенные команды почти без изменений переносятся из одного в другой.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки