Add columns for process autogroup identifier and nice value

Adds AGRP (autogroup) and ANI (autogroup nice) columns that
report the information from /proc/PID/autogroup, as well as
handlers for '{' and '}' to change the autogroup nice value.

This is guarded by /proc/sys/kernel/sched_autogroup_enabled
such that sampling and/or changing values wont be attempted
unless the kernel feature is enabled.

Fixes: #720
This commit is contained in:
Nathan Scott
2021-08-06 16:45:30 +10:00
committed by BenBE
parent aa0424ade8
commit 1bd95983b2
13 changed files with 201 additions and 10 deletions

View File

@ -100,6 +100,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
[AUTOGROUP_ID] = { .name = "AUTOGROUP_ID", .title = "AGRP", .description = "The autogroup identifier of the process", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
[AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
};
Process* LinuxProcess_new(const Settings* settings) {
@ -159,6 +161,37 @@ bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) {
return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i);
}
bool LinuxProcess_isAutogroupEnabled(void) {
char buf[16];
if (xReadfile(PROCDIR "/sys/kernel/sched_autogroup_enabled", buf, sizeof(buf)) < 0)
return false;
return buf[0] == '1';
}
bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) {
char buffer[256];
xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", this->pid);
FILE* file = fopen(buffer, "r+");
if (!file)
return false;
long int identity;
int nice;
int ok = fscanf(file, "/autogroup-%ld nice %d", &identity, &nice);
bool success;
if (ok == 2) {
rewind(file);
xSnprintf(buffer, sizeof(buffer), "%d", nice + delta.i);
success = fputs(buffer, file) > 0;
} else {
success = false;
}
fclose(file);
return success;
}
#ifdef HAVE_DELAYACCT
static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
if (isnan(delay_percent)) {
@ -259,6 +292,25 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
break;
case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
case AUTOGROUP_ID:
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%4ld ", lp->autogroup_id);
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
break;
case AUTOGROUP_NICE:
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%3d ", lp->autogroup_nice);
attr = lp->autogroup_nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: lp->autogroup_nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
: CRT_colors[PROCESS_SHADOW];
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "N/A ");
}
break;
default:
Process_writeField(this, str, field);
return;
@ -350,6 +402,10 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
return SPACESHIP_NUMBER(p1->ctxt_diff, p2->ctxt_diff);
case SECATTR:
return SPACESHIP_NULLSTR(p1->secattr, p2->secattr);
case AUTOGROUP_ID:
return SPACESHIP_NUMBER(p1->autogroup_id, p2->autogroup_id);
case AUTOGROUP_NICE:
return SPACESHIP_NUMBER(p1->autogroup_nice, p2->autogroup_nice);
default:
return Process_compareByKey_Base(v1, v2, key);
}

View File

@ -28,6 +28,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_SECATTR 0x00008000
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
#define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000
#define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000
typedef struct LinuxProcess_ {
Process super;
@ -99,6 +100,10 @@ typedef struct LinuxProcess_ {
unsigned long ctxt_diff;
char* secattr;
unsigned long long int last_mlrs_calctime;
/* Autogroup scheduling (CFS) information */
long int autogroup_id;
int autogroup_nice;
} LinuxProcess;
extern int pageSize;
@ -117,6 +122,10 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
bool LinuxProcess_isAutogroupEnabled(void);
bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta);
bool Process_isThread(const Process* this);
#endif

View File

@ -909,6 +909,23 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t pro
fclose(file);
}
static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t procFd) {
process->autogroup_id = -1;
char autogroup[64]; // space for two numeric values and fixed length strings
ssize_t amtRead = xReadfileat(procFd, "autogroup", autogroup, sizeof(autogroup));
if (amtRead < 0)
return;
long int identity;
int nice;
int ok = sscanf(autogroup, "/autogroup-%ld nice %d", &identity, &nice);
if (ok == 2) {
process->autogroup_id = identity;
process->autogroup_nice = nice;
}
}
static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
FILE* file = fopenat(procFd, "status", "r");
if (!file)
@ -1521,6 +1538,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
LinuxProcessList_readCwd(lp, procFd);
}
if ((settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) {
LinuxProcessList_readAutogroup(lp, procFd);
}
if (proc->state == 'Z' && !proc->cmdline && statCommand[0]) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
} else if (Process_isThread(proc)) {
@ -2071,6 +2092,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
return;
}
if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP) {
// Refer to sched(7) 'autogroup feature' section
// The kernel feature can be enabled/disabled through procfs at
// any time, so check for it at the start of each sample - only
// read from per-process procfs files if it's globally enabled.
this->haveAutogroup = LinuxProcess_isAutogroupEnabled();
} else {
this->haveAutogroup = false;
}
/* PROCDIR is an absolute path */
assert(PROCDIR[0] == '/');
#ifdef HAVE_OPENAT

View File

@ -71,6 +71,7 @@ typedef struct LinuxProcessList_ {
TtyDriver* ttyDrivers;
bool haveSmapsRollup;
bool haveAutogroup;
#ifdef HAVE_DELAYACCT
struct nl_sock* netlink_socket;

View File

@ -150,8 +150,38 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
if (LinuxProcess_isAutogroupEnabled() == false) {
beep();
return false;
}
bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, LinuxProcess_changeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
}
static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
if (Settings_isReadonly())
return HTOP_OK;
bool changed = Platform_changeAutogroupPriority(st->mainPanel, -1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
if (Settings_isReadonly())
return HTOP_OK;
bool changed = Platform_changeAutogroupPriority(st->mainPanel, 1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
void Platform_setBindings(Htop_Action* keys) {
keys['i'] = Platform_actionSetIOPriority;
keys['{'] = Platform_actionLowerAutogroupPriority;
keys['}'] = Platform_actionHigherAutogroupPriority;
}
const MeterClass* const Platform_meterTypes[] = {

View File

@ -44,8 +44,8 @@ in the source distribution for its full text.
M_PSSWP = 121, \
CTXT = 122, \
SECATTR = 123, \
\
DUMMY_BUMP_FIELD = CWD, \
AUTOGROUP_ID = 127, \
AUTOGROUP_NICE = 128, \
// End of list