svaante / dape

Debug Adapter Protocol for Emacs
GNU General Public License v3.0
448 stars 25 forks source link

Breakpoints not working in JS debug attach mode #101

Closed chahak13 closed 4 months ago

chahak13 commented 4 months ago

Hello!

Thank you for this package! I'm just recently moving to using this instead of dap-mode and I'm not able to get the configuration to work the way I want it. We have a custom debug attach section in launch.json since most people in the team use vscode but I'm not sure how to move it to dape-config.

    {
      "name": "server attach",
      "type": "pwa-node",
      "request": "attach",
      "cwd": "${workspaceFolder}/src/server",
      "sourceMaps": true,
      "skipFiles": ["<node_internals>/**/*.js"],
      "outFiles": ["${workspaceFolder}/src/server/dist/*"],
      "port": 9239,
      "address": "127.0.0.1"
    },

I'm using the default `js-debug-node-attach- config with the following changes to the adapter:

js-debug-node-attach :port 9239 :skipFiles ["**/node_modules/**/*.js"] :outFiles ["/path/to/src/server/dist/*"] :sourceMaps t :cwd "/path/to/src/server"

to try and replicate the same. It does seem to attach to the debugger since but if I use dape-breakpoint-toggle to set a breakpoint and trigger the code path in the server, it doesn't actually stop there. Honestly, I'm not entirely sure what's the issue here but I'm happy to provide more details if you can help me out. Thank you!

chahak13 commented 4 months ago

Something that I realized might be helpful - this application spawns multiple processes using fork(). If I'm understanding the setup right, the debugger is attaching itself to one of the processes since when I run the function with the breakpoint, it logs

[2024-03-30T16:51:15.169-04:00] [Process 40517] Function with breakpoint called

But if I go to the repl and do a console.log then it prints from a different process.

> console.log("hello")
[2024-03-30T16:52:14.246-04:00] [Process 40508] hello

Since this worked on vscode by default, I had to end up using that for debugging but would love to use dape for this too. I know multithreaded support is present but would a setup like mine be supported too? Thanks!

svaante commented 4 months ago

Hi! Would you mind throwing together an minimal example node application for me to test with?

chahak13 commented 4 months ago

I don't have my laptop with me right now, but I will try to set something up in a couple of days and let you know!

However, I was tinkering with this yesterday and realized that the subprocess spawned by cluster.fork() ends up listening to port 9240. So I was able to connect it when I changed the port from 9239 to 9240. So, I guess a better question for this issue could be - is there a way we can enable auto connection to the subprocess port? I also set the attachExistingChildren and autoAttachChildProcesses to true specifically too (but that's the default value too, so not sure) and that did not work.

chahak13 commented 4 months ago

Got my hands on a laptop so here it is.

const cluster = require('node:cluster');
const http = require('node:http');
const numCPUs = require('node:os').availableParallelism();
const process = require('node:process');

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < 1; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

This is from an example from the docs for node cluster - https://nodejs.org/api/cluster.html

When I run this with

node --inspect server.js

it starts the server with a primary process and a worker process that will handle the requests.

Next, I start dape with the following adapter config

js-debug-node-attach :skipFiles ["**/node_modules/**/*.js"] :sourceMaps t

This attaches the debugger to the process successfully. I see the Debugger attached logs for both.

I then do a simple curl http://localhost:8000 from the terminal. This hangs the server since it looks like it has triggered the breakpoint in the hello world route but it doesn't set dape correctly. In emacs I can't see anything. Nor can I use the dape-repl to continue and get a response.

image

On the other hand, if I connect the debugger specifically to port 9230 in the adapter, I get the expected behaviour.

image

I hope this helps! Let me know if you need anything else.

svaante commented 4 months ago

Helped a lot. I traced this down to dapes handling of child connections of child connections. Which was not handled correctly. The commit above should fix this.

Thanks for reporting this issue and helping me tracking it down.

Please get back to me if dape from master solved your issue?

chahak13 commented 4 months ago

Hello @svaante!

I tried to use master but was still able to replicate the same issue on there too. I followed the exact same steps as above and it showed the same behaviour. Sorry :/

svaante commented 4 months ago

Hmm, strange. I was able to reproduce your issue and I am pretty sure that I solved the issue.

What if you eval the following lisp code before debugging (paste into scratch M-x eval-buffer)

(defun dape--live-connections ()
  "Get all live connections."
  (cl-labels ((live-connections-1 (conn)
                (when (and conn (jsonrpc-running-p conn))
                  (cons conn
                        (mapcan #'live-connections-1
                                ;; New children are `push'ed onto the
                                ;; children list, therefore children
                                ;; are `reverse'd to guarantee LIFO
                                ;; order.
                                (reverse (dape--children conn)))))))
    (live-connections-1 dape--connection)))
chahak13 commented 4 months ago

You're right actually, that did indeed fix it (even without having to run the code before). I might've had some funkiness while I updated the package to master. I cleaned all of it and tried again and it worked successfully. Thank you!

svaante commented 4 months ago

Issue seams to be fixed and new version released