60 lines
1.6 KiB
JavaScript
60 lines
1.6 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readdirSync, readFileSync, readlinkSync } from 'fs';
|
|
import { execFileSync } from 'child_process';
|
|
|
|
const window_id = process.argv[2];
|
|
if (!window_id) {
|
|
console.error('Usage: debug-window.js <window-id>');
|
|
process.exit(1);
|
|
}
|
|
|
|
function read_file(path) {
|
|
try { return readFileSync(path, 'utf8'); } catch { return null; }
|
|
}
|
|
|
|
function get_ppid(pid) {
|
|
const status = read_file(`/proc/${pid}/status`);
|
|
const match = status?.match(/^PPid:\s+(\d+)/m);
|
|
return match ? match[1] : null;
|
|
}
|
|
|
|
function get_comm(pid) {
|
|
return read_file(`/proc/${pid}/comm`)?.trim() ?? null;
|
|
}
|
|
|
|
function get_cmdline(pid) {
|
|
return read_file(`/proc/${pid}/cmdline`)?.replace(/\0/g, ' ').trim() ?? null;
|
|
}
|
|
|
|
// Get PID of the window
|
|
const out = execFileSync('xprop', ['-id', window_id, '_NET_WM_PID'], { encoding: 'utf8' });
|
|
const match = out.match(/= (\d+)/);
|
|
if (!match) {
|
|
console.error('Could not get PID for window', window_id);
|
|
process.exit(1);
|
|
}
|
|
const root_pid = match[1];
|
|
console.log(`Window ${window_id} → PID ${root_pid} (${get_comm(root_pid)})`);
|
|
|
|
// Find all descendants
|
|
const children = new Map(); // pid → [child pids]
|
|
for (const entry of readdirSync('/proc')) {
|
|
if (!/^\d+$/.test(entry)) continue;
|
|
const ppid = get_ppid(entry);
|
|
if (!ppid) continue;
|
|
if (!children.has(ppid)) children.set(ppid, []);
|
|
children.get(ppid).push(entry);
|
|
}
|
|
|
|
function print_tree(pid, depth = 0) {
|
|
const indent = ' '.repeat(depth);
|
|
const comm = get_comm(pid) ?? '?';
|
|
const cmdline = get_cmdline(pid) ?? '';
|
|
console.log(`${indent}${pid} [${comm}] ${cmdline.slice(0, 120)}`);
|
|
for (const child of children.get(pid) ?? []) {
|
|
print_tree(child, depth + 1);
|
|
}
|
|
}
|
|
|
|
print_tree(root_pid);
|