Skip to content

Commit

Permalink
darwin: lazily set process TTY name
Browse files Browse the repository at this point in the history
Fetching the TTY name of a process is extremely expensive on darwin and
the call to devname accounts for 95% of htop's CPU usage when there is
high process turnover (this is mostly due to devname calling lstat,
which is incredibly slow). This can make htop unresponsive.

To mitigate this only set the process TTY name if the it is being
actively displayed (PROCESS_FLAG_TTY), which by default it is not
on darwin.
  • Loading branch information
charlievieth authored and BenBE committed Feb 18, 2022
1 parent 978a7c8 commit d35db47
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 9 deletions.
41 changes: 32 additions & 9 deletions darwin/DarwinProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ in the source distribution for its full text.
#include <stdlib.h>
#include <string.h>
#include <mach/mach.h>
#include <sys/dirent.h>

#include "CRT.h"
#include "Process.h"
Expand All @@ -26,7 +27,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY] = { .name = "TTY", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TTY] = { .name = "TTY", .title = "TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_TTY, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
Expand Down Expand Up @@ -276,6 +277,18 @@ static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) {
return nanoseconds / nanoseconds_per_second * centiseconds_per_second;
}

static char* DarwinProcess_getDevname(dev_t dev) {
if (dev == NODEV) {
return NULL;
}
char buf[sizeof("/dev/") + MAXNAMLEN];
char *name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN);
if (name) {
return xStrdup(name);
}
return NULL;
}

void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
DarwinProcess* dp = (DarwinProcess*)proc;

Expand Down Expand Up @@ -306,15 +319,8 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->isKernelThread = false;
proc->isUserlandThread = false;
dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;

proc->tty_nr = ps->kp_eproc.e_tdev;
const char* name = (ps->kp_eproc.e_tdev != NODEV) ? devname(ps->kp_eproc.e_tdev, S_IFCHR) : NULL;
if (!name) {
free(proc->tty_name);
proc->tty_name = NULL;
} else {
free_and_xStrdup(&proc->tty_name, name);
}
proc->tty_name = NULL;

proc->starttime_ctime = ep->p_starttime.tv_sec;
Process_fillStarttimeBuffer(proc);
Expand All @@ -327,6 +333,23 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
}
}

if (proc->tty_name == NULL && (dev_t)proc->tty_nr != NODEV) {
/* The call to devname() is extremely expensive (due to lstat)
* and represents ~95% of htop's CPU usage when there is high
* process turnover.
*
* To mitigate this we only fetch TTY information if the TTY
* field is enabled in the settings.
*/
if (proc->settings->ss->flags & PROCESS_FLAG_TTY) {
proc->tty_name = DarwinProcess_getDevname(proc->tty_nr);
if (!proc->tty_name) {
/* devname failed: prevent us from calling it again */
proc->tty_nr = NODEV;
}
}
}

/* Mutable information */
proc->nice = ep->p_nice;
proc->priority = ep->p_priority;
Expand Down
2 changes: 2 additions & 0 deletions darwin/DarwinProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ in the source distribution for its full text.
#include "darwin/DarwinProcessList.h"


#define PROCESS_FLAG_TTY 0x00000100

typedef struct DarwinProcess_ {
Process super;

Expand Down

0 comments on commit d35db47

Please sign in to comment.