Tidy up process state handling

This commit is contained in:
marcluque 2021-10-12 00:45:09 +02:00 committed by cgzones
parent afa3fe4af1
commit d8dfbbd37c
11 changed files with 189 additions and 137 deletions

View File

@ -415,7 +415,7 @@ void Process_makeCommandStr(Process* this) {
* - a user thread and showThreadNames is not set */
if (Process_isKernelThread(this))
return;
if (this->state == 'Z' && !this->mergedCommand.str)
if (this->state == ZOMBIE && !this->mergedCommand.str)
return;
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames))
return;
@ -749,6 +749,28 @@ void Process_printPercentage(float val, char* buffer, int n, int* attr) {
}
}
static inline char processStateChar(ProcessState state) {
switch (state) {
case UNKNOWN: return '?';
case RUNNABLE: return 'U';
case RUNNING: return 'R';
case QUEUED: return 'Q';
case WAITING: return 'W';
case UNINTERRUPTIBLE_WAIT: return 'D';
case BLOCKED: return 'B';
case PAGING: return 'P';
case STOPPED: return 'T';
case TRACED: return 't';
case ZOMBIE: return 'Z';
case DEFUNCT: return 'X';
case IDLE: return 'I';
case SLEEPING: return 'S';
default:
assert(0);
return '!';
}
}
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256];
size_t n = sizeof(buffer);
@ -883,22 +905,32 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE:
xSnprintf(buffer, n, "%c ", this->state);
xSnprintf(buffer, n, "%c ", processStateChar(this->state));
switch (this->state) {
#ifdef HTOP_NETBSD
case 'P':
#else
case 'R':
#endif
case RUNNABLE:
case RUNNING:
case TRACED:
attr = CRT_colors[PROCESS_RUN_STATE];
break;
case 'D':
case BLOCKED:
case DEFUNCT:
case STOPPED:
case ZOMBIE:
attr = CRT_colors[PROCESS_D_STATE];
break;
case 'I':
case 'S':
case QUEUED:
case WAITING:
case UNINTERRUPTIBLE_WAIT:
case IDLE:
case SLEEPING:
attr = CRT_colors[PROCESS_SHADOW];
break;
case UNKNOWN:
case PAGING:
break;
}
break;
case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
@ -1072,44 +1104,6 @@ int Process_compare(const void* v1, const void* v2) {
return (Settings_getActiveDirection(settings) == 1) ? result : -result;
}
static uint8_t stateCompareValue(char state) {
switch (state) {
case 'S':
return 10;
case 'I':
return 9;
case 'X':
return 8;
case 'Z':
return 7;
case 't':
return 6;
case 'T':
return 5;
case 'L':
return 4;
case 'D':
return 3;
case 'R':
return 2;
case '?':
return 1;
default:
return 0;
}
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
@ -1164,7 +1158,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
case STATE:
return SPACESHIP_NUMBER(stateCompareValue(p1->state), stateCompareValue(p2->state));
return SPACESHIP_NUMBER(p1->state, p2->state);
case ST_UID:
return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME:

View File

@ -60,6 +60,26 @@ typedef enum ProcessField_ {
LAST_PROCESSFIELD
} ProcessField;
/* Core process states (shared by platforms)
* NOTE: The enum has an ordering that is important!
* See processStateChar in process.c for ProcessSate -> letter mapping */
typedef enum ProcessState_ {
UNKNOWN = 1,
RUNNABLE,
RUNNING,
QUEUED,
WAITING,
UNINTERRUPTIBLE_WAIT,
BLOCKED,
PAGING,
STOPPED,
TRACED,
ZOMBIE,
DEFUNCT,
IDLE,
SLEEPING
} ProcessState;
struct Settings_;
/* Holds information about regions of the cmdline that should be
@ -202,20 +222,8 @@ typedef struct Process_ {
/* Number of major faults the process has made which have required loading a memory page from disk */
unsigned long int majflt;
/*
* Process state (platform dependent):
* D - Waiting
* I - Idle
* L - Acquiring lock
* R - Running
* S - Sleeping
* T - Stopped (on a signal)
* X - Dead
* Z - Zombie
* t - Tracing stop
* ? - Unknown
*/
char state;
/* Process state enum field (platform dependent) */
ProcessState state;
/* Whether the process was updated during the current scan */
bool updated;

View File

@ -331,7 +331,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->nice = ep->p_nice;
proc->priority = ep->p_priority;
proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
proc->state = (ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN;
/* Make sure the updated flag is set */
proc->updated = true;
@ -386,7 +386,7 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
return;
}
if (proc->state == 'Z') {
if (proc->state == ZOMBIE) {
return;
}
@ -430,15 +430,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
mach_port_deallocate(mach_task_self(), port);
char state = '?';
/* Taken from: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/thread_info.h#L129 */
switch (run_state) {
case TH_STATE_RUNNING: state = 'R'; break;
case TH_STATE_STOPPED: state = 'S'; break;
case TH_STATE_WAITING: state = 'W'; break;
case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
case TH_STATE_HALTED: state = 'H'; break;
case TH_STATE_RUNNING: proc->state = RUNNING; break;
case TH_STATE_STOPPED: proc->state = STOPPED; break;
case TH_STATE_WAITING: proc->state = WAITING; break;
case TH_STATE_UNINTERRUPTIBLE: proc->state = UNINTERRUPTIBLE_WAIT; break;
case TH_STATE_HALTED: proc->state = BLOCKED; break;
default: proc->state = UNKNOWN;
}
proc->state = state;
}

View File

@ -542,60 +542,61 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
}
// would be nice if we could store multiple states in proc->state (as enum) and have writeField render them
/* Taken from: https://github.com/DragonFlyBSD/DragonFlyBSD/blob/c163a4d7ee9c6857ee4e04a3a2cbb50c3de29da1/sys/sys/proc_common.h */
switch (kproc->kp_stat) {
case SIDL: proc->state = 'I'; isIdleProcess = true; break;
case SIDL: proc->state = IDLE; isIdleProcess = true; break;
case SACTIVE:
switch (kproc->kp_lwp.kl_stat) {
case LSSLEEP:
if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptible wait short/long
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
proc->state = 'I';
proc->state = IDLE;
isIdleProcess = true;
} else {
proc->state = 'S';
proc->state = SLEEPING;
}
else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptible lwkt wait
proc->state = 'S';
proc->state = SLEEPING;
else if (kproc->kp_paddr) // uninterruptible wait
proc->state = 'D';
proc->state = UNINTERRUPTIBLE_WAIT;
else // uninterruptible lwkt wait
proc->state = 'B';
proc->state = UNINTERRUPTIBLE_WAIT;
break;
case LSRUN:
if (kproc->kp_lwp.kl_stat == LSRUN) {
if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ)))
proc->state = 'Q';
proc->state = QUEUED;
else
proc->state = 'R';
proc->state = RUNNING;
}
break;
case LSSTOP:
proc->state = 'T';
proc->state = STOPPED;
break;
default:
proc->state = 'A';
proc->state = PAGING;
break;
}
break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SCORE: proc->state = 'C'; break;
default: proc->state = '?';
case SSTOP: proc->state = STOPPED; break;
case SZOMB: proc->state = ZOMBIE; break;
case SCORE: proc->state = BLOCKED; break;
default: proc->state = UNKNOWN;
}
if (kproc->kp_flags & P_SWAPPEDOUT)
proc->state = 'W';
proc->state = SLEEPING;
if (kproc->kp_flags & P_TRACED)
proc->state = 'T';
proc->state = TRACED;
if (kproc->kp_flags & P_JAILED)
proc->state = 'J';
proc->state = TRACED;
if (Process_isKernelThread(proc))
super->kernelThreads++;
super->totalTasks++;
if (proc->state == 'R')
if (proc->state == RUNNING)
super->runningTasks++;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));

View File

@ -578,15 +578,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->nice = PRIO_MAX + 1 + kproc->ki_pri.pri_level - PRI_MIN_IDLE;
}
/* Taken from: https://github.com/freebsd/freebsd-src/blob/1ad2d87778970582854082bcedd2df0394fd4933/sys/sys/proc.h#L851 */
switch (kproc->ki_stat) {
case SIDL: proc->state = 'I'; break;
case SRUN: proc->state = 'R'; break;
case SSLEEP: proc->state = 'S'; break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SWAIT: proc->state = 'D'; break;
case SLOCK: proc->state = 'L'; break;
default: proc->state = '?';
case SIDL: proc->state = IDLE; break;
case SRUN: proc->state = RUNNING; break;
case SSLEEP: proc->state = SLEEPING; break;
case SSTOP: proc->state = STOPPED; break;
case SZOMB: proc->state = ZOMBIE; break;
case SWAIT: proc->state = WAITING; break;
case SLOCK: proc->state = BLOCKED; break;
default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc))
@ -595,7 +596,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
super->totalTasks++;
if (proc->state == 'R')
if (proc->state == RUNNING)
super->runningTasks++;
proc->updated = true;
}

View File

@ -310,6 +310,22 @@ static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long
return t * 100 / jiffy;
}
/* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */
static inline ProcessState LinuxProcessList_getProcessState(char state) {
switch (state) {
case 'S': return SLEEPING;
case 'X': return DEFUNCT;
case 'Z': return ZOMBIE;
case 't': return TRACED;
case 'T': return STOPPED;
case 'D': return UNINTERRUPTIBLE_WAIT;
case 'R': return RUNNING;
case 'P': return BLOCKED;
case 'I': return IDLE;
default: return UNKNOWN;
}
}
static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
@ -335,7 +351,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location = end + 2;
/* (3) state - %c */
process->state = location[0];
process->state = LinuxProcessList_getProcessState(location[0]);
location += 2;
/* (4) ppid - %d */
@ -1098,7 +1114,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
return false;
if (amtRead == 0) {
if (process->state != 'Z') {
if (process->state != ZOMBIE) {
process->isKernelThread = true;
}
Process_updateCmdline(process, NULL, 0, 0);
@ -1511,7 +1527,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
ProcessList_add(pl, proc);
} else {
if (settings->updateProcessNames && proc->state != 'Z') {
if (settings->updateProcessNames && proc->state != ZOMBIE) {
if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
}
@ -1549,7 +1565,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
if (!proc->cmdline && statCommand[0] &&
(proc->state == 'Z' || Process_isKernelThread(proc) || settings->showThreadNames)) {
(proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}

View File

@ -331,31 +331,33 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
int nlwps = 0;
const struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
/* TODO: According to the link below, SDYING should be a regarded state */
/* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */
switch (kproc->p_realstat) {
case SIDL: proc->state = 'I'; break;
case SIDL: proc->state = IDLE; break;
case SACTIVE:
// We only consider the first LWP with a one of the below states.
for (int j = 0; j < nlwps; j++) {
if (klwps) {
switch (klwps[j].l_stat) {
case LSONPROC: proc->state = 'P'; break;
case LSRUN: proc->state = 'R'; break;
case LSSLEEP: proc->state = 'S'; break;
case LSSTOP: proc->state = 'T'; break;
default: proc->state = '?';
case LSONPROC: proc->state = RUNNING; break;
case LSRUN: proc->state = RUNNABLE; break;
case LSSLEEP: proc->state = SLEEPING; break;
case LSSTOP: proc->state = STOPPED; break;
default: proc->state = UNKNOWN;
}
if (proc->state != '?')
if (proc->state != UNKNOWN)
break;
} else {
proc->state = '?';
proc->state = UNKNOWN;
break;
}
}
break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SDEAD: proc->state = 'D'; break;
default: proc->state = '?';
case SSTOP: proc->state = STOPPED; break;
case SZOMB: proc->state = ZOMBIE; break;
case SDEAD: proc->state = DEFUNCT; break;
default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc)) {
@ -365,8 +367,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
}
this->super.totalTasks++;
// SRUN ('R') means runnable, not running
if (proc->state == 'P') {
if (proc->state == RUNNING) {
this->super.runningTasks++;
}
proc->updated = true;

View File

@ -345,15 +345,16 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid);
}
/* Taken from: https://github.com/openbsd/src/blob/6a38af0976a6870911f4b2de19d8da28723a5778/sys/sys/proc.h#L420 */
switch (kproc->p_stat) {
case SIDL: proc->state = 'I'; break;
case SRUN: proc->state = 'P'; break;
case SSLEEP: proc->state = 'S'; break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SDEAD: proc->state = 'D'; break;
case SONPROC: proc->state = 'R'; break;
default: proc->state = '?';
case SIDL: proc->state = IDLE; break;
case SRUN: proc->state = RUNNABLE; break;
case SSLEEP: proc->state = SLEEPING; break;
case SSTOP: proc->state = STOPPED; break;
case SZOMB: proc->state = ZOMBIE; break;
case SDEAD: proc->state = DEFUNCT; break;
case SONPROC: proc->state = RUNNING; break;
default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc)) {
@ -363,7 +364,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
}
this->super.totalTasks++;
if (proc->state == 'R') {
if (proc->state == RUNNING) {
this->super.runningTasks++;
}

View File

@ -142,10 +142,27 @@ static inline char Metric_instance_char(int metric, int pid, int offset, char fa
return fallback;
}
static inline PCPProcessList_getProcessState(char state) {
switch (state) {
case '?': return UNKNOWN;
case 'R': return RUNNING;
case 'W': return WAITING;
case 'D': return UNINTERRUPTIBLE_WAIT;
case 'P': return PAGING;
case 'T': return STOPPED;
case 't': return TRACED;
case 'Z': return ZOMBIE;
case 'X': return DEFUNCT;
case 'I': return IDLE;
case 'S': return SLEEPING;
default: return UNKNOWN;
}
}
static void PCPProcessList_updateID(Process* process, int pid, int offset) {
process->tgid = Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1);
process->ppid = Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1);
process->state = Metric_instance_char(PCP_PROC_STATE, pid, offset, '?');
process->state = PCPProcessList_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
}
static void PCPProcessList_updateInfo(Process* process, int pid, int offset, char* command, size_t commLen) {
@ -283,7 +300,7 @@ static void PCPProcessList_updateUsername(Process* process, int pid, int offset,
static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, const char* comm) {
pmAtomValue value;
if (!PCPMetric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
if (process->state != 'Z')
if (process->state != ZOMBIE)
process->isKernelThread = true;
Process_updateCmdline(process, NULL, 0, 0);
return;
@ -351,7 +368,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
proc->updated = true;
proc->show = false;
if (proc->state == 'R')
if (proc->state == RUNNING)
pl->runningTasks++;
pl->kernelThreads++;
pl->totalTasks++;
@ -360,7 +377,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
proc->updated = true;
proc->show = false;
if (proc->state == 'R')
if (proc->state == RUNNING)
pl->runningTasks++;
pl->userlandThreads++;
pl->totalTasks++;
@ -398,7 +415,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
PCPProcessList_updateCmdline(proc, pid, offset, command);
Process_fillStarttimeBuffer(proc);
ProcessList_add(pl, proc);
} else if (settings->updateProcessNames && proc->state != 'Z') {
} else if (settings->updateProcessNames && proc->state != ZOMBIE) {
PCPProcessList_updateCmdline(proc, pid, offset, command);
}
@ -420,7 +437,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP)
PCPProcessList_readAutogroup(pp, pid, offset);
if (proc->state == 'Z' && !proc->cmdline && command[0]) {
if (proc->state == ZOMBIE && !proc->cmdline && command[0]) {
Process_updateCmdline(proc, command, 0, strlen(command));
} else if (Process_isThread(proc)) {
if ((settings->showThreadNames || Process_isKernelThread(proc)) && command[0]) {
@ -439,7 +456,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
(hideUserlandThreads && Process_isUserlandThread(proc)));
pl->totalTasks++;
if (proc->state == 'R')
if (proc->state == RUNNING)
pl->runningTasks++;
proc->updated = true;
}

View File

@ -363,6 +363,19 @@ static void SolarisProcessList_updateCwd(pid_t pid, Process* proc) {
free_and_xStrdup(&proc->procCwd, target);
}
/* Taken from: https://docs.oracle.com/cd/E19253-01/817-6223/6mlkidlom/index.html#tbl-sched-state */
static inline ProcessState SolarisProcessList_getProcessState(char state) {
switch (state) {
case 'S': return SLEEPING;
case 'R': return RUNNABLE;
case 'O': return RUNNING;
case 'Z': return ZOMBIE;
case 'T': return STOPPED;
case 'I': return IDLE;
default: return UNKNOWN;
}
}
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
@ -402,7 +415,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
proc->priority = _lwpsinfo->pr_pri;
proc->nice = _lwpsinfo->pr_nice - NZERO;
proc->processor = _lwpsinfo->pr_onpro;
proc->state = _lwpsinfo->pr_sname;
proc->state = SolarisProcessList_getProcessState(_lwpsinfo->pr_sname);
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
@ -462,11 +475,11 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
if (proc->isKernelThread && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
pl->totalTasks += proc->nlwp + 1;
if (proc->state == 'O') {
if (proc->state == RUNNING) {
pl->runningTasks++;
}
} else if (!proc->isKernelThread) {
if (proc->state == 'O') {
if (proc->state == RUNNING) {
pl->runningTasks++;
}
if (pl->settings->hideUserlandThreads) {

View File

@ -57,7 +57,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->updated = true;
proc->state = 'R';
proc->state = RUNNING;
proc->isKernelThread = false;
proc->isUserlandThread = false;
proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */