( pid: string | number, maxDepth = 10, )
| 34 | * @returns Array of ancestor PIDs from immediate parent to furthest ancestor |
| 35 | */ |
| 36 | export async function getAncestorPidsAsync( |
| 37 | pid: string | number, |
| 38 | maxDepth = 10, |
| 39 | ): Promise<number[]> { |
| 40 | if (process.platform === 'win32') { |
| 41 | // For Windows, use a PowerShell script that walks the process tree |
| 42 | const script = ` |
| 43 | $pid = ${String(pid)} |
| 44 | $ancestors = @() |
| 45 | for ($i = 0; $i -lt ${maxDepth}; $i++) { |
| 46 | $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$pid" -ErrorAction SilentlyContinue |
| 47 | if (-not $proc -or -not $proc.ParentProcessId -or $proc.ParentProcessId -eq 0) { break } |
| 48 | $pid = $proc.ParentProcessId |
| 49 | $ancestors += $pid |
| 50 | } |
| 51 | $ancestors -join ',' |
| 52 | `.trim() |
| 53 | |
| 54 | const result = await execFileNoThrowWithCwd( |
| 55 | 'powershell.exe', |
| 56 | ['-NoProfile', '-Command', script], |
| 57 | { timeout: 3000 }, |
| 58 | ) |
| 59 | if (result.code !== 0 || !result.stdout?.trim()) { |
| 60 | return [] |
| 61 | } |
| 62 | return result.stdout |
| 63 | .trim() |
| 64 | .split(',') |
| 65 | .filter(Boolean) |
| 66 | .map(p => parseInt(p, 10)) |
| 67 | .filter(p => !isNaN(p)) |
| 68 | } |
| 69 | |
| 70 | // For Unix, use a shell command that walks up the process tree |
| 71 | // This uses a single process invocation instead of multiple sequential calls |
| 72 | const script = `pid=${String(pid)}; for i in $(seq 1 ${maxDepth}); do ppid=$(ps -o ppid= -p $pid 2>/dev/null | tr -d ' '); if [ -z "$ppid" ] || [ "$ppid" = "0" ] || [ "$ppid" = "1" ]; then break; fi; echo $ppid; pid=$ppid; done` |
| 73 | |
| 74 | const result = await execFileNoThrowWithCwd('sh', ['-c', script], { |
| 75 | timeout: 3000, |
| 76 | }) |
| 77 | if (result.code !== 0 || !result.stdout?.trim()) { |
| 78 | return [] |
| 79 | } |
| 80 | return result.stdout |
| 81 | .trim() |
| 82 | .split('\n') |
| 83 | .filter(Boolean) |
| 84 | .map(p => parseInt(p, 10)) |
| 85 | .filter(p => !isNaN(p)) |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Gets the command line for a given process |
no test coverage detected