Pull to refresh
105.35
АСКОН
Russia's largest engineering software developer

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

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

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


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

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



Для решения этой задачи было написано расширение для 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 сможет их визуализировать:



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

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



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


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

В коде расширения 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. Выполнение программы находится в изначальной точке останова.

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

Articles

Information

Website
ascon.ru
Registered
Founded
Employees
501–1,000 employees
Location
Россия