This project contains libraries that will give your application the ability to remote debug a Squirrel (or Quirrel) VM. It does so using the standard Squirrel Debug Hooks, but provides a threadsafe & easy to use interface to set breakpoints, query the current state of the stack or local/global variables, redirect output, etc.
Also included (but optional) is an HTTP server that provides remote-debug capability. This is via an HTTP REST API, and output redirection/status notifications can be subscribed to via websockets. The REST API started by the embedded server is designed to be used by the companion Visual Studio Code extension, but you can also call them manually via something like POSTman, or the Swagger UI that is started by the embedded server on the provided port (e.g. http://localhost:8000/swagger/ui).
- interfaces Contains 2 pure-virtual interfaces that are implemented by the following 2 projects.
MessageEventInterface
, allowing the Debugger to send messages to the debug user interface.MessageCommandInterface
, which handles debug commands from the debug user interface.
- embedded_server
- uses OATPP to host an HTTP server. This acts as an endpoint for debug user interfaces to control the application.
- Provides an implementation of
MessageEventInterface
. - Exposes a "Swagger" interface that's accessible via the browser. Located by default at http://localhost:8000/swagger/ui.
- Swagger can be disabled by setting the cmake option
SDB_ENABLE_OATPP_SWAGGER=OFF
. This will save ~200KB binary size in release builds.
- squirrel_debugger
- Implementation of the Squirrel Debug API. Uses semaphores to lock the squirrel execution thread at breakpoints, and provides access to VM stack & variables.
- Provides an implementation of
MessageCommandInterface
.
- sample_app
- Shows an example of how to embed the debugger in an application that runs a Quirrel script.
[x] Improved output of variables in inspector
[x] Evaluation of arbitrary variable strings (eg foo['bar']
) to enable watch-window & variable hover functionality in VSCode
First versioned release, 'MVP'
[x] Websocket listener for server state changes [x] HTTP Command interface [x] Stack-Local variables [x] Global Variables [x] Simple Breakpoints [x] Output redirection & capture [x] Disablement of the swagger UI on startup [x] Modification of variable values (int, bool, float and string only.)
[ ] Multiple VM's (threads) [ ] Conditional breakpoints [ ] Immediate window for execution [ ] MacOS / Linux support [ ] squirrel unicode builds [ ] Modification of local & free variables. (No need to support modification of globals)
Currently, only support building on windows using CMake. This requires that you have Visual Studio 2019 installed.
To build: open a "Developer Command Prompt for VS 2019" (via start menu) and change to the repository directory.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release --target "sample_app"
sample_app.exe
will now exist in the build/
The provided sample_app
source code shows fleshed out examples; but a detailed list of steps you need to take are:
To use the debugger in your application, you will need to take a static library dependency on the following CMake targets: sdb::embedded_server
and sdb::squirrel_debugger
. The easiest way to do this is with CMake FetchContent. In your CMakeLists.txt
:
include(FetchContent)
FetchContent_Declare(
sdb
GIT_REPOSITORY https://github.com/leweaver/squirrel-debug-server.git
# Optionally pin to a specific version
# GIT_TAG v0.1.0
GIT_TAG origin/main
)
FetchContent_MakeAvailable(sdb)
target_link_libraries(${PROJECT_NAME}
sdb::embedded_server
sdb::squirrel_debugger)
If you do not use CMake to build your own project, you can directly link to the libraries generated by building the sample project.
- follow the instructions in the previous Building Sample from source using CMake section to build the libraries.
- Add the following include directories:
- interfaces/include
- embedded_server/include
- squirrel_debugger/include
- cmake-build-debug/_deps/oatpp-src
- cmake-build-debug/_deps/oatpp-src/src
- cmake-build-debug/_deps/oatpp-swagger-src/src
- cmake-build-debug/_deps/oatpp-websocket-src/src
- Add linker dependencies to the following (you may want to copy them to a convenient directory):
- cmake-build-debug/embedded_server/embedded_server.lib
- cmake-build-debug/squirrel_debugger/squirrel_debugger.lib
- cmake-build-debug/_deps/oatpp-build/src/oatpp.lib
- cmake-build-debug/_deps/oatpp-websocket-build/src/oatpp-websocket.lib
- cmake-build-debug/_deps/oatpp-swagger-build/src/oatpp-swagger.lib
In order to compile, you need to create implementations of logging functions. These are used to capture any logging output that this library generates, and route it to your own logging system.
Place the following code in any C++ file in your project.
namespace sdb::log
{
void LogFormatted(const char* tag, size_t line, Level level, const char* message, ...)
{
// OPTIONAL define this function body to hook into your logging system.
}
void LogString(const char* tag, size_t line, Level level, const char* str)
{
// OPTIONAL define this function body to hook into your logging system.
}
}// namespace sdb::log
- Initialize the global environment for the embedded server (
EmbeddedServer::InitEnvironment()
) - Create an
EmbeddedServer
instance (embeddedServer_
), giving it a port number to listen to network requests on (e.g. 8000). - Create an
SquirrelDebugger
instance (squirrelDebugger_
) - Set the
MessageCommandInterface
member of the EmbeddedServer to the SquirrelDebugger instance (embeddedServer_->SetCommandInterface(squirrelDebugger)
) - Set the
MessageEventInterface
member of the SquirrelDebugger to the EmbeddedServer instance. (squirrelDebugger->SetEventInterface(embeddedServer_->GetEventInterface())
) - Start the
EmbeddedServer
so it listens to network requests. (embeddedServer_->Start()
)
- Create your squirrel VM
- Add your Squirrel VM to the debugger:
squirrelDebugger_->AddVm
- Optional: Tell the debugger to pause execution as soon as it is attached:
debugger_->PauseExecution
- This is important. Most debug functionality will not work unless the squirrel VM is running a script, but execution is paused.
- call
sq_setprintfunc
with a function pointer that can redirect print calls to thesquirrelDebugger_->SquirrelPrintCallback
method. - call
sq_setnativedebughook
with a function pointer that can redirect VM debug calls to thesquirrelDebugger_->SquirrelNativeDebugHook
method. - call
sq_enabledebuginfo
with SQTrue to turn on native debugging - You can now execute and debug squirrel code.
- Call
embeddedServer_->Stop(true)
to request shutdown and join the network thread to wait for completion - Call
EmbeddedServer::ShutdownEnvironment()
to cleanup global resources.
Once you have compiled & run your application, you can try connecting to the debug interface via an HTTP GET request.
The simplest way to do this is in your favorite web browser: http://localhost:8000/DebugCommand/Variables/Global?path
You should see a JSON blob, representing all the global variables defined in the current Squirrel.
- If the connection to this page fails, the OATPP server is not running inside your application properly.
- If you get a valid HTTP response, but it contains an error; that will guide you to what may be wrong.
You can also check the Swagger page in your browser (http://localhost:8000/swagger/ui), to try out more advanced functionality. This HTTP API is what the visual studio code extension uses to communicate with the debug engine.