Debugging

How to debug the Plugify core, language modules and native plugins inside a running game with symbols, Visual Studio, GDB and LLDB.

This page is about debugging the core, a language module or a native plugin inside a running game, such as a Counter-Strike 2 server. For debugging plugins in a particular language, see the Debugging page of that language in Languages.

Building with symbols

Published packages of the core, the modules and the plugins are release builds without debug symbols. To get readable stack traces and breakpoints, build the part you are debugging yourself:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build

RelWithDebInfo gives symbols with optimizations, which is closer to what runs in production. Replace the library in the server's envs folder (or the host's bin folder for the core) with the one you built.

Seeing Plugify types in the debugger

The core repository has formatters for the plg:: types (strings, vectors, variants) in its debug folder:

  • debug/vs/plg_types.natvis for Visual Studio: add it to your project or to the Visualizers folder of Visual Studio;
  • debug/lldb/plg_types_formatter.py for LLDB: load it with command script import <path>/plg_types_formatter.py, or add that line to ~/.lldbinit.

Debugging on Windows

Visual Studio 2022 can start the game under the debugger.

  1. Open the executable. Choose File > Open > Project/Solution and select the game executable, for example cs2.exe in game/bin/win64.

  1. Set the command line. In the properties of the project, Debugging > Command Arguments, enter the arguments you start the server with, for example -dedicated +map de_dust2.

  1. Choose the debugger type. Auto is enough for native code. To debug C# plugins as well, choose mixed mode (native and .NET Core).
  2. Turn off output redirection. The game has its own console, which does not work with redirected output.
  3. Start debugging with F5. When the game crashes, Visual Studio stops at the crash with the stack and variables.

Debugging on Linux

On Linux use GDB or LLDB, from the command line or from an IDE such as CLion or VS Code. Start the server binary directly so the debugger sees the whole process:

cd game/bin/linuxsteamrt64
gdb --args ./cs2 -dedicated +map de_dust2

In GDB, run starts the server, bt prints the stack after a crash, and break <function> sets a breakpoint. A breakpoint in a module or plugin that is not loaded yet is set as pending and becomes active once the library loads.

Without a debugger attached, a crash can still be examined afterwards: if the system keeps core dumps, coredumpctl gdb opens the last one.

What to look at first

  1. The load report. After loading, the core logs which extensions did not start and why. Many problems end here: a wrong manifest, a missing dependency, a library that cannot be loaded.
  2. The log level. Run the host with a more detailed level to see what the core does step by step; S2-Launcher takes --verbosity=<level>, see Configuration.
  3. A minimal case. Reproduce the problem in plug with only the extensions involved: it starts in a second and is easy to run under a debugger.

Common cases

  • Crash inside a plugin: null pointers, a destroyed object, a wrong signature in the manifest. Check that paramTypes, retType and ref in the manifest match the real function.
  • Wrong values between languages: the manifest of the exporting plugin and the bindings of the caller disagree. Regenerate the bindings after changing a manifest.
  • Performance: use a profiler such as perf or Visual Studio's profiler on a RelWithDebInfo build.

If you are stuck, ask on Discord or open an issue on GitHub.