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

@ -176,6 +176,13 @@ This can only be done by the superuser.
.B F8, [
Decrease the selected process's priority (add to 'nice' value)
.TP
.B }
Increase the selected process's autogroup priority (subtract from autogroup 'nice' value).
This can only be done by the superuser.
.TP
.B {
Decrease the selected process's autogroup priority (add to autogroup 'nice' value)
.TP
.B F9, k
"Kill" process: sends a signal which is selected in a menu, to one or a group
of processes. If processes were tagged, sends the signal to all tagged processes.
@ -484,6 +491,12 @@ The command name for the process. Requires Linux kernel 2.6.33 or newer.
.B EXE
The executable file of the process as reported by the kernel. Requires CAP_SYS_PTRACE and PTRACE_MODE_READ_FSCRED.
.TP
.B AGRP
The autogroup identifier for the process. Requires Linux CFS to be enabled.
.TP
.B ANI
The autogroup nice value for the process autogroup. Requires Linux CFS to be enabled.
.TP
.B All other flags
Currently unsupported (always displays '-').
.SH "EXTERNAL LIBRARIES"

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

View File

@ -82,6 +82,8 @@ const ProcessFieldData Process_fields[] = {
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process", .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* PCPProcess_new(const Settings* settings) {
@ -168,6 +170,25 @@ static void PCPProcess_writeField(const Process* this, RichString* str, ProcessF
xSnprintf(buffer, n, "%5lu ", pp->ctxt_diff);
break;
case SECATTR: snprintf(buffer, n, "%-30s ", pp->secattr ? pp->secattr : "?"); break;
case AUTOGROUP_ID:
if (pp->autogroup_id != -1) {
xSnprintf(buffer, n, "%4ld ", pp->autogroup_id);
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
break;
case AUTOGROUP_NICE:
if (pp->autogroup_id != -1) {
xSnprintf(buffer, n, "%3d ", pp->autogroup_nice);
attr = pp->autogroup_nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: pp->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;
@ -245,6 +266,10 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process
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

@ -20,11 +20,12 @@ in the source distribution for its full text.
#include "Settings.h"
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#define PROCESS_FLAG_LINUX_SMAPS 0x2000
#define PROCESS_FLAG_LINUX_CTXT 0x4000
#define PROCESS_FLAG_LINUX_SECATTR 0x8000
#define PROCESS_FLAG_LINUX_CGROUP 0x00000800
#define PROCESS_FLAG_LINUX_OOM 0x00001000
#define PROCESS_FLAG_LINUX_SMAPS 0x00002000
#define PROCESS_FLAG_LINUX_CTXT 0x00004000
#define PROCESS_FLAG_LINUX_SECATTR 0x00008000
#define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000
typedef struct PCPProcess_ {
Process super;
@ -70,6 +71,8 @@ typedef struct PCPProcess_ {
double io_rate_read_bps;
double io_rate_write_bps;
char* cgroup;
long int autogroup_id;
int autogroup_nice;
unsigned int oom;
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;

View File

@ -89,13 +89,20 @@ void ProcessList_delete(ProcessList* pl) {
free(this);
}
static inline unsigned long Metric_instance_s32(int metric, int pid, int offset, unsigned long fallback) {
static inline long Metric_instance_s32(int metric, int pid, int offset, long fallback) {
pmAtomValue value;
if (Metric_instance(metric, pid, offset, &value, PM_TYPE_32))
return value.l;
return fallback;
}
static inline long long Metric_instance_s64(int metric, int pid, int offset, long long fallback) {
pmAtomValue value;
if (Metric_instance(metric, pid, offset, &value, PM_TYPE_64))
return value.l;
return fallback;
}
static inline unsigned long Metric_instance_u32(int metric, int pid, int offset, unsigned long fallback) {
pmAtomValue value;
if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U32))
@ -222,6 +229,11 @@ static void PCPProcessList_readOomData(PCPProcess* pp, int pid, int offset) {
pp->oom = Metric_instance_u32(PCP_PROC_OOMSCORE, pid, offset, 0);
}
static void PCPProcessList_readAutogroup(PCPProcess* pp, int pid, int offset) {
pp->autogroup_id = Metric_instance_s64(PCP_PROC_AUTOGROUP_ID, pid, offset, -1);
pp->autogroup_nice = Metric_instance_s32(PCP_PROC_AUTOGROUP_NICE, pid, offset, 0);
}
static void PCPProcessList_readCtxtData(PCPProcess* pp, int pid, int offset) {
pmAtomValue value;
unsigned long ctxt = 0;
@ -403,6 +415,9 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (settings->flags & PROCESS_FLAG_CWD)
PCPProcessList_readCwd(pp, pid, offset);
if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP)
PCPProcessList_readAutogroup(pp, pid, offset);
if (proc->state == 'Z' && !proc->cmdline && command[0]) {
Process_updateCmdline(proc, command, 0, strlen(command));
} else if (Process_isThread(proc)) {
@ -651,6 +666,9 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
Metric_enable(PCP_PROC_NVCTXSW, flagged && enabled);
flagged = settings->flags & PROCESS_FLAG_LINUX_SECATTR;
Metric_enable(PCP_PROC_LABELS, flagged && enabled);
flagged = settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP;
Metric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled);
Metric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled);
/* Sample smaps metrics on every second pass to improve performance */
static int smaps_flag;

View File

@ -224,6 +224,8 @@ static const char* Platform_metricNames[] = {
[PCP_PROC_TTYNAME] = "proc.psinfo.ttyname",
[PCP_PROC_EXE] = "proc.psinfo.exe",
[PCP_PROC_CWD] = "proc.psinfo.cwd",
[PCP_PROC_AUTOGROUP_ID] = "proc.autogroup.id",
[PCP_PROC_AUTOGROUP_NICE] = "proc.autogroup.nice",
[PCP_PROC_ID_UID] = "proc.id.uid",
[PCP_PROC_ID_USER] = "proc.id.uid_nm",
[PCP_PROC_IO_RCHAR] = "proc.io.rchar",

View File

@ -210,6 +210,9 @@ typedef enum Metric_ {
PCP_PROC_EXE, /* proc.psinfo.exe */
PCP_PROC_CWD, /* proc.psinfo.cwd */
PCP_PROC_AUTOGROUP_ID, /* proc.autogroup.id */
PCP_PROC_AUTOGROUP_NICE, /* proc.autogroup.nice */
PCP_PROC_ID_UID, /* proc.id.uid */
PCP_PROC_ID_USER, /* proc.id.uid_nm */

View File

@ -43,8 +43,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