Расширение Visual Studio для визуализации пользовательских классов в режиме отладки. Часть 2

в 10:17, , рубрики: c++, c3d, c3d toolkit, C3DKernel, Extensions, microsoft, Visual Studio, Блог компании АСКОН, отладка, разработка под windows

Эта статья является продолжением статьи из далекого 2014 года. Напомню, о чем шла речь в прошлой статье.

Какую задачу будем решать

Мы пишем ПО на языке С++, в среде Visual Studio 2015. У нас в проекте, естественно, есть пользовательские типы данных. В качестве примера таких типов могу привести класс MbSolid. Этот класс входит в состав математического ядра C3D и является абстракцией твердого тела. Тело описывается гранями, грани какими-то поверхностями и т.д. Т.е. структура класса довольно сложная, и в процессе отладки собственных алгоритмов хотелось бы визуально прикинуть, какое тело получилось на данный момент.

Картинка из прошлой статьи. В качестве примера пользовательского класса там используется класс отрезка прямой.

Расширение Visual Studio для визуализации пользовательских классов в режиме отладки. Часть 2 - 1

Для решения этой задачи было написано расширение для VisualStudio. Тут ничего интересного, ссылки есть в прошлой статье. Но возникла проблема — как в расширении VisualStudio получать данные из адресного пространства отлаживаемого (другого) процесса?

В случае простых типов данных, например массивов, всё не так уж сложно, и Microsoft даже подготовили пример для std::vector. А в нашем случае MbSolid имеет внушительную иерархию наследования и большое количество полей данных.

Модификация пользовательских типов

В прошлой статье был предложен инвазивный вариант решения этой проблемы. В каждый пользовательский класс (который мы хотим визуализировать во время отладки) добавляется поле-маркер. И в каждый не константный метод класса добавляется код для сериализации данных класса в shared memory. В поле-маркере сохраняется адрес в shared memory, куда мы сохранили данные класса. В момент отладки, при просмотре содержимого интересующей нас переменной, расширение VisualStudio находит поле-маркер и десериализует данные из shared memory, ну а дальше как-то визуализирует полученные данные.

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

Сервер в пользовательском процессе

И вот недавно пришла идея написать простой сервер, который будет жить в пользовательском процессе в отдельном потоке и отвечать на запросы, пришедшие от нашего расширения VisualStudio. Для сервера за основу был взят проект Microsoft C++ REST SDK. Этот проект позволил быстро написать свой http-сервер, который получает GET-запросы и возвращает описание экземпляра пользовательского класса в json-формате. Напомню, нас интересует визуальное представление экземпляров класса MbSolid (твердые тела).

В запросе к серверу передается адрес переменной в адресном пространстве отлаживаемого процесса. Т.к. сервер живет в том же процессе, то он без проблем получает доступ к данным по запрашиваемому адресу. Сервер, получив адрес экземпляра класса, приводит этот указатель к типу MbSolid*. Далее сервер создает аппроксимацию этого тела в виде полигональной сетки. Сериализует вычисленные вершины и индексы треугольников в json и отправляет ответ. На стороне VisualStudio расширение получает ответ, десериализует данные и отрисовывает полученную полигональную сетку в окне VisualStudio.

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

Расширение Visual Studio для визуализации пользовательских классов в режиме отладки. Часть 2 - 2

Более того, таким способом можно даже отправлять запросы нашему серверу из браузера и визуализировать данные процесса с помощью WebGL.

Сделал простой демонстрационный пример. Запускаем наше приложение. Открываем страницу примера в браузере, на странице вводим адрес переменной, отправляем запрос к серверу и отрисовываем ответ. Не знаю, зачем это может понадобиться, но штука прикольная image

Расширение Visual Studio для визуализации пользовательских классов в режиме отладки. Часть 2 - 4

Оживляем сервер

Все бы хорошо. Но есть одна проблема. Когда срабатывает точка останова, студия останавливает все потоки пользовательского процесса. В результате наш сервер тоже останавливается и не может отвечать на запросы. Для обхода этой проблемы используем следующий костыль: текущий поток, в котором сработала точка останова, замораживается, и пользовательский процесс запускается. В этот момент наш сервер оживает и расширение отправляет ему запрос с адресом интересующей нас переменной. После получения ответа выполнение пользовательского процесса снова приостанавливается, и в качестве текущего потока для дебагера устанавливается изначальный поток, в котором сработала точка останова. Для пользователя это выглядит так, будто ничего не произошло и выполнение программы остановилось в точке останова.

В коде расширения VisualStudio этот костыль выглядит следующим образом. Сработала точка останова. Пользователь запрашивает данные интересующей его переменной. В этот момент мы замораживаем текущий поток и запускаем дебагер:

if (dte.Debugger.CurrentMode != EnvDTE.dbgDebugMode.dbgBreakMode)
   return;

currentThread = dte.Debugger.CurrentThread;

currentThread.Freeze();
            
dte.Debugger.Go(false);

Посылаем запрос серверу. Получаем ответ. Останавливаем процесс, размораживаем наш поток:


if (dte.Debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) 
  return;

dte.Debugger.Break();

if (currentThread != null)
{
   currentThread.Thaw();
   dte.Debugger.CurrentThread = currentThread;
}

Всё! Мы получили данные от сервера и отрисовали их в окне VisualStudio. Выполнение программы находится в изначальной точке останова.

В завершение хочу отметить, что пока мне непонятны побочные эффекты, которые порождает такой подход. Буду рад, если вы поделитесь вашими соображениями в комментариях к этой статье.

Автор: ershovdz

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js