mirror of
https://github.com/xzeldon/htop.git
synced 2024-12-23 22:55:46 +00:00
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:
parent
aa0424ade8
commit
1bd95983b2
13
htop.1.in
13
htop.1.in
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -71,6 +71,7 @@ typedef struct LinuxProcessList_ {
|
||||
|
||||
TtyDriver* ttyDrivers;
|
||||
bool haveSmapsRollup;
|
||||
bool haveAutogroup;
|
||||
|
||||
#ifdef HAVE_DELAYACCT
|
||||
struct nl_sock* netlink_socket;
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user