Почему минидампы не дают хороших стеков вызовов?

Я использовал минидампы во многих игровых проектах на протяжении многих лет, и, похоже, у них примерно 50% шанс иметь действительный стек вызовов. Что я могу сделать, чтобы у них были лучшие стеки вызовов?

Я пробовал поместить последнюю версию dbghelp.dll в каталог exe. Кажется, это некоторым помогает.

Visual Studio 2008 или 2010 лучше? (Я все еще на VS 2005).

Код, который я использую, выглядит как этот образец.


person Tod    schedule 12.10.2009    source источник


Ответы (5)


Одна вещь, которую вы можете сделать, чтобы повысить точность стеков вызовов, найденных в дампах, - это использовать отладчик, отличный от Visual Studio, в частности, использовать WinDbg или другой инструмент, который использует механизм отладки Windows Debugger, найденный в dbgeng.dll (в отличие от Механизм отладки отладчика Visual Studio, который использует Visual Studio).

По нашему опыту, WinDbg на 100% надежен в создании хороших стеков вызовов из тех же дампов, где Visual Studio создает непригодные или совершенно неточные стеки вызовов. Насколько я могу судить, в тех случаях, когда источником сбоя является необработанное исключение, WinDbg автоматически выполняет сложный процесс реконструкции / восстановления стека вызовов исключения, но Visual Studio этого не делает (или не может?). Два отладчика используют разными эвристиками для интерпретации стеков < / а>

Поначалу WinDbg может показаться сложным, поэтому вот мое краткое руководство о том, как упростить его или даже избежать необходимости использовать его напрямую.

Руководство простого смертного по извлечению хороших стеков вызовов

Они упорядочены от самого быстрого / самого простого к самому медленному / самому загадочному для интерпретации.

  1. Самый простой вариант: используйте DbgDiag от Microsoft

Это малоизвестный инструмент, который автоматизирует анализ часто встречающихся проблем, и его достаточно просто передать непрограммистам или даже клиентам. Он быстрый и почти надежный, и стал моим любимым инструментом для быстрого анализа входящего аварийного дампа.

  • Запустите приложение DebugDiag Analysis.
  • Установите флажок CrashHangAnalysis на главной странице.
  • Перетащите дамп на панель файлов данных на главной странице.
  • Нажмите Начать анализ.


Через несколько секунд или нескольких минут он выдаст красивый файл .mhtml, содержащий анализ проблемы, информацию обо всех связанных потоках, полных стеках вызовов и т. д. Все ссылки имеют гиперссылки и просты в использовании.

DebugDiag даже автоматизирует некоторые из более сложных анализов, которые возможны, но болезненны в WinDbg (например, отслеживание того, какой из 350 потоков в вашем приложении отвечает за тупик).

Примечание. Chrome не будет загружать и открывать файлы .mhtml по соображениям безопасности, поэтому вы должны открывать их в Internet Explorer или Microsoft Edge, чтобы его можно было использовать. Это раздражает, и я отправил запрос в команду DebugDiag ([email protected]) на изменение формата на простой HTML

  1. Средний вариант: установить WinDbg в качестве альтернативного механизма отладки для Visual Studio
  • Установите Visual Studio, если он еще не установлен. Это нужно сделать перед следующим шагом.
  • Установите Драйвер Windows). Комплект (WDK)
  • Запустите Visual Studio и (эта часть важна!) используйте новую опцию Файл - ›Открыть -› Crash Dump ..., чтобы открыть дамп. Это отладит аварийный дамп с помощью отладчика Windows (, если вместо этого вы перетащите дамп в Visual Studio или воспользуетесь стандартной опцией File - ›Open -› File ..., чтобы открыть дамп, он выполнит отладку он использует старый механизм отладки Visual Studio ... поэтому будьте осторожны, выбирая правильный вариант).
  • Теперь вы должны иметь возможность видеть правильный стек вызовов и перемещаться по нему с помощью графического интерфейса Visual Studio, хотя некоторые вещи работают по-другому (окна просмотра требуют использования незнакомого синтаксиса WinDbg, идентификаторы потоков другие и т. Д.). Примечание. Пользовательский интерфейс Visual Studio может работать очень медленно, особенно если задействовано много потоков и окна «потоки» или «параллельные стеки» открыты.
  1. Хардкорный вариант: напрямую использовать WinDbg
  • Запустите WinDbg.exe
  • Перетащите дамп в окно WinDbg.
  • Введите !analyze -v и нажмите Enter. Через некоторое время WinDbg выдаст стек вызовов сбоя, а также свою оценку источника проблемы. Если вы анализируете тупик, вы можете попробовать !analyze -v -hang, и WinDbg часто покажет вам задействованную цепочку зависимостей.


На этом этапе у вас может быть вся необходимая информация! Однако, если вы затем захотите проверить состояние процесса в отладчике Visual Studio, вы можете предпринять следующие дополнительные шаги:

  • Откройте аварийный дамп в Visual Studio
  • Щелкните правой кнопкой мыши в окне стека вызовов и выберите «Перейти к разборке».
  • Вставьте шестнадцатеричный адрес из верхней строки выходного стека вызовов WinDbg в адресную строку окна дизассемблирования и нажмите ввод. Теперь вы находитесь на месте аварии и смотрите на дизассемблированный код.
  • Щелкните правой кнопкой мыши в окне дизассемблирования и выберите «Перейти к исходному коду», чтобы перейти к исходному коду местоположения. Теперь вы смотрите на исходный код на месте крушения.

Примечание: все вышеперечисленное требует настройки правильных путей к серверу символов, иначе вы не сможете разрешить символы в стеках вызовов. Я рекомендую установить переменную среды _NT_SYMBOL_PATH, чтобы он был автоматически доступен для Visual Studio, WinDbg и DebugDiag.

person Chris Kline    schedule 28.10.2015
comment
Еще одно замечание: чтобы получить гораздо более приятный опыт отладки с аварийными дампами в режиме выпуска, есть специальный флаг компилятора, который вставляет дополнительную информацию в ваши PDB, что позволяет вам правильно выполнять оптимизированный код и видеть встроенные функции в стеке вызовов (и трассировки профилировщика!). Он был доступен как недокументированный флаг / d2Zi + в VS2010, а затем был изменен на официальный флаг / Zo в обновлении 3 VS2013. Для получения дополнительной информации см. randomascii.wordpress.com/2013/09/11/ - person Chris Kline; 12.08.2016

Чего не хватает в вашем стеке вызовов? У вас есть несколько адресов, которые не разрешаются в допустимые имена функций (например, 0x8732ae00 вместо CFoo: Bar ())? Если это так, то вам нужно разместить свои .PDB там, где ваш отладчик может их найти, или настроить symbol server и задайте« Пути к символам »в контекстном меню правой кнопки мыши на панели« Модули ».

Мы сохраняем каждый .PDB из каждого двоичного файла каждый раз, когда кто-то регистрирует новый список изменений Perforce, поэтому, когда дамп возвращается от кого-либо в офисе или от любого покупателя в рознице, у нас есть .PDB, соответствующий версии игры, в которой они были Бег. После установки сервера символов и путей все, что мне нужно сделать, это просто дважды щелкнуть .mdmp, и он будет работать каждый раз.

Или у вас есть стек вызовов, в котором есть только одна функция? Например, 0x8538cf00 без чего-либо в стеке над ним? Если это так, то ваш сбой на самом деле является повреждением самого стека. Если адреса возврата в backchain были перезаписаны, естественно, отладчик не сможет их разрешить.

Иногда вы также обнаруживаете, что поток, который на самом деле генерирует минидамп, не является тем, который вызвал исключение, вызвавшее сбой. Посмотрите в окне потоков, чтобы увидеть, есть ли в одном из других потоков неправильный код.

Если вы отлаживаете сборку «Release», то есть сборку, скомпилированную со всеми включенными флагами оптимизации, вам придется смириться с тем фактом, что отладчику будет сложно найти локальные переменные и некоторые другие данные. Это связано с тем, что включение оптимизации означает разрешение компилятору хранить данные в регистрах, свертывания вычислений и, как правило, выполнять различные действия, предотвращающие фактическую запись данных в стек. Если это ваша проблема, вам нужно открыть окно дизассемблирования и отслеживать данные вручную или перестроить отладочный двоичный файл и воспроизвести проблему там, где вы можете посмотреть на нее.

person Crashworks    schedule 12.10.2009
comment
0x8732ae00 - маловероятный адрес, он находится в пространстве ядра (с настройкой 2 ГБ x86-32). Адреса 0x7_______ встречаются чаще, потому что DLL Windows прижимается к границе 2 ГБ. Это сокращает количество необходимых переездов. Если вы не видите их символов, используйте сервер символов Microsoft. - person MSalters; 12.10.2009
comment
Например, я просто вытаскивал адреса наугад (в данном случае именно здесь консоль телеприставки любит перемещать библиотеки DLL пользовательского режима). - person Crashworks; 12.10.2009

Отключите оптимизацию указателя фрейма, если вам нужны дампы стека. Указатели кадров используются для явного определения кадров стека. Без них отладчик должен определять местоположение каждого кадра.

person MSalters    schedule 12.10.2009
comment
Это хорошая идея. Однако с PDB и исходными DLL отладчик MSVC может обрабатывать кадры стека с помощью FPO в любом случае, но, конечно, его работа становится намного сложнее. Я знаю это, потому что мы компилируем с использованием FPO, и я все время получаю стеки из минидампов. - person Crashworks; 12.10.2009
comment
Конечно, это просто, если сбой произошел из-за ручной точки останова INT 3. Проблема в том, что большинство сбоев происходит не в точном соответствии с ошибочной инструкцией. ЦП некоторое время спотыкается, пока не сработает ошибка. Между тем, исполняемый код работает не так, как задумано, и может немного повредить состояние программы. Это может включать выполнение инструкций, которые вы не собирались выполнять (особая неприятность: косвенные переходы через неверно интерпретированную vtable). vtablecourse - person MSalters; 12.10.2009
comment
Да, отсутствие указателей кадра определенно значительно усложняет задачу ручного поиска по стеку. Даже если программа умерла, перескочив через дикий указатель vfunc, вы обычно можете выяснить, откуда он взялся, потому что операция CALL помещает IP в стек, но найти его и затем определить, куда ушли все местные жители, может стать трудным упражнением. работая в обратном направлении, по одной операции за раз. Если вы все же окажетесь наверху этого ручья, у windbg есть полезная команда dps, которая ищет в памяти вероятные известные символы и адреса функций; это может помочь вам в поисках старого EIP. - person Crashworks; 12.10.2009

Код для записи минидампа вряд ли будет актуален. Основное, что записывает минидамп, - это информация о модуле (для получения символов) и полное содержимое всех стеков потоков. Помимо этой основной информации (которая всегда записывается) ничего не имеет значения.

Получение хороших символов (включая PE-файлы) имеет решающее значение для обхода стека. Более подробную информацию можно найти здесь: https://randomascii.wordpress.com/2013/03/09/symbols-the-microsoft-way/

Я считаю, что Visual Studio обычно надежно отображает стеки вызовов. Он автоматически отображает соответствующий стек вызовов из записи об исключении и упрощает изменение потоков, чтобы вы могли видеть стеки вызовов всех потоков. Иногда он пытается «скрыть» детали, которые, по его мнению, могут вас запутать - хорошо это или плохо, зависит от вашего уровня навыков.

По умолчанию Windbg показывает стек вызовов кода, который записал аварийный дамп, а не сбойный стек вызовов. Windbg требует, чтобы вы указали ".ecxr" или "! Analysis -v", чтобы увидеть стек сбоев. Меня это раздражает. Windbg также требует дополнительных настроек, чтобы быть полезным.

У двух отладчиков разные эвристики обхода стека. Эти эвристики необходимы, например, если вы вызываете или возвращаетесь на нулевой адрес, так как для этого адреса нет информации о раскручивании. Для «чистых» сбоев, когда инструкция с ошибкой находится в нормальном коде, эти эвристики менее важны.

Перемещение стека почти наверняка улучшилось за последние десять лет. VS 2015 Community Edition очень функциональна и бесплатна, так что вы можете попробовать ее.

Если вы используете windbg, то можете попробовать поэкспериментировать:

!vc7fpo - toggles some of the windbg heuristics.
!stackdbg d, 7, f - turns on windbg stack walk
k1 - walks one level of the stack, spitting diagnostics as controlled by !stackdbg
dds esp - dumps the raw contents of the stack, doing a symbol lookup on each pointer

Если вы обновляетесь до VS 2015 и по-прежнему испытываете проблемы, вполне вероятно, что сбои при обходе стека являются специфическими для сбоев, которые вы наблюдаете. Если переполнение буфера приведет к выходу из строя стека, то стек вызовов будет безвозвратно поврежден. В вашем вопросе слишком мало информации о том, какие неисправности вы наблюдаете, чтобы поставить окончательный диагноз. Я считаю, что отображение стека обоих отладчиков довольно надежно, но я также обычно понимаю, почему они иногда выходят из строя, и когда это происходит, я все еще могу извлечь нужную мне информацию.

person Bruce Dawson    schedule 02.11.2015

Я не использую минидампы, а просто выгружаю стек «вручную» в файл журнала (см. www.ddj.com / cpp / 185300443 и Как регистрировать кадры стека в Windows x64).

Я сталкиваюсь с похожим поведением, как и вы: иногда есть действующий стек вызовов, иногда нет. В незначительном количестве случаев стек может быть действительно поврежден. Возможно, в 1/3 всех случаев установленный обработчик исключений вообще не вызывается! Я предполагаю, что это как-то проблема обработки исключений, структурированных в Windows.

person RED SOFT ADAIR    schedule 12.10.2009