From a55bfbbf9b46123876c90b1110d023e16e4eb970 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:31:01 -0700 Subject: [PATCH] perf(wsl): fold the first libc probe into the WSL host facts exec (#35324) * perf(wsl): fold the first libc probe into the WSL host facts exec The WSL connect's facts exec now also runs the libc probe's first command after its own marker. resolveOrcadRemoteContext takes it (with the login home) as a known fact, answering that one probe; the fallbacks still run over the transport if it proves nothing. One fewer serial guest round trip per connect; SSH passes no known facts, so its probes are unchanged. * fix(wsl): run the libc fallbacks through the default exec With a folded first answer, the remaining libc probes used the fenced orcad runner; they now use the same plain execCommand the default probe does, so they match main exactly. --- .../install/orcad-remote-context.ts | 27 ++++++++++++--- .../install/orcad-remote-primitives.test.ts | 34 +++++++++++++++++-- src/main/node-runtime/linux-libc-probe.ts | 5 ++- src/main/wsl/wsl-orcad-deploy.test.ts | 2 +- src/main/wsl/wsl-orcad-held-context.test.ts | 24 +++++++++---- src/main/wsl/wsl-orcad-held-context.ts | 27 +++++++++++---- 6 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/main/managed-server/install/orcad-remote-context.ts b/src/main/managed-server/install/orcad-remote-context.ts index 2e9f945a5a7..ad2663aff35 100644 --- a/src/main/managed-server/install/orcad-remote-context.ts +++ b/src/main/managed-server/install/orcad-remote-context.ts @@ -3,6 +3,8 @@ import type { SshTarget } from '../../../shared/ssh-types' import type { OrcadActivationRecord } from '../activation/orcad-activation-record' import { readOrcadActivationRecord } from '../activation/orcad-activation-record-store' import { resolveOrcadRuntimeTarget } from '../lifecycle/orcad-runtime-target' +import { LINUX_LIBC_FIRST_PROBE } from '../../node-runtime/linux-libc-probe' +import { execCommand } from '../../ssh/remote-exec/remote-exec-command' import { prepareWindowsOrcadHost } from '../host-state/orcad-windows-host-preparation' import { isWslOrcadHostTarget, @@ -53,8 +55,11 @@ export async function resolveOrcadRemoteContext> { const host = detectedHost ?? (await detectRemoteHostPlatform(connection, { signal })) if (!host) { @@ -68,12 +73,26 @@ export async function resolveOrcadRemoteContext { + if (command === LINUX_LIBC_FIRST_PROBE && firstLibcProbeOutput !== undefined) { + const output = firstLibcProbeOutput + firstLibcProbeOutput = undefined + return output + } + // The fallbacks run exactly as the default exec would. + return execCommand(connection, command, { signal }) + } + }) }) if (!isWslOrcadHostTarget(target)) { rememberSshHostPlatform(target.id, host, serverTarget) 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 70ebf43a094..bed3ea5b032 100644 --- a/src/main/managed-server/install/orcad-remote-primitives.test.ts +++ b/src/main/managed-server/install/orcad-remote-primitives.test.ts @@ -292,10 +292,40 @@ describe('orcad remote context', () => { }) const wslTarget = { kind: 'wsl', id: 'wsl:u', distro: 'U', label: 'U' } as const - const context = await resolveOrcadRemoteContext(wslTarget, conn, undefined, linux, '/home/u\n') + const context = await resolveOrcadRemoteContext(wslTarget, conn, undefined, linux, { + loginHome: '/home/u\n', + firstLibcProbeOutput: 'ldd (Ubuntu GLIBC 2.39-0ubuntu8) 2.39\n' + }) - expect(context).toMatchObject({ remoteHome: '/home/u', userDataDir: '/home/u/.orca' }) + expect(context).toMatchObject({ + remoteHome: '/home/u', + userDataDir: '/home/u/.orca', + serverTarget: 'linux-x64-glibc' + }) expect(detectRemoteHostPlatform).not.toHaveBeenCalled() expect(commands.some((command) => command.includes('$HOME'))).toBe(false) + expect(commands.some((command) => command.includes('ldd'))).toBe(false) + }) + + it('runs the libc fallbacks when the folded first probe proves nothing', async () => { + vi.mocked(detectRemoteHostPlatform).mockClear() + const commands: string[] = [] + mockExec.mockImplementation(async (_conn, command: string) => { + commands.push(command) + return command.includes('getconf') ? 'glibc 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, { + loginHome: '/home/u', + firstLibcProbeOutput: 'sh: ldd: not found\n' + }) + + expect(context.serverTarget).toBe('linux-x64-glibc') + // The same plain exec the default probe uses, not the fenced orcad runner. + expect(mockExec).toHaveBeenCalledWith(conn, expect.stringContaining('getconf'), { + signal: undefined + }) + expect(commands.some((command) => command.includes('ldd --version'))).toBe(false) }) }) diff --git a/src/main/node-runtime/linux-libc-probe.ts b/src/main/node-runtime/linux-libc-probe.ts index 1713368bf43..53d0b60f8b2 100644 --- a/src/main/node-runtime/linux-libc-probe.ts +++ b/src/main/node-runtime/linux-libc-probe.ts @@ -35,8 +35,11 @@ export function parseGlibcVersion(output: string): GlibcVersion | null { return match ? { major: Number(match[1]), minor: Number(match[2]) } : null } +/** The first probe, which answers on nearly every glibc or musl host; callers may run it early. */ +export const LINUX_LIBC_FIRST_PROBE = 'LC_ALL=C ldd --version 2>&1 || true' + const PROBES = [ - 'LC_ALL=C ldd --version 2>&1 || true', + LINUX_LIBC_FIRST_PROBE, 'LC_ALL=C getconf GNU_LIBC_VERSION 2>/dev/null || true', // Appliances may omit both tools; newer GNU loaders can report their own version. 'for loader in /lib/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 /lib/ld-linux-aarch64.so.1; do ' + diff --git a/src/main/wsl/wsl-orcad-deploy.test.ts b/src/main/wsl/wsl-orcad-deploy.test.ts index 165b8953b25..41195fdaf44 100644 --- a/src/main/wsl/wsl-orcad-deploy.test.ts +++ b/src/main/wsl/wsl-orcad-deploy.test.ts @@ -110,7 +110,7 @@ function launch(kernel = '5.15.167.4-microsoft-standard-WSL2', options = {}) { mocks.exec.mockImplementation(async (_conn: unknown, command: string) => { if (command.includes('uname -r')) { mocks.steps.push('uname') - return `__ORCA_WSL_KERNEL__ ${kernel}\n__ORCA_WSL_HOME__ /home/u\n\n__ORCA_REMOTE_PLATFORM__ Linux x86_64\n` + return `__ORCA_WSL_KERNEL__ ${kernel}\n__ORCA_WSL_HOME__ /home/u\n\n__ORCA_REMOTE_PLATFORM__ Linux x86_64\n\n__ORCA_WSL_LIBC__\nldd (GNU libc) 2.39\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 552c1e146ae..dc5f42985dd 100644 --- a/src/main/wsl/wsl-orcad-held-context.test.ts +++ b/src/main/wsl/wsl-orcad-held-context.test.ts @@ -86,8 +86,8 @@ 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` +function facts(kernel: string, home: string, uname: string, libc = 'ldd (GNU libc) 2.39'): string { + return `__ORCA_WSL_KERNEL__ ${kernel}\n__ORCA_WSL_HOME__ ${home}\n\n__ORCA_REMOTE_PLATFORM__ ${uname}\n\n__ORCA_WSL_LIBC__\n${libc}\n` } beforeEach(() => { @@ -192,14 +192,14 @@ describe('the folded host facts probe', () => { expect(mocks.spawned.filter((label) => label === 'host-facts')).toHaveLength(1) expect(mocks.contextArgs).toEqual([ expect.objectContaining({ os: 'linux', arch: 'x64' }), - '/home/u' + { loginHome: '/home/u', firstLibcProbeOutput: 'ldd (GNU libc) 2.39\n' } ]) }) 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.contextArgs).toEqual([undefined, { loginHome: undefined }]) expect(mocks.spawned[0]).toBe('host-facts') expect(mocks.spawned).not.toContain('uname -r') }) @@ -207,7 +207,7 @@ describe('the folded host facts probe', () => { 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.contextArgs).toEqual([undefined, { loginHome: undefined }]) expect(mocks.spawned.slice(0, 2)).toEqual(['host-facts', 'uname -r']) }) @@ -216,10 +216,22 @@ describe('the folded host facts probe', () => { `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.contextArgs).toEqual([undefined, { loginHome: undefined }]) expect(mocks.spawned.slice(0, 2)).toEqual(['host-facts', 'uname -r']) }) + it('keeps a multi-line libc answer whole, and rejects output missing the libc section', () => { + const musl = 'musl libc (x86_64)\nVersion 1.2.4\nDynamic Program Loader' + expect( + readWslHostFacts(facts('5.15.167.4-microsoft-standard-WSL2', '/home/u', 'Linux x86_64', musl)) + ).toMatchObject({ firstLibcProbeOutput: `${musl}\n` }) + expect( + readWslHostFacts( + '__ORCA_WSL_KERNEL__ 5.15\n__ORCA_WSL_HOME__ /home/u\n\n__ORCA_REMOTE_PLATFORM__ Linux x86_64\n' + ) + ).toBeNull() + }) + 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() diff --git a/src/main/wsl/wsl-orcad-held-context.ts b/src/main/wsl/wsl-orcad-held-context.ts index ea52c0a7f1d..df8314192d8 100644 --- a/src/main/wsl/wsl-orcad-held-context.ts +++ b/src/main/wsl/wsl-orcad-held-context.ts @@ -7,15 +7,23 @@ import { UNAME_PLATFORM_PROBE_MARKER } from '../ssh/ssh-remote-platform-detection' import type { RemoteHostPlatform } from '../ssh/ssh-remote-platform' +import { LINUX_LIBC_FIRST_PROBE } from '../node-runtime/linux-libc-probe' 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}` +const LIBC_MARKER = '__ORCA_WSL_LIBC__' +// The libc probe goes last: its output may span lines, so it takes everything after its marker. +export const WSL_HOST_FACTS_COMMAND = `printf '${KERNEL_MARKER}%s\\n${HOME_MARKER}%s\\n' "$(uname -r)" "$HOME"; ${UNAME_PLATFORM_PROBE_COMMAND}; printf '\\n%s\\n' '${LIBC_MARKER}'; ${LINUX_LIBC_FIRST_PROBE}` -type WslHostFacts = { kernel: string; loginHome?: string; platform?: RemoteHostPlatform } +type WslHostFacts = { + kernel: string + loginHome?: string + platform?: RemoteHostPlatform + firstLibcProbeOutput?: string +} /** * Accepts only the exact shape the facts command prints; anything else (startup noise, a HOME @@ -23,13 +31,15 @@ type WslHostFacts = { kernel: string; loginHome?: string; platform?: RemoteHostP */ export function readWslHostFacts(output: string): WslHostFacts | null { const lines = output.split('\n') - const [kernelLine = '', homeLine = '', gap, platformLine = '', ...rest] = lines + const [kernelLine = '', homeLine = '', gap, platformLine = '', libcGap, libcMarker, ...libc] = + lines if ( !kernelLine.startsWith(KERNEL_MARKER) || !homeLine.startsWith(HOME_MARKER) || gap !== '' || !platformLine.startsWith(UNAME_PLATFORM_PROBE_MARKER) || - rest.some((line) => line.trim() !== '') + libcGap !== '' || + libcMarker !== LIBC_MARKER ) { return null } @@ -37,7 +47,8 @@ export function readWslHostFacts(output: string): WslHostFacts | null { return { kernel: kernelLine.slice(KERNEL_MARKER.length).trim(), ...(loginHome.trim() ? { loginHome } : {}), - ...withPlatform(remoteHostPlatformFromUnameProbe(`\n${platformLine}`)) + ...withPlatform(remoteHostPlatformFromUnameProbe(`\n${platformLine}`)), + firstLibcProbeOutput: libc.join('\n') } } @@ -85,7 +96,11 @@ export async function holdWslDistroWithContext( probes.abort(error) throw error }), - resolveOrcadRemoteContext(target, conn, probes.signal, facts.platform, facts.loginHome) + resolveOrcadRemoteContext(target, conn, probes.signal, facts.platform, { + loginHome: facts.loginHome, + // Only with a proven platform: the libc answer belongs to the same host shape. + ...(facts.platform ? { firstLibcProbeOutput: facts.firstLibcProbeOutput } : {}) + }) ]) return { held, context } } catch (error) {