Skip to content
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

Ensure node-based debug adapters spawn same node exec as Theia #5492

Closed
wants to merge 5 commits into from

Conversation

perspectivus1
Copy link
Contributor

Signed-off-by: Eyal Barlev eyal.barlev@sap.com

Sometimes the container/machine running Theia has more than one node executable available.

The node executable used to run Theia should also be used to run node-based debug adapters; whereas, another node executable can be used to run/debug user applications.

Debug adapters specify their runtime in their corresponding package.json file (e.g. link):

"contributes": {
  "debuggers": [
    {
       "runtime": "node"

The debug adapter typically does not specify the full path of the node executable, which means that Theia uses the first node executable on the PATH environment variable to spawn the debug adapter. However, this might not be the same node executable that was used to run Theia.

For us at SAP, this is critical, as we expose node executables to the Theia container, and those executables might run remotely on other containers that have no access to the debug adapter code (which resides on the Theia container).

This pull request ensures that if a debug adapter doesn't specify a fully qualified node runtime, Theia would spawn the debug adapter using the same node executable that was used to launch Theia.

This change should not adversely affect existing flows, but rather add support for the requirement detailed above.

Signed-off-by: Eyal Barlev <eyal.barlev@sap.com>
@akosyakov
Copy link
Member

akosyakov commented Jun 17, 2019

We've discussed something like that for TS language servers already. cc @marcdumais-work @kittaakos

I believe the proper way there and here is to use child_process.fork instead of guessing node executable from args. It's designed to start new processes with exactly the same node which is used to run Theia.

Why guessing would be better? @tolusha What is your take on it?

@perspectivus1
Copy link
Contributor Author

I tried your proposed approach here. This indeed ran the debug adapter using child_process.fork(), but debugging user code kept failing with this error message:

Starting inspector on 127.0.0.1:24166 failed: address already in use

Do you know why this is happening?

Note that the node's API documentation specifically states this:

child_process.fork() will spawn new Node.js instances using the process.execPath of the parent process.

@tolusha
Copy link
Contributor

tolusha commented Jun 18, 2019

I personally stand for fork

@akosyakov
Copy link
Member

@perspectivus1 Can you open an alternative PR with fork that we can have a look as well?

@perspectivus1
Copy link
Contributor Author

I created an alternative PR (#5508), with a fork() implementation (that fails to debug user code).

@kittaakos
Copy link
Contributor

kittaakos commented Jun 28, 2019

This won't work when running from electron, as it will spawn/fork a new electron process. Also, we have to generalize it and reuse for the TS and JSON LSs.

See: #5385

@akosyakov
Copy link
Member

We should sort out Electron before merging.

@akosyakov akosyakov added debug issues that related to debug functionality electron issues related to the electron target labels Jul 10, 2019
Signed-off-by: Eyal Barlev <eyal.barlev@sap.com>
Signed-off-by: Eyal Barlev <eyal.barlev@sap.com>
@perspectivus1
Copy link
Contributor Author

@akosyakov, I fixed the Electron case.

@kittaakos, there's a simple change that I have not pushed, that when running Electron would spawn the debug adapter as a node process:

In file packages\debug\src\node\debug-adapter-factory.ts:

        const options = { stdio: ['pipe', 'pipe', 2], env: environment.electron.runAsNodeEnv() };

Should I add it to this pull request?

@@ -66,6 +68,12 @@ export class LaunchBasedDebugAdapterFactory implements DebugAdapterFactory {
const isForkOptions = (forkOptions: RawForkOptions | any): forkOptions is RawForkOptions =>
!!forkOptions && !!forkOptions.modulePath;

if (!isForkOptions(executable)) {
if ((executable as DebugAdapterSpawnExecutable).command === 'node' && !environment.electron.is()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the description:

The node executable used to run Theia should also be used to run node-based debug adapters

This won't be the case if you run in electron.

Signed-off-by: Eyal Barlev <eyal.barlev@sap.com>
if (!isForkOptions(executable)) {
if ((executable as DebugAdapterSpawnExecutable).command === 'node') {
if (environment.electron.is()) {
options.env = environment.electron.runAsNodeEnv();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kittaakos, is this what you meant?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks much better now, logically it is correct.

Can we move this Node.js-specific customization to the process-factory?
What's your take on that, @akosyakov?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why execPath is used only in else branch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this Node.js-specific customization to the process-factory?
What's your take on that, @akosyakov?

Do you mean if command === process.execPath? for node i don't think so, it is not the same

I don't think this code should use process factory at all, but it is another issue. User processes (running in user dev env like terminals and task) should be created by a factory, all other should be created vis node js APIs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this Node.js-specific customization to the process-factory?

So, should I move the implementation elsewhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

execPath contains the full path of the Electron executable; whereas we want to spawn Electron's embedded node executable.

I am not sure if it is true. But please correct me if I am wrong.
If I have Node.js v10.15.3 available from the PATH and I start a Theia-based electron application which comes with electron 3.1.7 (as of today), then I will run with a different Node.js version than I am expecting.

Akoss-MacBook-Pro:theia akos.kitta$ npx electron -i
> console.log(process.versions.node)
10.2.0
undefined
> .exit

Does this make sense?

Copy link
Contributor

@kittaakos kittaakos Jul 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want to spawn Electron's embedded node executable.

This part is not true, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Theia runs in node, we can control the node executable that is used to spawn the debug adapter, because we can infer it from process.execPath.

When Theia runs in Electron, process.execPath contains a full path to the electron executable. Therefore, we have 2 choices:

  1. Spawn a node process using the first node executable found in the PATH environment variable.
  2. Fork an electron process using the ELECTRON_RUN_AS_NODE environment variable, which will use Electron's embedded node.

However, the current implementation of AbstractVSCodeDebugAdapterContribution.provideDebugAdapterExecutable() creates a DebugAdapterExecutable that causes Theia to spawn, rather than fork debug adapters.

In other words, in the case of a node-based debug adapter, the code above always spawn a node process, regardless of whether Theia runs in Electron or in node and regardless of the value of the ELECTRON_RUN_AS_NODE environment variable.

If this is the desired behavior, then this PR fulfills the requirement.

If we wish to use Electron's embedded node, I will have to modify PR #5508.

Please advise

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forking is a proper solution. It is the same way as VS Code does it. I would be in favor of closing this PR and modifying #5508.

…d not fork)

Signed-off-by: Eyal Barlev <eyal.barlev@sap.com>
@akosyakov
Copy link
Member

Closing it in favor of #5508

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debug issues that related to debug functionality electron issues related to the electron target
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants