В заметке про live-режим для opengl-приложений в daScript я кратко описывал контексты (раздел Архитектура приложения). Здесь разберу их немного более подробно.
Программы daScript выполняются в контексте, структуре, которая хранит окружение программы — выделенную память, в которой хранятся код и данные программы, загруженные модули, настройки виртуальной машины.
tutorual06 показывает работу с контекстом в daScript:
Создание и удаление контекстов в daScript дешёво, один из паттернов организации C++-приложения, использующего daScript — выделение нового контекста, выполнение в нём работы и уничтожение.
Контекст можно воспринимать как экземпляр выполняющейся программы. Программа на С++ может иметь несколько контекстов, каждый из которых может выполнять программу на daScript (как скомпилированную из различных das-файлов, так и из одного и того же).
В примере выше в скомплированные программу передаётся адрес из другого контекста. Это небезопасно, так как один контекст ничего не знает про время жизни переменных другом, поэтому операция отмечена как unsafe.
Кроме возможности иметь раздельные контексты, daScript позволяет склонировать существующий контекст — fork_debug_agent_context. Функция создаёт клон контекста, и выставляет флаг контекста persistent, а затем в этом клонированном контексте выполняет функцию инициализации, переданную аргументом. Обычно в этой функции регистрируется новый DebugAgent — обёртка над контекстом, которая позволяет найти указатель на склонированный контекст по имени (и опционально, изменить поведение с помощью хуков). Склонированный контекст имеет доступ к тому же окружению, что и основной (модули, функции, копии переменных).
Склонированный контекст не копирует состояние из основного!
Можно вызвать функции, определённые в классе-наследнике 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-агент + сервер для связи с IDE) (плагин для vscode): (необходимо определить #define DAS_DEBUGGER 1 если это по каким-то причинам не определилось в das_config.h автоматически)
Более продвинутый примерinstrument_function — обёртка, сохраняющая результат в файл, который можно посмотреть в виде флеймграфа.