В заметке про live-режим для opengl-приложений в daScript я кратко описывал контексты (раздел Архитектура приложения
). Здесь разберу их немного более подробно.
Контекст
Программы daScript выполняются в контексте, структуре, которая хранит окружение программы — выделенную память, в которой хранятся код и данные программы, загруженные модули, настройки виртуальной машины.
tutorual06 показывает работу с контекстом в daScript:
//загружаем das-файл и компилируем его |
(Устройство интерпретаторов lua-jit и daScript — немного более детально про симуляцию программы)
Создание и удаление контекстов в daScript дешёво, один из паттернов организации C++-приложения, использующего daScript — выделение нового контекста, выполнение в нём работы и уничтожение.
Контекст можно воспринимать как экземпляр выполняющейся программы. Программа на С++ может иметь несколько контекстов, каждый из которых может выполнять программу на daScript (как скомпилированную из различных das-файлов, так и из одного и того же).
Создание контекста из daScript
API для создания контекстов также доступно в самом daScript, так что скрипт сам может создавать новые контексты и выполнять в них программы.
Пример eval_in_context:
В примере выше в скомплированные программу передаётся адрес из другого контекста. Это небезопасно, так как один контекст ничего не знает про время жизни переменных другом, поэтому операция отмечена как unsafe
.
Форк контекста
Кроме возможности иметь раздельные контексты, daScript позволяет склонировать существующий контекст — fork_debug_agent_context. Функция создаёт клон контекста, и выставляет флаг контекста persistent
, а затем в этом клонированном контексте выполняет функцию инициализации, переданную аргументом. Обычно в этой функции регистрируется новый DebugAgent
— обёртка над контекстом, которая позволяет найти указатель на склонированный контекст по имени (и опционально, изменить поведение с помощью хуков). Склонированный контекст имеет доступ к тому же окружению, что и основной (модули, функции, копии переменных).
Склонированный контекст не копирует состояние из основного!
Пример agent_fork_sample.das
Выведет:my_context:default // в основном контексте test_value имеет значение по умолчанию
my_context:my_context // изменили значение в основном контексте
debug agent debug_context:default // <--в склонированном контексте test_value имеет значение по умолчанию
debug agent debug_context:debug_context // изменили значение в склонированном контексте
my_context:my_context // в основном контексте значение test_value не изменилось
Аннотация для функций apply_in_context позволяет вызывать функцию в другом контексте прозрачно для вызывающего кода. Пример выше можно переписать так:
Пример создания отдельного контекста — модуль live, пример клонирования — opengl_cache.
Переопределение поведения debug-агентов
Можно вызвать функции, определённые в классе-наследнике debug-агента (invoke_debug_agent_function), но намного более интересной возможностью является переопределения методов. Виртуальная машина daScript отслеживает наличие зарегистрированных отладочных агентов и передаёт им информацию в ходе выполнения программы — интерфейс DapiDebugAgent.
Простые примеры определения кастомных debug-агентов:
logger_agent — добавление кастомного префикса при логгировании
insturment_function — профилирование вызовов функций.
instrument — программный брейкпоинт и пошаговая трассировка
Более сложные примеры:
stackwalk — более продвинутая версия instrument
, устанавливает кастомный DapiStackWalker
и DapiDataWalker
, печатает значение локальных переменных на каждом шаге
context_state_example — еще немного более полная версия информации о переменных, уже более похожая на информацию для отладчика
opengl_state — в хуке onCollect печатает информацию о состояниях OpenGL
decs_state — собирает информацию из модуля decs
(entity-component-system)
ast_debug — информация о состоянии expression tree, для отладки макросов
Инструмнты основе debug-агентов
Полноценный отладчик
— debug (debug-агент + сервер для связи с IDE) (плагин для vscode):
(необходимо определить #define DAS_DEBUGGER 1
если это по каким-то причинам не определилось в das_config.h автоматически)
Более продвинутый пример instrument_function
— обёртка, сохраняющая результат в файл, который можно посмотреть в виде флеймграфа.