diff options
Diffstat (limited to 'Process.c')
-rw-r--r-- | Process.c | 897 |
1 files changed, 386 insertions, 511 deletions
@@ -2,7 +2,7 @@ htop - Process.c (C) 2004-2015 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. -Released under the GNU GPLv2, see the COPYING file +Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ @@ -11,7 +11,6 @@ in the source distribution for its full text. #include "Process.h" #include <assert.h> -#include <limits.h> #include <math.h> #include <signal.h> #include <stdbool.h> @@ -19,15 +18,18 @@ in the source distribution for its full text. #include <stdlib.h> #include <string.h> #include <time.h> -#include <unistd.h> #include <sys/resource.h> #include "CRT.h" +#include "Hashtable.h" +#include "Machine.h" #include "Macros.h" -#include "Platform.h" -#include "ProcessList.h" +#include "ProcessTable.h" +#include "DynamicColumn.h" #include "RichString.h" +#include "Scheduling.h" #include "Settings.h" +#include "Table.h" #include "XUtils.h" #if defined(MAJOR_IN_MKDEV) @@ -38,221 +40,15 @@ in the source distribution for its full text. /* Used to identify kernel threads in Comm and Exe columns */ static const char* const kthreadID = "KTHREAD"; -static uid_t Process_getuid = (uid_t)-1; - -int Process_pidDigits = 7; - -void Process_setupColumnWidths() { - int maxPid = Platform_getMaxPid(); - if (maxPid == -1) - return; - - Process_pidDigits = ceil(log10(maxPid)); - assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS); -} - -void Process_printBytes(RichString* str, unsigned long long number, bool coloring) { - char buffer[16]; - int len; - - int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int processGigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; - int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; - int processColor = CRT_colors[PROCESS]; - - if (number == ULLONG_MAX) { - //Invalid number - RichString_appendAscii(str, shadowColor, " N/A "); - return; - } - - number /= ONE_K; - - if (number < 1000) { - //Plain number, no markings - len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (number < 100000) { - //2 digit MB, 3 digit KB - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 1000); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (number < 1000 * ONE_K) { - //3 digit MB - number /= ONE_K; - len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (number < 10000 * ONE_K) { - //1 digit GB, 3 digit MB - number /= ONE_K; - len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (number < 100000 * ONE_K) { - //2 digit GB, 1 digit MB - number /= 100 * ONE_K; - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - number %= 10; - len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - RichString_appendAscii(str, processGigabytesColor, "G "); - } else if (number < 1000 * ONE_M) { - //3 digit GB - number /= ONE_M; - len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - } else if (number < 10000ULL * ONE_M) { - //1 digit TB, 3 digit GB - number /= ONE_M; - len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - } else if (number < 100000 * ONE_M) { - //2 digit TB, 1 digit GB - number /= 100 * ONE_M; - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - number %= 10; - len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - RichString_appendAscii(str, largeNumberColor, "T "); - } else if (number < 10000ULL * ONE_G) { - //3 digit TB or 1 digit PB, 3 digit TB - number /= ONE_G; - len = xSnprintf(buffer, sizeof(buffer), "%4lluT ", number); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else { - //2 digit PB and above - len = xSnprintf(buffer, sizeof(buffer), "%4.1lfP ", (double)number / ONE_T); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } -} - -void Process_printKBytes(RichString* str, unsigned long long number, bool coloring) { - if (number == ULLONG_MAX) - Process_printBytes(str, ULLONG_MAX, coloring); - else - Process_printBytes(str, number * ONE_K, coloring); -} - -void Process_printCount(RichString* str, unsigned long long number, bool coloring) { - char buffer[13]; - - int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int processColor = CRT_colors[PROCESS]; - int processShadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; - - if (number == ULLONG_MAX) { - RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A "); - } else if (number >= 100000LL * ONE_DECIMAL_T) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); - RichString_appendnAscii(str, largeNumberColor, buffer, 12); - } else if (number >= 100LL * ONE_DECIMAL_T) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); - RichString_appendnAscii(str, largeNumberColor, buffer, 8); - RichString_appendnAscii(str, processMegabytesColor, buffer + 8, 4); - } else if (number >= 10LL * ONE_DECIMAL_G) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); - RichString_appendnAscii(str, largeNumberColor, buffer, 5); - RichString_appendnAscii(str, processMegabytesColor, buffer + 5, 3); - RichString_appendnAscii(str, processColor, buffer + 8, 4); - } else { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number); - RichString_appendnAscii(str, largeNumberColor, buffer, 2); - RichString_appendnAscii(str, processMegabytesColor, buffer + 2, 3); - RichString_appendnAscii(str, processColor, buffer + 5, 3); - RichString_appendnAscii(str, processShadowColor, buffer + 8, 4); - } -} - -void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring) { - char buffer[10]; - int len; - - unsigned long long totalSeconds = totalHundredths / 100; - unsigned long long hours = totalSeconds / 3600; - unsigned long long days = totalSeconds / 86400; - int minutes = (totalSeconds / 60) % 60; - int seconds = totalSeconds % 60; - int hundredths = totalHundredths - (totalSeconds * 100); - - int yearColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int dayColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; - int hourColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int defColor = CRT_colors[PROCESS]; - - if (days >= /* Ignore leapyears */365) { - int years = days / 365; - int daysLeft = days - 365 * years; - - if (daysLeft >= 100) { - len = xSnprintf(buffer, sizeof(buffer), "%3dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } else if (daysLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } - } else if (days >= 100) { - int hoursLeft = hours - days * 24; - - if (hoursLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4llud", days); - RichString_appendnAscii(str, dayColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dh ", hoursLeft); - RichString_appendnAscii(str, hourColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5llud", days); - RichString_appendnAscii(str, dayColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dh ", hoursLeft); - RichString_appendnAscii(str, hourColor, buffer, len); - } - } else if (hours >= 100) { - int minutesLeft = totalSeconds / 60 - hours * 60; - - if (minutesLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dm ", minutesLeft); - RichString_appendnAscii(str, defColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dm ", minutesLeft); - RichString_appendnAscii(str, defColor, buffer, len); - } - } else if (hours > 0) { - len = xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); - RichString_appendnAscii(str, defColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths); - RichString_appendnAscii(str, defColor, buffer, len); - } -} - void Process_fillStarttimeBuffer(Process* this) { struct tm date; + time_t now = this->super.host->realtime.tv_sec; (void) localtime_r(&this->starttime_ctime, &date); - strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date); + + strftime(this->starttime_show, + sizeof(this->starttime_show) - 1, + (this->starttime_ctime > now - 86400) ? "%R " : (this->starttime_ctime > now - 364 * 86400) ? "%b%d " : " %Y ", + &date); } /* @@ -264,7 +60,7 @@ void Process_fillStarttimeBuffer(Process* this) { * * Note: when colorizing a basename with the comm prefix, the entire basename * (not just the comm prefix) is colorized for better readability, and it is - * implicit that only upto (TASK_COMM_LEN - 1) could be comm. + * implicit that only up to (TASK_COMM_LEN - 1) could be comm. */ #define TASK_COMM_LEN 16 @@ -380,17 +176,19 @@ static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr * This function makes the merged Command string. It also stores the offsets of the * basename, comm w.r.t the merged Command string - these offsets will be used by * Process_writeCommand() for coloring. The merged Command string is also - * returned by Process_getCommandStr() for searching, sorting and filtering. + * returned by Process_getCommand() for searching, sorting and filtering. */ -void Process_makeCommandStr(Process* this) { +void Process_makeCommandStr(Process* this, const Settings* settings) { ProcessMergedCommand* mc = &this->mergedCommand; - const Settings* settings = this->settings; bool showMergedCommand = settings->showMergedCommand; bool showProgramPath = settings->showProgramPath; bool searchCommInCmdline = settings->findCommInCmdline; bool stripExeFromCmdline = settings->stripExeFromCmdline; bool showThreadNames = settings->showThreadNames; + bool shadowDistPathPrefix = settings->shadowDistPathPrefix; + + uint64_t settingsStamp = settings->lastUpdate; /* Nothing to do to (Re)Generate the Command string, if the process is: * - a kernel thread, or @@ -398,54 +196,29 @@ 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) - return; - if (Process_isUserlandThread(this) && settings->showThreadNames) + if (this->state == ZOMBIE && !this->mergedCommand.str) return; /* this->mergedCommand.str needs updating only if its state or contents changed. * Its content is based on the fields cmdline, comm, and exe. */ - if ( - mc->prevMergeSet == showMergedCommand && - mc->prevPathSet == showProgramPath && - mc->prevCommSet == searchCommInCmdline && - mc->prevCmdlineSet == stripExeFromCmdline && - mc->prevShowThreadNames == showThreadNames && - !mc->cmdlineChanged && - !mc->commChanged && - !mc->exeChanged - ) { + if (mc->lastUpdate >= settingsStamp) return; - } - /* The field separtor "│" has been chosen such that it will not match any + mc->lastUpdate = settingsStamp; + + /* The field separator "│" has been chosen such that it will not match any * valid string used for searching or filtering */ const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT]; const int SEPARATOR_LEN = strlen(SEPARATOR); - /* Check for any changed fields since we last built this string */ - if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) { - free(mc->str); - /* Accommodate the column text, two field separators and terminating NUL */ - size_t maxLen = 2 * SEPARATOR_LEN + 1; - maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)"); - maxLen += this->procComm ? strlen(this->procComm) : 0; - maxLen += this->procExe ? strlen(this->procExe) : 0; - - mc->str = xCalloc(1, maxLen); - } - - /* Preserve the settings used in this run */ - mc->prevMergeSet = showMergedCommand; - mc->prevPathSet = showProgramPath; - mc->prevCommSet = searchCommInCmdline; - mc->prevCmdlineSet = stripExeFromCmdline; - mc->prevShowThreadNames = showThreadNames; + /* Accommodate the column text, two field separators and terminating NUL */ + size_t maxLen = 2 * SEPARATOR_LEN + 1; + maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)"); + maxLen += this->procComm ? strlen(this->procComm) : 0; + maxLen += this->procExe ? strlen(this->procExe) : 0; - /* Mark everything as unchanged */ - mc->cmdlineChanged = false; - mc->commChanged = false; - mc->exeChanged = false; + free(mc->str); + mc->str = xCalloc(1, maxLen); /* Reset all locations that need extra handling when actually displaying */ mc->highlightCount = 0; @@ -457,7 +230,7 @@ void Process_makeCommandStr(Process* this) { /* Check if we still have capacity */ \ assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \ if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \ - continue; \ + break; \ \ mc->highlights[mc->highlightCount].offset = str - strStart + (_offset) - mbMismatch; \ mc->highlights[mc->highlightCount].length = _length; \ @@ -473,6 +246,56 @@ void Process_makeCommandStr(Process* this) { str = stpcpy(str, SEPARATOR); \ } while (0) + #define CHECK_AND_MARK(str_, prefix_) \ + if (String_startsWith(str_, prefix_)) { \ + WRITE_HIGHLIGHT(0, strlen(prefix_), CRT_colors[PROCESS_SHADOW], CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR); \ + break; \ + } else (void)0 + + #define CHECK_AND_MARK_DIST_PATH_PREFIXES(str_) \ + do { \ + if ((str_)[0] != '/') { \ + break; \ + } \ + switch ((str_)[1]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/lib/"); \ + CHECK_AND_MARK(str_, "/lib32/"); \ + CHECK_AND_MARK(str_, "/lib64/"); \ + CHECK_AND_MARK(str_, "/libx32/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/sbin/"); \ + break; \ + case 'u': \ + if (String_startsWith(str_, "/usr/")) { \ + switch ((str_)[5]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/usr/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/usr/libexec/"); \ + CHECK_AND_MARK(str_, "/usr/lib/"); \ + CHECK_AND_MARK(str_, "/usr/lib32/"); \ + CHECK_AND_MARK(str_, "/usr/lib64/"); \ + CHECK_AND_MARK(str_, "/usr/libx32/"); \ + \ + CHECK_AND_MARK(str_, "/usr/local/bin/"); \ + CHECK_AND_MARK(str_, "/usr/local/lib/"); \ + CHECK_AND_MARK(str_, "/usr/local/sbin/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/usr/sbin/"); \ + break; \ + } \ + } \ + break; \ + } \ + } while (0) + const int baseAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_BASENAME] : CRT_colors[PROCESS_BASENAME]; const int commAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_COMM] : CRT_colors[PROCESS_COMM]; const int delExeAttr = CRT_colors[FAILED_READ]; @@ -499,17 +322,29 @@ void Process_makeCommandStr(Process* this) { assert(cmdlineBasenameStart <= (int)strlen(cmdline)); if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ - if (showMergedCommand && showThreadNames && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */ + if ((showMergedCommand || (Process_isUserlandThread(this) && showThreadNames)) && procComm && strlen(procComm)) { /* set column to or prefix it with comm */ if (strncmp(cmdline + cmdlineBasenameStart, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) { WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); str = stpcpy(str, procComm); + if (!showMergedCommand) + return; + WRITE_SEPARATOR; } } + if (shadowDistPathPrefix && showProgramPath) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + if (cmdlineBasenameEnd > cmdlineBasenameStart) WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); + + if (this->procExeDeleted) + WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); + else if (this->usesDeletedLib) + WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); + (void)stpcpyWithNewlineConversion(str, cmdline + (showProgramPath ? 0 : cmdlineBasenameStart)); return; @@ -523,12 +358,14 @@ void Process_makeCommandStr(Process* this) { assert(exeBasenameOffset <= (int)strlen(procExe)); bool haveCommInExe = false; - if (procExe && procComm && showThreadNames) { + if (procExe && procComm && (!Process_isUserlandThread(this) || showThreadNames)) { haveCommInExe = strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0; } /* Start with copying exe */ if (showProgramPath) { + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(procExe); if (haveCommInExe) WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); @@ -555,14 +392,14 @@ void Process_makeCommandStr(Process* this) { /* Try to match procComm with procExe's basename: This is reliable (predictable) */ if (searchCommInCmdline) { /* commStart/commEnd will be adjusted later along with cmdline */ - haveCommInCmdline = showThreadNames && findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd); + haveCommInCmdline = (!Process_isUserlandThread(this) || showThreadNames) && findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd); } int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen); bool haveCommField = false; - if (!haveCommInExe && !haveCommInCmdline && procComm && showThreadNames) { + if (!haveCommInExe && !haveCommInCmdline && procComm && (!Process_isUserlandThread(this) || showThreadNames)) { WRITE_SEPARATOR; WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); str = stpcpy(str, procComm); @@ -570,11 +407,15 @@ void Process_makeCommandStr(Process* this) { } if (matchLen) { - /* strip the matched exe prefix */ - cmdline += matchLen; + if (stripExeFromCmdline) { + /* strip the matched exe prefix */ + cmdline += matchLen; - commStart -= matchLen; - commEnd -= matchLen; + commStart -= matchLen; + commEnd -= matchLen; + } else { + matchLen = 0; + } } if (!matchLen || (haveCommField && *cmdline)) { @@ -582,13 +423,18 @@ void Process_makeCommandStr(Process* this) { WRITE_SEPARATOR; } - if (!haveCommInExe && haveCommInCmdline && !haveCommField && showThreadNames) + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + + if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames)) WRITE_HIGHLIGHT(commStart, commEnd - commStart, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); /* Display cmdline if it hasn't been consumed by procExe */ if (*cmdline) (void)stpcpyWithNewlineConversion(str, cmdline); + #undef CHECK_AND_MARK_DIST_PATH_PREFIXES + #undef CHECK_AND_MARK #undef WRITE_SEPARATOR #undef WRITE_HIGHLIGHT } @@ -597,18 +443,20 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin (void)baseAttr; const ProcessMergedCommand* mc = &this->mergedCommand; + const char* mergedCommand = mc->str; int strStart = RichString_size(str); - const bool highlightBaseName = this->settings->highlightBaseName; + const Settings* settings = this->super.host->settings; + const bool highlightBaseName = settings->highlightBaseName; const bool highlightSeparator = true; - const bool highlightDeleted = this->settings->highlightDeletedExe; + const bool highlightDeleted = settings->highlightDeletedExe; - if (!this->mergedCommand.str) { + if (!mergedCommand) { int len = 0; const char* cmdline = this->cmdline; - if (highlightBaseName || !this->settings->showProgramPath) { + if (highlightBaseName || !settings->showProgramPath) { int basename = 0; for (int i = 0; i < this->cmdlineBasenameEnd; i++) { if (cmdline[i] == '/') { @@ -619,7 +467,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin } } if (len == 0) { - if (this->settings->showProgramPath) { + if (settings->showProgramPath) { strStart += basename; } else { cmdline += basename; @@ -630,14 +478,14 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin RichString_appendWide(str, attr, cmdline); - if (this->settings->highlightBaseName) { + if (settings->highlightBaseName) { RichString_setAttrn(str, baseAttr, strStart, len); } return; } - RichString_appendWide(str, attr, this->mergedCommand.str); + RichString_appendWide(str, attr, mergedCommand); for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) { const ProcessCmdlineHighlight* hl = &mc->highlights[i]; @@ -657,87 +505,71 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin if (!highlightDeleted) continue; + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR) + if (!highlightDeleted) + continue; + RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length); } } -void Process_printRate(RichString* str, double rate, bool coloring) { - char buffer[16]; - - int largeNumberColor = CRT_colors[LARGE_NUMBER]; - int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; - int processColor = CRT_colors[PROCESS]; - int shadowColor = CRT_colors[PROCESS_SHADOW]; - - if (!coloring) { - largeNumberColor = CRT_colors[PROCESS]; - processMegabytesColor = CRT_colors[PROCESS]; - } - - if (isnan(rate)) { - RichString_appendAscii(str, shadowColor, " N/A "); - } else if (rate < 0.005) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); - RichString_appendnAscii(str, shadowColor, buffer, len); - } else if (rate < ONE_K) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (rate < ONE_M) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (rate < ONE_G) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (rate < ONE_T) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else if (rate < ONE_P) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else { - int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P); - RichString_appendnAscii(str, largeNumberColor, buffer, len); +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_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) { - int columns = width; - RichString_appendnWideColumns(str, attr, content, strlen(content), &columns); - RichString_appendChr(str, attr, ' ', width + 1 - columns); +static void Process_rowWriteField(const Row* super, RichString* str, RowField field) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + Process_writeField(this, str, field); } -void Process_writeField(const Process* this, RichString* str, ProcessField field) { - char buffer[256]; - size_t n = sizeof(buffer); +void Process_writeField(const Process* this, RichString* str, RowField field) { + const Row* super = (const Row*) &this->super; + const Machine* host = super->host; + const Settings* settings = host->settings; + + bool coloring = settings->highlightMegabytes; + char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; - bool coloring = this->settings->highlightMegabytes; + size_t n = sizeof(buffer) - 1; switch (field) { case COMM: { int baseattr = CRT_colors[PROCESS_BASENAME]; - if (this->settings->highlightThreads && Process_isThread(this)) { + if (settings->highlightThreads && Process_isThread(this)) { attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } - if (!this->settings->treeView || this->indent == 0) { + const ScreenSettings* ss = settings->ss; + if (!ss->treeView || super->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; } char* buf = buffer; - int maxIndent = 0; - bool lastItem = (this->indent < 0); - int indent = (this->indent < 0 ? -this->indent : this->indent); + const bool lastItem = (super->indent < 0); - for (int i = 0; i < 32; i++) { - if (indent & (1U << i)) { - maxIndent = i + 1; - } - } - - for (int i = 0; i < maxIndent - 1; i++) { + for (uint32_t indent = (super->indent < 0 ? -super->indent : super->indent); indent > 1; indent >>= 1) { int written, ret; - if (indent & (1 << i)) { + if (indent & 1U) { ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); } else { ret = xSnprintf(buf, n, " "); @@ -752,7 +584,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE]; - xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); + xSnprintf(buf, n, "%s%s ", draw, super->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer); Process_writeCommand(this, attr, baseattr, str); return; @@ -767,14 +599,14 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field procComm = Process_isKernelThread(this) ? kthreadID : "N/A"; } - Process_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1); + Row_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1); return; } case PROC_EXE: { const char* procExe; if (this->procExe) { attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME]; - if (this->settings->highlightDeletedExe) { + if (settings->highlightDeletedExe) { if (this->procExeDeleted) attr = CRT_colors[FAILED_READ]; else if (this->usesDeletedLib) @@ -786,7 +618,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field procExe = Process_isKernelThread(this) ? kthreadID : "N/A"; } - Process_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1); + Row_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1); return; } case CWD: { @@ -800,19 +632,27 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } else { cwd = this->procCwd; } - Process_printLeftAlignedField(str, attr, cwd, 25); + Row_printLeftAlignedField(str, attr, cwd, 25); + return; + } + case ELAPSED: { + const uint64_t rt = host->realtimeMs; + const uint64_t st = this->starttime_ctime * 1000; + const uint64_t dt = + rt < st ? 0 : + rt - st; + Row_printTime(str, /* convert to hundreds of a second */ dt / 10, coloring); return; } - case ELAPSED: Process_printTime(str, /* convert to hundreds of a second */ this->processList->realtimeMs / 10 - 100 * this->starttime_ctime, coloring); return; - case MAJFLT: Process_printCount(str, this->majflt, coloring); return; - case MINFLT: Process_printCount(str, this->minflt, coloring); return; - case M_RESIDENT: Process_printKBytes(str, this->m_resident, coloring); return; - case M_VIRT: Process_printKBytes(str, this->m_virt, coloring); return; + case MAJFLT: Row_printCount(str, this->majflt, coloring); return; + case MINFLT: Row_printCount(str, this->minflt, coloring); return; + case M_RESIDENT: Row_printKBytes(str, this->m_resident, coloring); return; + case M_VIRT: Row_printKBytes(str, this->m_virt, coloring); return; case NICE: xSnprintf(buffer, n, "%3ld ", this->nice); attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] - : this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY] - : CRT_colors[PROCESS_SHADOW]; + : this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY] + : CRT_colors[PROCESS_SHADOW]; break; case NLWP: if (this->nlwp == 1) @@ -820,68 +660,70 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field xSnprintf(buffer, n, "%4ld ", this->nlwp); break; - case PERCENT_CPU: + case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break; case PERCENT_NORM_CPU: { - float cpuPercentage = this->percent_cpu; - if (field == PERCENT_NORM_CPU) { - cpuPercentage /= this->processList->cpuCount; - } - if (cpuPercentage > 999.9F) { - xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage); - } else if (cpuPercentage > 99.9F) { - xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage); - } else { - if (cpuPercentage < 0.05F) - attr = CRT_colors[PROCESS_SHADOW]; - - xSnprintf(buffer, n, "%4.1f ", cpuPercentage); - } + float cpuPercentage = this->percent_cpu / host->activeCPUs; + Row_printPercentage(cpuPercentage, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break; } - case PERCENT_MEM: - if (this->percent_mem > 99.9F) { - xSnprintf(buffer, n, "100. "); - } else { - if (this->percent_mem < 0.05F) - attr = CRT_colors[PROCESS_SHADOW]; - - xSnprintf(buffer, n, "%4.1f ", this->percent_mem); - } - break; + case PERCENT_MEM: Row_printPercentage(this->percent_mem, buffer, n, 4, &attr); break; case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break; - case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break; - case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break; + case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getPid(this)); break; + case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getParent(this)); break; case PRIORITY: if (this->priority <= -100) xSnprintf(buffer, n, " RT "); else xSnprintf(buffer, n, "%3ld ", this->priority); break; - case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; + case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(settings, this->processor)); break; + case SCHEDULERPOLICY: { + const char* schedPolStr = "N/A"; +#ifdef SCHEDULER_SUPPORT + if (this->scheduling_policy >= 0) + schedPolStr = Scheduling_formatPolicy(this->scheduling_policy); +#endif + xSnprintf(buffer, n, "%-5s ", schedPolStr); + break; + } 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) { - case 'R': - attr = CRT_colors[PROCESS_R_STATE]; - break; - case 'D': - attr = CRT_colors[PROCESS_D_STATE]; - break; - case 'I': - case 'S': - attr = CRT_colors[PROCESS_SHADOW]; - break; + case RUNNAB |