Возможно, вы уже слышали о том, что Microsoft выпустила обновлённую версию своего известного отладчика WinDbg, который и раньше был хорош, но слишком уж отстал по интерфейсу от современных тенденций. Новая версия WinDbg, к счастью, не пошла настолько далеко, чтобы получить новомодный UWP-интерфейс, но вот классические риббон-бары в стиле Microsoft Office — ей очень идут. Приложение распространяется только через Microsoft Store и работают на Win10 как минимум с Anniversary Update. Microsoft говорит, что это сделано для удобства установки и обновления, но я как-то не помню, чтобы с классическим WinDbg были какие-то проблемы с установкой. Скорее это выглядит как ещё один способ приучения разработчиков и пользователей к привычке пользоваться только самой последней версией Windows. Ну ок, пусть так.
WinDbg выглядит симпатично
И вся его мощь в виде команд, отладки драйверов, удалённой отладки, скриптов и прочего — осталась при нём. Более того, 25 сентября было выпущено обновление, добавляющее в новый WinDbg важную фичу — отладку с возможностью двигаться по ходу работы программы в обратном направлении (Time Travel Debugging). Возможность интересная, поскольку попав в некоторое невалидное состояние программист часто задаётся вопросом «А как же так вышло?». Ранее получить на него ответ можно было либо проигрывая в уме команды в обратном порядке, либо перезапуская отладку снова и снова с добавлением логов и новых контрольных точек. Всё это занимало время. Давайте посмотрим, как это работает сейчас.
Устанавливаем WinDbg
Пишем каку-нибудь небольшую программу и компилируем её. Я взял первую попавшуюся в Интернете реализацию пузырьковой сортировки (да, потому, что я лентяй).
#include "stdafx.h"
void swap(int *xp, int *yp)
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
// An optimized version of Bubble Sort
void bubbleSort(int arr[], int n)
{
int i, j;
bool swapped;
for (i = 0; i < n - 1; i++)
{
swapped = false;
for (j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
swap(&arr[j], &arr[j + 1]);
swapped = true;
}
}
// IF no two elements were swapped by inner loop, then break
if (swapped == false)
break;
}
}
/* Function to print an array */
void printArray(int arr[], int size)
{
int i;
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
}
// Driver program to test above functions
int main()
{
int arr[] = { 64, 34, 25, 12, 22, 11, 90 };
int n = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, n);
printf("Sorted array: n");
printArray(arr, n);
return 0;
}
Теперь у нас есть скомпилированный бинарник, символьный файл к нему и файл с исходником. Это всё понадобится для WinDbg.
Запускаем WinDbg с привилигиями администратора (это важно!). Выбираем File->Start debugging->Launch executable (advanced)
Задаём путь к отлаживаемому бинарнику, ставим галку «Record process with Time Travel Debugging», задаём путь для сохранения записанного трейса выполнения.
Жмём ок, программа запускается, отрабатывает и закрывается. WinDbg сохраняет записанный трейс выполнения в указанную папку и сразу же загружает его (это экономит время отладки).
Теперь открываем в WinDbg файл с кодом, ставим пару брейкпоинтов, запускаем отладку. На первый взгляд всё выглядит знакомо.
Но вот оно — главное отличие:
Нам доступен блок реверсивного управления направлением выполнения кода. Мы можем просто ступить на строку назад.
Мы можем поставить новый брейкпоинт где-нибудь выше и нажать «Go back», чтобы обратное выполнение программы дошло до него.
Обратите внимание — мы прыгнули назад во времени до входа программы в циклы for — и вот внизу в окне Locals мы уже видим, что переменные i и j в этот момент ещё имеют неопределённые значения.
Мы можем ходить вперёд и назад сколько угодно, перезапускать отладку с самого начала. Можно коннектится к уже запущенным процессам для записи выполнения только определённых блоков кода. Очень удобно ловить баги, которые воспроизводятся только иногда или только в определённом окружении, а потом прокручивать их снова и снова.
В общем, фича мне нравится.
Материалы по теме:
Автор: tangro