Skip to content

ZScript DAP Debug server #3009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 113 commits into
base: master
Choose a base branch
from
Open

ZScript DAP Debug server #3009

wants to merge 113 commits into from

Conversation

nikitalita
Copy link
Contributor

@nikitalita nikitalita commented Mar 16, 2025

This adds support for debugging ZScript scripts via a DAP-compatible debugger client.

image

Overview of changes:
[TODO]

@coelckers
Copy link
Member

This is very interesting, but please give a bit of info of how to set this up. Also, since this is marked as draft, what's still missing`?

Please be aware that if we accept this feature we will also have to take over the debugger client project in order to maintain it in the future.

Just one remark: The lack of a working ZScript debugger has been one of the main reasons why a lot of code that could be moved to scripts never hasn't, because it was necessary to keep it debuggable. As such, the effort put in here is very much appreciated. :)

@nikitalita
Copy link
Contributor Author

nikitalita commented Mar 17, 2025

This is very interesting, but please give a bit of info of how to set this up.

To set it up, install the extension linked above (instructions on the release page), then create a .vscode/launch.json file like this in whatever project you want to debug:

        {
            "type": "gzdoom",
            "name": "gzdoom Launch",
            "request": "launch",
            // Path to gzdoom executable
            "gzdoomPath": "/Users/nikita/Workspace/gzdoom/build/gzdoom.app/Contents/MacOS/gzdoom",
            // Port for GZDoom to start the debug server on
            "port": 19021, // this is the default port
            // These files will be loaded as pwads
            "projects": [
                // main project, loaded from and mapped to the main workspace folder
                "${workspaceFolder}",
                // Another project, loaded from the pk3 archive, but scripts are mapped to a folder
                {
                    "path": "/Users/nikita/Workspace/gzdoom/wadsrc/static",
                    "archive": "/Users/nikita/Workspace/gzdoom/build/gzdoom.app/Contents/MacOS/gzdoom.pk3"
                }
            ],
            "iwad": "doom2.wad",
            // Custom configuration file (optional)
            "configPath": "",
            "map": "MAP01",
            // Whatever additional args you want to pass
            "additionalArgs": ["-skill", "-4"],
            "cwd": "${workspaceFolder}",
        }

This ends up calling GZDoom with -debug 19021 -iwad doom2.wad -file ${workspaceFolder} -file /Users/nikita/Workspace/gzdoom/build/gzdoom.app/Contents/MacOS/gzdoom.pk3 +map MAP01 -skill -4

Attaching to a running GZDoom process is also possible if it was launched with -debug <PORT>, or had the vm_debug CVAR set to true:

        {
            "type": "gzdoom",
            "request": "attach",
            "name": "GZdoom attach",
            "port": 19021,
            "projects": [
                "${workspaceFolder}",
                {
                    "path": "/Users/nikita/Workspace/gzdoom/wadsrc/static",
                    "archive": "/Users/nikita/Workspace/gzdoom/build/gzdoom.app/Contents/MacOS/gzdoom.pk3"
                }
            ],
        }, 

Also, since this is marked as draft, what's still missing`?

Nothing's really missing, I just wanted to write a description for what I've changed. I'll mark it as ready for review now and write up a description later today.

The basic overview of the architecture is: VSCode runs a DAP client, GZDoom runs a DAP server (using cppdap). When a session is established, the DAP server subscribes to event hooks in the VM, the exception handlers, and the loggers so that it can emit events to the client when an instruction has hit a breakpoint, an exception has been thrown, log output, etc.

The DAP Client goes through an inline DAP proxy in the extension linked above. The DAP proxy is there primarily because:

  1. VSCode's ability to debug DAP debug servers via using its default methods is insanely lacking and errors connecting to the debugger, launching the process, etc. don't get logged, period; this provides an easy method of tracing where things go wrong
  2. We need to intercept requests and responses to correctly map scripts loaded by GZDoom to absolute paths that match the project files, because GZDoom will not always be able to determine the absolute paths to the files that were loaded (say, because we need to load the project as a pk3 or wad file)

As for the hooks, I've tried to keep the changes to the pre-existing code to a minimum and only hook where I need to. When debugging is off, it's a simple boolean check to see if the event hooks are installed and continues on if it isn't, we don't want a huge performance impact if the user isn't debugging.

Please be aware that if we accept this feature we will also have to take over the debugger client project in order to maintain it in the future.

I was hoping you'd say something like this :) I would be happy to turn it over to you, how do you want that set up?

Just one remark: The lack of a working ZScript debugger has been one of the main reasons why a lot of code that could be moved to scripts never hasn't, because it was necessary to keep it debuggable. As such, the effort put in here is very much appreciated. :)

I'm glad, I hope this makes both yours and modders' workflow a lot smoother.

@nikitalita nikitalita marked this pull request as ready for review March 17, 2025 10:12
@RicardoLuis0
Copy link
Collaborator

looked through the changes in the engine code, all seems fine 👍, will take quite a while more to look through the actual dap code though

nikitalita added 26 commits May 16, 2025 16:59
I have e-mailed the author for clarifcation on the license, will update this when they respond
…view

Defaults to `0`, should not change output if it's not set
…ements/decrements the pc

Does not change alignment, the offsets used in VMFrame still work
We need this for the debugger because we otherwise have no way to get the pc; it was a local in `ExecScriptFunc()`
Patches:
removed .gitmodules (submodules were thirdparty/json, thirdparty/googletest)
removed thirdparty/googletest, not needed
removed thirdparty/json/docs, thirdparty/json/test, thirdparty/json/benchmarks to prevent massive bloat
@recurracy3
Copy link

recurracy3 commented May 18, 2025

Breakpoints seem to have trouble with some functions. I overwrote Die() in my enemy, and trying to put a breakpoint in there makes GZDoom crash. It works fine in a lot of places but that's where it goes wrong. What should I provide? Seems to be a stack overflow.

This is amazing so far though, I'm incredibly impressed you got this working.

@nikitalita
Copy link
Contributor Author

Can you please attach your project?

@recurracy3
Copy link

https://drive.google.com/file/d/1SE0Cz48YPcjaai4TC-phMq1h9y0j1t1g/view?usp=drive_link

Should be in this link. The crash also happens when I insert a breakpoint in gruntgore.zs in class GruntHitRegion, function ModifyDamage, it happens anywhere in that function.

CrashReport.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants