Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compatibility with ps from BusyBox #173

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 3.0.2

- add compatibility with ps from busybox

### 3.0.1

- removed dynamic requires to allow for bundling #154
Expand Down
26 changes: 13 additions & 13 deletions lib/ps.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,16 @@ function parseTime (timestr, centisec) {
* @param {Function} done(err, stat)
*/
function ps (pids, options, done) {
const pArg = pids.join(',')
let args = ['-o', 'etime,pid,ppid,pcpu,rss,time', '-p', pArg]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my issue with this is that ps forwards way more data then needed no? Isn't there a way to do this only for busybox if there's no -p, or maybe that busybox has a procfs?

Copy link
Author

@paescuj paescuj Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your quick review!

Yeah, I had the same concerns, but opted for this simple way because I assumed it wouldn't make much difference, as the output is usually very small since only the processes of the current user are printed.

That made me realize though, that the current solution wouldn't work on non-BusyBox ps because of this exact reason! We would have to pass the ax flags which indeed means the output can become quite large.

Two alternative ideas:

  • Try to spawn ps with the -p flag, and in case of error, check the output for ps: unrecognized option: p and then respawn again without the -p flag.
  • Check if ps comes from BusyBox by doing a preflight readlink call on /bin/ps. The result should be /bin/busybox in that case. Then we could apply the logic from this PR only if it's BusyBox.

The first option would mean no performance degradation for non-BusyBox platforms, but a little one for BusyBox. Therefore probably a bit more reasonable than the second option... WDYT?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spawning is really slow, I'd go for 2nd

let args = ['-o', 'etime,pid,ppid,pcpu,rss,time']

if (PLATFORM === 'aix' || PLATFORM === 'os400') {
args = ['-o', 'etime,pid,ppid,pcpu,rssize,time', '-p', pArg]
args = ['-o', 'etime,pid,ppid,pcpu,rssize,time']
}

bin('ps', args, function (err, stdout, code) {
if (err) {
if (PLATFORM === 'os390' && /no matching processes found/.test(err)) {
err = new Error('No matching pid found')
err.code = 'ENOENT'
}

return done(err)
}
if (code === 1) {
const error = new Error('No matching pid found')
error.code = 'ENOENT'
return done(error)
}
if (code !== 0) {
return done(new Error('pidusage ps command exited with code ' + code))
}
Expand Down Expand Up @@ -102,6 +91,11 @@ function ps (pids, options, done) {
}

const pid = parseInt(line[1], 10)

if (!pids.includes(pid)) {
continue
}

let hst = history.get(pid, options.maxage)
if (hst === undefined) hst = {}

Expand All @@ -128,6 +122,12 @@ function ps (pids, options, done) {
history.set(pid, statistics[pid], options.maxage)
}

if (Object.keys(statistics).length === 0) {
const error = new Error('No matching pid found')
error.code = 'ENOENT'
return done(error)
}

done(null, statistics)
})
}
Expand Down
40 changes: 2 additions & 38 deletions test/ps.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ test('should parse ps output on Darwin', async t => {

const ps = require('../lib/ps')

const result = await pify(ps)([348932], {})
const result = await pify(ps)([430, 7166], {})
t.deepEqual(result, {
430: {
cpu: (93784070 / 319853000) * 100,
Expand All @@ -56,24 +56,6 @@ test('should parse ps output on Darwin', async t => {
elapsed: (2 * 86400 + 40 * 3600 + 50 * 60 + 53 * 1) * 1000,
timestamp: 864000000
},
432: {
cpu: (90123100 / 147053000) * 100,
memory: 2364 * 1024,
ppid: 430,
pid: 432,
ctime: (1 * 86400 + 1 * 3600 + 2 * 60 + 3 * 1) * 1000 + (10 * 10),
elapsed: (40 * 3600 + 50 * 60 + 53 * 1) * 1000,
timestamp: 864000000
},
727: {
cpu: (867260 / 6650000) * 100,
memory: 348932 * 1024,
ppid: 1,
pid: 727,
ctime: (14 * 60 + 27 * 1) * 1000 + (10 * 26),
elapsed: (1 * 3600 + 50 * 60 + 50 * 1) * 1000,
timestamp: 864000000
},
7166: {
cpu: (20 / 20000) * 100,
memory: 3756 * 1024,
Expand Down Expand Up @@ -110,7 +92,7 @@ test('should parse ps output on *nix', async t => {
//
// const ps = require('../lib/ps')
//
// const result = await pify(ps)([11678], {})
// const result = await pify(ps)([430, 7166], {})
// t.deepEqual(result, {
// 430: {
// cpu: 3.0,
Expand All @@ -121,24 +103,6 @@ test('should parse ps output on *nix', async t => {
// elapsed: (2 * 86400 + 40 * 3600 + 50 * 60 + 53 * 1) * 1000,
// timestamp: 864000000
// },
// 432: {
// cpu: 0.0,
// memory: 2364 * 1024,
// ppid: 430,
// pid: 432,
// ctime: (1 * 86400 + 1 * 3600 + 2 * 60 + 3 * 1) * 1000,
// elapsed: (40 * 3600 + 50 * 60 + 53 * 1) * 1000,
// timestamp: 864000000
// },
// 727: {
// cpu: 10.0,
// memory: 348932 * 1024,
// ppid: 1,
// pid: 727,
// ctime: (14 * 60 + 27 * 1) * 1000,
// elapsed: (1 * 3600 + 50 * 60 + 50 * 1) * 1000,
// timestamp: 864000000
// },
// 7166: {
// cpu: 0.1,
// memory: 3756 * 1024,
Expand Down