Time Travel Debugging в новом WinDbg

Возможно, вы уже слышали о том, что Microsoft выпустила обновлённую версию своего известного отладчика WinDbg, который и раньше был хорош, но слишком уж отстал по интерфейсу от современных тенденций. Новая версия WinDbg, к счастью, не пошла настолько далеко, чтобы получить новомодный UWP-интерфейс, но вот классические риббон-бары в стиле Microsoft Office — ей очень идут. Приложение распространяется только через Microsoft Store и работают на Win10 как минимум с Anniversary Update. Microsoft говорит, что это сделано для удобства установки и обновления, но я как-то не помню, чтобы с классическим WinDbg были какие-то проблемы с установкой. Скорее это выглядит как ещё один способ приучения разработчиков и пользователей к привычке пользоваться только самой последней версией Windows. Ну ок, пусть так.

WinDbg выглядит симпатично

image

И вся его мощь в виде команд, отладки драйверов, удалённой отладки, скриптов и прочего — осталась при нём. Более того, 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)

59edb603b221a219221030.png

Задаём путь к отлаживаемому бинарнику, ставим галку «Record process with Time Travel Debugging», задаём путь для сохранения записанного трейса выполнения.

59edb603d8e2f281212034.png

Жмём ок, программа запускается, отрабатывает и закрывается. WinDbg сохраняет записанный трейс выполнения в указанную папку и сразу же загружает его (это экономит время отладки).

59edb60414580673449419.png

Теперь открываем в WinDbg файл с кодом, ставим пару брейкпоинтов, запускаем отладку. На первый взгляд всё выглядит знакомо.

59edb6043c0b6384019724.png

Но вот оно — главное отличие:

59edb60439f51908260634.png

Нам доступен блок реверсивного управления направлением выполнения кода. Мы можем просто ступить на строку назад.

59edb6045bd0a129446285.png

Мы можем поставить новый брейкпоинт где-нибудь выше и нажать «Go back», чтобы обратное выполнение программы дошло до него.

59edb604608de632229728.png

Обратите внимание — мы прыгнули назад во времени до входа программы в циклы for — и вот внизу в окне Locals мы уже видим, что переменные i и j в этот момент ещё имеют неопределённые значения.

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

В общем, фича мне нравится.

Материалы по теме:

  1. Анонс фичи в блоге WinDbg
  2. Документация
  3. Установить WinDbg

© Habrahabr.ru