среда, 1 ноября 2017 г.

Windbg debugging series (1 of n)

Лирическое отступление

Давно я ничего не писал в блог, но тут несколько людей так или иначе спрашивали меня про Windbg и про его расширения, поэтому решил попробовать написать серию постов об отладке C/C++ кода в Windows с помощью отладчика Windbg, поделиться своим опытом использования windbg, а также услышать feedback и ваши истории успеха при использовании Windbg

Введение

Вообще, Windbg - очень мощный отладчик, причем насколько мощный, настолько и странный. Синтаксис его команд для меня до сих пор загадка - чтобы записать условный брейпоинт, я неизменно лезу в гугл или во встроенную помощь, благо, она довольно хорошая.

Еще одна сильная сторона в windbg - это возможность автоматизировать (скриптовать) свои действия - я уже раньше писал об этом немного в заметке "Парад планет" - вот это выражение
 bu hal!x86BiosCall ".echo {; r rdx; r rcx; r $t0 = rdx; dd @rcx L8; gu; r rax; dd @$t0 L8; .echo }; g" 
Создает условный брейкпоинт, при срабатывании которого:

  • выводится содержимое регистров RDX и RCX
  • запоминается значение регистра RDX в псведорегистр windbg $t0
  • продолжается выполнение до конца этой функции
  • распечатывает код возврата этой функции
  • распечатывается содержимое регистра $t0 при выходе. 
У меня получился примитивный трассировщик аргументов на входе и выходе (на выходе трассирую для детектирования возможной модификации аргументов), а также печать результата выполнения функции. Причем, я отлаживал не свою функцию, а функцию из ядра Windows. Я уже не помню деталей, для чего это было нужно, но сам факт того, что я могу получать информацию при входе в функцию и при выходе очень воодушевляет при анализе сложных дампов, падений, или просто непонятного поведения.

Также нужно помнить, что некоторые команды пишутся просто (например, kb), некоторые требуют точку перед именем (например, .effmach), а некоторые восклицательный знак (!analyze).  Единственное, что я из всего этого понял - команды, которые начинаются с ! - это команды расширений - для Windbg можно писать расширения, которые добавляют свои команды. Расширения позволяют выполнять различные рутинные задачи в процессе отладки, а также позволяют избегать ограничений встроенных windbg команд. Например, у нас в конторе есть свое расширение, которое, используя знания о нашем внутреннем фреймворке, позволяет искать объекты в памяти, искать последние события трассировки, строить граф взаимосвязей между объектами и т.д.

Ладно, это все была лирика, сейчас перейдем к сути, и начать я хотел с настройки windbg.

Подготовка к работе

Вообще windbg - это продукт, написанный программистами для програмистов, и именно поэтому он такой мощный, но в тоже время и такой у***щный - у меня просто нет других слов! Ну почему, скроллинг в дизассемблере пролистывает на целую страницу, так что я теряю последнюю просмотренную инструкцию, сбиваюсь, матерюсь, возвращаюсь обратно, на бумажке записываю адрес последней инструкции, и скролю еще раз!! Мне кажется, создатели windbg хотели опровергнуть все тезисы книги "Психбольница в руках пациентов" Алана Купера 😃.

Итак, чтобы продуктивно использовать windbg, нужно его сначала настроить, я расскажу свои рецепты настройки отладчика.

Workspace

В Windbg есть понятие workspace - это такая штука, которая позволяет сохранить различные рабочие окружения для разных типов задач. Например, для анализа дампов у меня есть свой workspace, в нем определенным образом размещены окна Command, Call Stack, Locals. Для kernel debug свое расположение окон, для отладки обычных приложений свое расположение. Причем эти workspace автоматически подхватываются, когда начинается та или иная работа - если открываю memory dump, то грузится один workspace, для kernel debug - другой. В общем, это довольно удобно. Еще полезная штука есть в меню Window -> Open Dock, открывается новое окно, в которое можно перенести окна, см скриншоты ниже.

Это главное окно windgb с иходниками и(или) дизассемблером,
автоматически загружен workspace для анализа дампов

А это вспомогательное окно с Command, CallStack, Locals
Самое главное, что нужно помнить при работе с workspace - как можно чаще нажимать File -> Save Workspace.

Command logging

Итак, с окнами разобрались, но есть еще одна очень полезная фича - трассировка (логирование) всех команд windbg с их выводом - это команды .logappend, .logopen, .logclose - они незаменимы при анализе сложных проблем. Пишется вся история анализа, выхлоп от всех команды, отладочная печать (DebugPrint), что очень помогает, при написании выжимки для отчета (в отчете можно указать ключевые моменты анализа). Кроме того, такой лог очень сильно помогает при повторном рассмотрении бага - например, если баг откладывался на неопределенное время, то когда к нему возвращаешься, можно быстро восстановить ключевые моменты анализа, просматривая этот файл.

Инсталляция отладчика

После нескольких лет использования windbg я пришел к следующей странной схеме - я НЕ инсталлирую отладчик. Вместо этого, я завел директорию, в которую складываю все версии отладчиков, примерно так:
В эту директорию я складываю все отладчики по версиям, и отдельно настроил Junction на активную версию отдачика. Я использую такой подход, потому что, во-первых, windbg разрешает так его использовать (его можно скопировать в отдельную директорию, и там запускать без всякой инсталляции). Во-вторых, чтобы обходить всякие странные баги самого отладчика - например, версия 10.0.15063.468 при анализе дампов от Windows 10 выводила на экран всякие странные сообщения, например "CompressedPageDataReader warning: failed to get _SM_PAGE_KEY symbol.", а иногда, некоторые команды и вовсе были сломаны. На скриншоте видно, что есть симлинк Debuggers - он как раз ссылается на нужную мне версию, а все мои батники и ярлыки уже используют этот симлинк.

При необходимости, я могу с помощью команд
rd Debuggers 
mklink /J Debuggers 10.0.15063.468\Debuggers
Обновить симлинк, и использовать нужную мне версию отладчика.

Еще раз повторюсь - такие танцы с бубном вокруг отладчика сделаны на основе моего опыта использования различных версий. Мой подход позволяет мне максимально безболезненно перейти на новую версию отладчика в моем рабочем окружении. Если у кого-то есть свои истории успеха, то прошу ими поделиться :)

Инсталляция расширений

Есть несколько директорий, куда можно устанавливать расширения - даже есть целая команда в Windbg. Я выбрал для себя путь C:\Users\[USERNAME]\AppData\Local\Dbg\EngineExtensions - что позволяет мне не зависеть от различных версий отладчика, и не вмешиваться в работу системы. На данный момент у меня установлены следующие расширения:
  • mex - расширение, которое предоставлется Microsoft
  • cmkd - расширение от CodeMachine - практически не использую
  • dbgkit - GUI расширение от Andrey Bazhan - практически не использую
Есть еще расширение wdbgark от  коллеги по работе Вячеслава Русакова, также есть PyKD - python расширение от другого коллеги - Александра Тарасенко - он даже делал доклад на C++ Russia 2016 о PyKD.

Заключение

В этой вводной статье я попытался рассказать вкратце о моем опыте подготовки Windbg для продуктивного использования. В следующих статьях я расскажу об используемых расширениях - это стандартные расширения windbg + MEX, также расскажу о некоторых интересных проблемах, которые были разрешены с помщью windbg.

Да, кстати, ребята из Microsoft хорошо потрудились над Windbg - сделали, для него новый GUI, зарелизили фичу Time Travel Debugging. Единственный нюанс - эта версия windbg распространяется через Windows Store - а у меня инсталляция заканчивается с ошибкой, поэтому я ничего не могу сказать про новый отладчик - это к слову о продукте от программистов для программистов :)



2 комментария:

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

    http://www.osronline.com/article.cfm?article=589
    http://eretik.omegahg.com/kd/cmd.html
    https://blogs.msdn.microsoft.com/ntdebugging/2011/07/20/updated-archive-of-the-debug-ninjas-twitter-debug-tips/

    Насчёт junction подход интересный, спасибо.

    ОтветитьУдалить
    Ответы
    1. Спасибо за ссылки. Мне кажется у каждого, кто долго пользовался Windbg, есть свой список любимых команд. И таких ссылок можно много найти. Проблема с ними заключается в том что начинающему пользователю абсолютно непонятно, что это значит, ну вот например:

      !process [address] 17 - Sets the context for this command, avoids the need for .process to see user stacks. Try !process 0 17

      Имхо, для начинающего windbg пользователя это описание ничем не поможет. Нужно знать, что есть user/kernel stack, что они могут быть различными (WoW64) и т.д. - это скорее напоминалки для продвинутых пользователей.

      В общем, к чему я клоню - в этой серии постов я пытаюсь рассказать приемы отладки, а уж сами команды - это дело десятое, но еще раз, спасибо за ссылки.

      Удалить