From 8857567570d7ca17e65ef296d2defee9eba750e7 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 28 Oct 2026 05:43:33 -0700 Subject: [PATCH] perf(wsl): read the kernel, login home and platform in one exec (#35070) * perf(wsl): read the kernel, login home and platform in one exec The WSL connect's first exec now prints the kernel release, $HOME and the marker-backed uname platform probe together, and hands the platform and home to resolveOrcadRemoteContext, which then skips its own uname and $HOME probes: two fewer serial guest round trips per connect. SSH passes neither, so its probes are unchanged. * fix(wsl): accept the folded host facts only in their exact shape Each fact now prints after its own marker, and the output is used only when it has exactly the kernel line, the home line, the blank separator and the platform line. Anything else (stdout noise, a HOME containing a newline) falls back to reading the kernel alone and lets the context probe the platform and home itself, so no truncated path is ever used. --- .../install/orcad-remote-context.ts | 12 +- .../install/orcad-remote-primitives.test.ts | 16 ++ .../ssh/ssh-remote-platform-detection.test.ts | 15 +- src/main/ssh/ssh-remote-platform-detection.ts | 16 +- src/main/wsl/wsl-orcad-deploy.test.ts | 4 +- src/main/wsl/wsl-orcad-held-context.test.ts | 143 ++++++++++++++++-- src/main/wsl/wsl-orcad-held-context.ts | 51 ++++++- 7 files changed, 230 insertions(+), 27 deletions(-) diff --git a/src/main/managed-server/install/orcad-remote-context.ts b/src/main/managed-server/install/orcad-remote-context.ts index ab1f332622f..2e9f945a5a7 100644 --- a/src/main/managed-server/install/orcad-remote-context.ts +++ b/src/main/managed-server/install/orcad-remote-context.ts @@ -52,7 +52,9 @@ export async function resolveOrcadRemoteContext> { const host = detectedHost ?? (await detectRemoteHostPlatform(connection, { signal })) if (!host) { @@ -65,7 +67,8 @@ export async function resolveOrcadRemoteContext { const { host } = remote if (!keyByMachine || isWindowsRemoteHost(host)) { const loginHome = normalizeRemoteHome( - await execOrcadRemote(remote, readRemoteHomeCommand(host)), + knownLoginHome ?? (await execOrcadRemote(remote, readRemoteHomeCommand(host))), host ) if (!validateRemoteHome(loginHome, host)) { diff --git a/src/main/managed-server/install/orcad-remote-primitives.test.ts b/src/main/managed-server/install/orcad-remote-primitives.test.ts index 071534b41f3..70ebf43a094 100644 --- a/src/main/managed-server/install/orcad-remote-primitives.test.ts +++ b/src/main/managed-server/install/orcad-remote-primitives.test.ts @@ -282,4 +282,20 @@ describe('orcad remote context', () => { }) expect(order).toEqual(['prepare', 'record']) }) + + it('uses a WSL caller-known platform and login home instead of probing them', async () => { + vi.mocked(detectRemoteHostPlatform).mockClear() + const commands: string[] = [] + mockExec.mockImplementation(async (_conn, command: string) => { + commands.push(command) + return command.includes('ldd') ? 'ldd (GNU libc) 2.39\n' : '__ORCAD_RECORD_ABSENT__\n' + }) + const wslTarget = { kind: 'wsl', id: 'wsl:u', distro: 'U', label: 'U' } as const + + const context = await resolveOrcadRemoteContext(wslTarget, conn, undefined, linux, '/home/u\n') + + expect(context).toMatchObject({ remoteHome: '/home/u', userDataDir: '/home/u/.orca' }) + expect(detectRemoteHostPlatform).not.toHaveBeenCalled() + expect(commands.some((command) => command.includes('$HOME'))).toBe(false) + }) }) diff --git a/src/main/ssh/ssh-remote-platform-detection.test.ts b/src/main/ssh/ssh-remote-platform-detection.test.ts index 43286af56a1..67740e2ca77 100644 --- a/src/main/ssh/ssh-remote-platform-detection.test.ts +++ b/src/main/ssh/ssh-remote-platform-detection.test.ts @@ -10,7 +10,8 @@ vi.mock('./remote-exec/remote-exec-command', async (importOriginal) => ({ execCommand: execCommandMock })) -const { detectRemoteHostPlatform } = await import('./ssh-remote-platform-detection') +const { detectRemoteHostPlatform, remoteHostPlatformFromUnameProbe } = + await import('./ssh-remote-platform-detection') const conn = {} as SshConnection @@ -264,3 +265,15 @@ describe('detectRemoteHostPlatform failure reporting', () => { }) }) }) + +describe('remoteHostPlatformFromUnameProbe', () => { + it('reads only marker-backed platform lines, past other output folded into the same exec', () => { + expect( + remoteHostPlatformFromUnameProbe( + '5.15.167.4-microsoft-standard-WSL2\n/home/u\n\n__ORCA_REMOTE_PLATFORM__ Linux aarch64\n' + ) + ).toMatchObject({ os: 'linux', arch: 'arm64' }) + expect(remoteHostPlatformFromUnameProbe('Linux x86_64\n')).toBeNull() + expect(remoteHostPlatformFromUnameProbe('\n__ORCA_REMOTE_PLATFORM__ Linux sparc\n')).toBeNull() + }) +}) diff --git a/src/main/ssh/ssh-remote-platform-detection.ts b/src/main/ssh/ssh-remote-platform-detection.ts index 047a1809483..1270588c35c 100644 --- a/src/main/ssh/ssh-remote-platform-detection.ts +++ b/src/main/ssh/ssh-remote-platform-detection.ts @@ -14,6 +14,8 @@ import { getRemoteHostPlatform, type RemoteHostPlatform } from './ssh-remote-pla import { powerShellCommand } from './ssh-remote-powershell' const PLATFORM_PROBE_MARKER = '__ORCA_REMOTE_PLATFORM__' +/** The probe's marker-backed line starts with this. */ +export const UNAME_PLATFORM_PROBE_MARKER = `${PLATFORM_PROBE_MARKER} ` const MAX_UNAME_FIELD_CHARS = 64 const MAX_THROWN_OUTPUT_CHARS = 200 const MAX_LOGGED_OUTPUT_CHARS = 1000 @@ -24,6 +26,15 @@ type PlatformProbeOutcome = | { kind: 'unparsed'; output: string } | { kind: 'failed'; error: unknown } +/** The POSIX platform probe, for callers that fold it into a command of their own. */ +export const UNAME_PLATFORM_PROBE_COMMAND = `printf '\\n%s ' '${PLATFORM_PROBE_MARKER}'; uname -sm` + +/** The platform a probe's output proves; null for unsupported, unparsed or startup-noise output. */ +export function remoteHostPlatformFromUnameProbe(output: string): RemoteHostPlatform | null { + const outcome = parseRemotePlatformOutput(output) + return outcome.kind === 'detected' ? getRemoteHostPlatform(outcome.platform) : null +} + export async function detectRemoteHostPlatform( conn: RemoteInstallTransport, options?: { signal?: AbortSignal } @@ -127,10 +138,9 @@ async function detectUnamePlatform( ): Promise { try { // Separate marker from startup output that may omit its trailing newline. - const command = `printf '\\n%s ' '${PLATFORM_PROBE_MARKER}'; uname -sm` const output = signal - ? await execCommand(conn, command, { signal }) - : await execCommand(conn, command) + ? await execCommand(conn, UNAME_PLATFORM_PROBE_COMMAND, { signal }) + : await execCommand(conn, UNAME_PLATFORM_PROBE_COMMAND) return parseRemotePlatformOutput(output) } catch (error) { signal?.throwIfAborted() diff --git a/src/main/wsl/wsl-orcad-deploy.test.ts b/src/main/wsl/wsl-orcad-deploy.test.ts index c5004e674e7..165b8953b25 100644 --- a/src/main/wsl/wsl-orcad-deploy.test.ts +++ b/src/main/wsl/wsl-orcad-deploy.test.ts @@ -108,9 +108,9 @@ function transport(target: WslOrcadHostTarget): OrcadHostTransport { function launch(kernel = '5.15.167.4-microsoft-standard-WSL2', options = {}) { mocks.exec.mockImplementation(async (_conn: unknown, command: string) => { - if (command === 'uname -r') { + if (command.includes('uname -r')) { mocks.steps.push('uname') - return `${kernel}\n` + return `__ORCA_WSL_KERNEL__ ${kernel}\n__ORCA_WSL_HOME__ /home/u\n\n__ORCA_REMOTE_PLATFORM__ Linux x86_64\n` } return '4242\n' }) diff --git a/src/main/wsl/wsl-orcad-held-context.test.ts b/src/main/wsl/wsl-orcad-held-context.test.ts index 814393f121a..552c1e146ae 100644 --- a/src/main/wsl/wsl-orcad-held-context.test.ts +++ b/src/main/wsl/wsl-orcad-held-context.test.ts @@ -4,10 +4,18 @@ import type { WslDistroHolder } from './wsl-distro-holder' import type { WslOrcadHostTarget } from './wsl-orcad-host-target' const mocks = vi.hoisted(() => { - const state: { spawned: string[]; refused: string[]; probeStep: (() => void) | null } = { + const state: { + spawned: string[] + refused: string[] + probeStep: (() => void) | null + facts: string + contextArgs: unknown[] + } = { spawned: [], refused: [], - probeStep: null + probeStep: null, + facts: '', + contextArgs: [] } return state }) @@ -16,25 +24,32 @@ const mocks = vi.hoisted(() => { vi.mock('../ssh/remote-exec/remote-exec-command', () => ({ execCommand: vi.fn( async (_conn: unknown, command: string, options?: { signal?: AbortSignal }) => { + const label = + command === 'uname -r' ? command : command.includes('uname -r') ? 'host-facts' : command if (options?.signal?.aborted) { - mocks.refused.push(command) + mocks.refused.push(label) throw options.signal.reason } - mocks.spawned.push(command) - return command === 'uname -r' ? '5.15.167.4-microsoft-standard-WSL2\n' : '' + mocks.spawned.push(label) + if (label === 'uname -r') { + return `${mocks.facts.includes('-Microsoft') ? '4.4.0-19041-Microsoft' : '5.15.167.4-microsoft-standard-WSL2'}\n` + } + return label === 'host-facts' ? mocks.facts : '' } ) })) -// Like the real context resolver: several probes in series, each passed the caller's signal. +// Like the real context resolver given a known platform and home: its remaining probes in series. vi.mock('../managed-server/install/orcad-remote-context', async () => { const { execCommand } = await import('../ssh/remote-exec/remote-exec-command') return { resolveOrcadRemoteContext: async ( _target: unknown, conn: OrcadHostTransport, - signal?: AbortSignal + signal?: AbortSignal, + ...known: unknown[] ) => { - for (const command of ['uname -sm', 'echo $HOME', 'ldd --version']) { + mocks.contextArgs = known + for (const command of ['ldd --version', 'cat orcad-active.json']) { await new Promise((resolve) => { mocks.probeStep = resolve }) @@ -45,7 +60,13 @@ vi.mock('../managed-server/install/orcad-remote-context', async () => { } }) -import { holdWslDistroWithContext, isWsl1KernelRelease } from './wsl-orcad-held-context' +import { runProcess } from '@orca/process-host' +import { + holdWslDistroWithContext, + isWsl1KernelRelease, + readWslHostFacts, + WSL_HOST_FACTS_COMMAND +} from './wsl-orcad-held-context' const target: WslOrcadHostTarget = { kind: 'wsl', id: 'wsl:u', distro: 'U', label: 'U' } // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: every host call is mocked at its module. @@ -64,10 +85,17 @@ function holderRejectingOn(gate: Promise): WslDistroHolder { } } +/** What WSL_HOST_FACTS_COMMAND prints. */ +function facts(kernel: string, home: string, uname: string): string { + return `__ORCA_WSL_KERNEL__ ${kernel}\n__ORCA_WSL_HOME__ ${home}\n\n__ORCA_REMOTE_PLATFORM__ ${uname}\n` +} + beforeEach(() => { mocks.spawned.length = 0 mocks.refused.length = 0 mocks.probeStep = null + mocks.facts = facts('5.15.167.4-microsoft-standard-WSL2', '/home/u', 'Linux x86_64') + mocks.contextArgs = [] }) describe('holdWslDistroWithContext', () => { @@ -90,14 +118,14 @@ describe('holdWslDistroWithContext', () => { const held = holdWslDistroWithContext('U', target, conn, hold, undefined).finally(() => { settled = true }) - for (let step = 0; step < 3; step++) { + for (let step = 0; step < 2; step++) { await vi.waitFor(() => expect(mocks.probeStep).not.toBeNull()) const next = mocks.probeStep mocks.probeStep = null next?.() } await vi.waitFor(() => - expect(mocks.spawned).toEqual(['uname -r', 'uname -sm', 'echo $HOME', 'ldd --version']) + expect(mocks.spawned).toEqual(['host-facts', 'ldd --version', 'cat orcad-active.json']) ) // The deploy awaits this before recovery, which may launch orcad. expect(settled).toBe(false) @@ -124,14 +152,101 @@ describe('holdWslDistroWithContext', () => { ) await vi.waitFor(() => expect(mocks.probeStep).not.toBeNull()) mocks.probeStep?.() - await vi.waitFor(() => expect(mocks.spawned).toEqual(['uname -r', 'uname -sm'])) + await vi.waitFor(() => expect(mocks.spawned).toEqual(['host-facts', 'ldd --version'])) failHold() await expect(held).rejects.toThrow('holder exited') mocks.probeStep?.() - await vi.waitFor(() => expect(mocks.refused).toEqual(['echo $HOME'])) + await vi.waitFor(() => expect(mocks.refused).toEqual(['cat orcad-active.json'])) - expect(mocks.spawned).toEqual(['uname -r', 'uname -sm']) + expect(mocks.spawned).toEqual(['host-facts', 'ldd --version']) + }) +}) + +describe('the folded host facts probe', () => { + function heldNow(): WslDistroHolder { + return { + ...holderRejectingOn(Promise.resolve()), + ensureHeld: vi.fn(async (distro: string) => ({ + outcome: 'adopted' as const, + record: { version: 1 as const, distro, pid: 9, creationTimeMs: 1, startedAtMs: 1 } + })) + } + } + + async function resolveWith(facts: string) { + mocks.facts = facts + const held = holdWslDistroWithContext('U', target, conn, heldNow(), undefined) + for (let step = 0; step < 2; step++) { + await vi.waitFor(() => expect(mocks.probeStep).not.toBeNull()) + const next = mocks.probeStep + mocks.probeStep = null + next?.() + } + return held + } + + it('hands the context the platform and login home it read in the same exec', async () => { + await resolveWith(mocks.facts) + + expect(mocks.spawned.filter((label) => label === 'host-facts')).toHaveLength(1) + expect(mocks.contextArgs).toEqual([ + expect.objectContaining({ os: 'linux', arch: 'x64' }), + '/home/u' + ]) + }) + + it('leaves the context to probe what the facts did not prove', async () => { + await resolveWith(facts('5.15.167.4-microsoft-standard-WSL2', '', 'Linux sparc')) + + expect(mocks.contextArgs).toEqual([undefined, undefined]) + expect(mocks.spawned[0]).toBe('host-facts') + expect(mocks.spawned).not.toContain('uname -r') + }) + + it('falls back to the separate probes for a HOME with a newline, never a truncated path', async () => { + await resolveWith(facts('5.15.167.4-microsoft-standard-WSL2', '/home/a\nb', 'Linux x86_64')) + + expect(mocks.contextArgs).toEqual([undefined, undefined]) + expect(mocks.spawned.slice(0, 2)).toEqual(['host-facts', 'uname -r']) + }) + + it('falls back without throwing when stdout noise precedes the facts', async () => { + await resolveWith( + `Welcome to Ubuntu\n${facts('5.15.167.4-microsoft-standard-WSL2', '/home/u', 'Linux x86_64')}` + ) + + expect(mocks.contextArgs).toEqual([undefined, undefined]) + expect(mocks.spawned.slice(0, 2)).toEqual(['host-facts', 'uname -r']) + }) + + it('refuses WSL 1 from the kernel line before holding or probing', async () => { + mocks.facts = facts('4.4.0-19041-Microsoft', '/home/u', 'Linux x86_64') + const holder = heldNow() + + await expect( + holdWslDistroWithContext('U', target, conn, holder, undefined) + ).rejects.toMatchObject({ + name: 'OrcadHostUnsupportedError' + }) + expect(holder.ensureHeld).not.toHaveBeenCalled() + expect(mocks.spawned).toEqual(['host-facts']) + }) +}) + +describe.skipIf(process.platform === 'win32')('WSL_HOST_FACTS_COMMAND under a real sh', () => { + it('prints exactly the shape readWslHostFacts accepts', async () => { + const result = await runProcess({ + program: 'sh', + args: ['-c', WSL_HOST_FACTS_COMMAND], + env: { ...process.env, HOME: '/home/orca user' }, + timeoutMs: 10_000 + }) + + expect(readWslHostFacts(result.stdout)).toMatchObject({ + kernel: expect.stringMatching(/\S/), + loginHome: '/home/orca user' + }) }) }) diff --git a/src/main/wsl/wsl-orcad-held-context.ts b/src/main/wsl/wsl-orcad-held-context.ts index 513810edc18..ea52c0a7f1d 100644 --- a/src/main/wsl/wsl-orcad-held-context.ts +++ b/src/main/wsl/wsl-orcad-held-context.ts @@ -1,10 +1,50 @@ import { resolveOrcadRemoteContext } from '../managed-server/install/orcad-remote-context' import type { OrcadHostTransport } from '../ssh/orcad-host-transport' import { execCommand } from '../ssh/remote-exec/remote-exec-command' +import { + remoteHostPlatformFromUnameProbe, + UNAME_PLATFORM_PROBE_COMMAND, + UNAME_PLATFORM_PROBE_MARKER +} from '../ssh/ssh-remote-platform-detection' +import type { RemoteHostPlatform } from '../ssh/ssh-remote-platform' import type { WslDistroHolder } from './wsl-distro-holder' import { refuseMissingWslDistro, WslDistroVersion1Error } from './wsl-distro-unsupported' import type { WslOrcadHostTarget } from './wsl-orcad-host-target' +const KERNEL_MARKER = '__ORCA_WSL_KERNEL__ ' +const HOME_MARKER = '__ORCA_WSL_HOME__ ' +export const WSL_HOST_FACTS_COMMAND = `printf '${KERNEL_MARKER}%s\\n${HOME_MARKER}%s\\n' "$(uname -r)" "$HOME"; ${UNAME_PLATFORM_PROBE_COMMAND}` + +type WslHostFacts = { kernel: string; loginHome?: string; platform?: RemoteHostPlatform } + +/** + * Accepts only the exact shape the facts command prints; anything else (startup noise, a HOME + * with a newline) returns null so the caller probes each fact on its own. + */ +export function readWslHostFacts(output: string): WslHostFacts | null { + const lines = output.split('\n') + const [kernelLine = '', homeLine = '', gap, platformLine = '', ...rest] = lines + if ( + !kernelLine.startsWith(KERNEL_MARKER) || + !homeLine.startsWith(HOME_MARKER) || + gap !== '' || + !platformLine.startsWith(UNAME_PLATFORM_PROBE_MARKER) || + rest.some((line) => line.trim() !== '') + ) { + return null + } + const loginHome = homeLine.slice(HOME_MARKER.length) + return { + kernel: kernelLine.slice(KERNEL_MARKER.length).trim(), + ...(loginHome.trim() ? { loginHome } : {}), + ...withPlatform(remoteHostPlatformFromUnameProbe(`\n${platformLine}`)) + } +} + +function withPlatform(platform: RemoteHostPlatform | null): { platform?: RemoteHostPlatform } { + return platform ? { platform } : {} +} + /** WSL 1 runs no Linux kernel, so the pinned Node and its pty cannot run there. */ export function isWsl1KernelRelease(release: string): boolean { return /^\d+\.\d+\.\d+-\d+-Microsoft$/.test(release.trim()) @@ -22,8 +62,13 @@ export async function holdWslDistroWithContext( holder: WslDistroHolder, signal: AbortSignal | undefined ) { - const kernel = await execCommand(conn, 'uname -r', { signal }).catch(refuseMissingWslDistro(name)) - if (isWsl1KernelRelease(kernel)) { + // One exec for the kernel, the login home and the platform the context would probe one by one. + const facts = readWslHostFacts( + await execCommand(conn, WSL_HOST_FACTS_COMMAND, { signal }).catch(refuseMissingWslDistro(name)) + ) ?? + // Unrecognized output proves nothing: read the kernel alone, as before, and let the context probe. + { kernel: await execCommand(conn, 'uname -r', { signal }).catch(refuseMissingWslDistro(name)) } + if (isWsl1KernelRelease(facts.kernel)) { throw new WslDistroVersion1Error(name) } // Once the hold fails, stop the probes: after dispose they fall back to fresh wsl.exe execs, @@ -40,7 +85,7 @@ export async function holdWslDistroWithContext( probes.abort(error) throw error }), - resolveOrcadRemoteContext(target, conn, probes.signal) + resolveOrcadRemoteContext(target, conn, probes.signal, facts.platform, facts.loginHome) ]) return { held, context } } catch (error) {