Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ui/processTree/darwinProcessTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export class DarwinProcessTree extends BaseProcessTree {
let commandOffset: number | void;
return line => {
if (!commandOffset) {
commandOffset = line.indexOf('COMMAND');
// procps on Linux also labels the comm column COMMAND, so use the last one.
commandOffset = line.lastIndexOf('COMMAND');
return;
}

Expand Down
11 changes: 5 additions & 6 deletions src/ui/processTree/posixProcessTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ export class PosixProcessTree extends DarwinProcessTree {
return;
}

let pos = process.args.indexOf(process.command);
if (pos === -1) {
return process;
}

pos = pos + process.command.length;
// comm is the name of the main thread, which is not always the binary:
// Node 24 renames it to "MainThread", and a title with spaces (pm2 sets
// "node <script>") is not a binary name. Fall back to argv[0] then.
let pos = /\s/.test(process.command) ? -1 : process.args.indexOf(process.command);
pos = pos === -1 ? 0 : pos + process.command.length;
while (pos < process.args.length) {
if (process.args[pos] === ' ') {
break;
Expand Down
73 changes: 73 additions & 0 deletions src/ui/processTree/processTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*--------------------------------------------------------*/

import { expect } from 'chai';
import { promises as fsPromises } from 'fs';
import { LocalFsUtils } from '../../common/fsUtils';
import { PosixProcessTree } from './posixProcessTree';
import { analyseArguments } from './processTree';

describe('process tree', () => {
Expand Down Expand Up @@ -36,4 +39,74 @@ describe('process tree', () => {
});
}
});

describe('posix ps output', () => {
class TestPosixProcessTree extends PosixProcessTree {
public parse(lines: string[]) {
const parser = this.createParser();
return lines.map(parser).filter(p => !!p);
}
}

// procps labels both the comm and command columns COMMAND, right aligns
// the ids, and pads comm to the 30 columns that PosixProcessTree asks for.
const row = (pid: string, ppid: string, comm: string, command: string) =>
`${pid.padStart(7)} ${ppid.padStart(7)} ${comm.padEnd(30)} ${command}`;

const tt = [
{
name: 'node',
comm: 'node',
input: '/usr/bin/node /app/server.js',
command: '/usr/bin/node',
args: '/app/server.js',
},
{
name: 'node 24, whose main thread is named MainThread',
comm: 'MainThread',
input: 'node /app/server.js',
command: 'node',
args: '/app/server.js',
},
{
name: 'node 24 without arguments',
comm: 'MainThread',
input: 'node',
command: 'node',
args: '',
},
{
name: 'node 25, whose main thread is named node-MainThread',
comm: 'node-MainThread',
input: '/usr/local/bin/node /app/server.js',
command: '/usr/local/bin/node',
args: '/app/server.js',
},
{
name: 'a pm2 app, whose title is "node <script>"',
comm: 'node /home/me/a',
input: 'node /home/me/app/server.js',
command: 'node',
args: '/home/me/app/server.js',
},
{
name: 'a process that set its title',
comm: 'my-long-server-',
input: 'my-long-server-name',
command: 'my-long-server-name',
args: '',
},
];

for (const t of tt) {
it(`should parse ${t.name}`, () => {
const tree = new TestPosixProcessTree(new LocalFsUtils(fsPromises));
const parsed = tree.parse([
row('PID', 'PPID', 'COMMAND', 'COMMAND'),
row('4242', '1', t.comm, t.input),
]);
expect(parsed).to.deep.equal([{ pid: 4242, ppid: 1, command: t.command, args: t.args }]);
});
}
});
});