diff --git a/Process.c b/Process.c index 5334d885..4bcb9dc6 100644 --- a/Process.c +++ b/Process.c @@ -252,35 +252,389 @@ void Process_fillStarttimeBuffer(Process* this) { strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date); } -static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) { - int start = RichString_size(str); - int len = 0; - const char* cmdline = this->cmdline; +/* + * TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is + * not available in an userspace header - so define it. + * + * Note: This is taken from LINUX headers, but implicitly taken for other platforms + * for sake of brevity. + * + * 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. + */ +#define TASK_COMM_LEN 16 - if (this->settings->highlightBaseName || !this->settings->showProgramPath) { - int basename = 0; - for (int i = 0; i < this->cmdlineBasenameEnd; i++) { - if (cmdline[i] == '/') { - basename = i + 1; - } else if (cmdline[i] == ':') { - len = i + 1; - break; +static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameStart, int *pCommStart, int *pCommEnd) { + /* Try to find procComm in tokenized cmdline - this might in rare cases + * mis-identify a string or fail, if comm or cmdline had been unsuitably + * modified by the process */ + const char *tokenBase; + size_t tokenLen; + const size_t commLen = strlen(comm); + + if (cmdlineBasenameStart < 0) + return false; + + for (const char *token = cmdline + cmdlineBasenameStart; *token;) { + for (tokenBase = token; *token && *token != '\n'; ++token) { + if (*token == '/') { + tokenBase = token + 1; } } - if (len == 0) { - if (this->settings->showProgramPath) { - start += basename; - } else { - cmdline += basename; - } - len = this->cmdlineBasenameEnd - basename; + tokenLen = token - tokenBase; + + if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) && + strncmp(tokenBase, comm, commLen) == 0) { + *pCommStart = tokenBase - cmdline; + *pCommEnd = token - cmdline; + return true; + } + + if (*token) { + do { + ++token; + } while (*token && '\n' == *token); } } + return false; +} - RichString_appendWide(str, attr, cmdline); +static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) { + int matchLen; /* matching length to be returned */ + char delim; /* delimiter following basename */ - if (this->settings->highlightBaseName) { - RichString_setAttrn(str, baseattr, start, len); + /* cmdline prefix is an absolute path: it must match whole exe. */ + if (cmdline[0] == '/') { + matchLen = exeBaseLen + exeBaseOffset; + if (strncmp(cmdline, exe, matchLen) == 0) { + delim = cmdline[matchLen]; + if (delim == 0 || delim == '\n' || delim == ' ') { + return matchLen; + } + } + return 0; + } + + /* cmdline prefix is a relative path: We need to first match the basename at + * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe + * suffix. But there is a catch: Some processes modify their cmdline in ways + * that make htop's identification of the basename in cmdline unreliable. + * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to + * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with + * proccmdlineBasenameEnd at "gdm-autologin]". This issue could arise with + * chrome as well as it stores in cmdline its concatenated argument vector, + * without NUL delimiter between the arguments (which may contain a '/') + * + * So if needed, we adjust cmdlineBaseOffset to the previous (if any) + * component of the cmdline relative path, and retry the procedure. */ + bool delimFound; /* if valid basename delimiter found */ + do { + /* match basename */ + matchLen = exeBaseLen + cmdlineBaseOffset; + if (cmdlineBaseOffset < exeBaseOffset && + strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) { + delim = cmdline[matchLen]; + if (delim == 0 || delim == '\n' || delim == ' ') { + int i, j; + /* reverse match the cmdline prefix and exe suffix */ + for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1; + i >= 0 && j >= 0 && cmdline[i] == exe[j]; --i, --j) + ; + + /* full match, with exe suffix being a valid relative path */ + if (i < 0 && j >= 0 && exe[j] == '/') + return matchLen; + } + } + + /* Try to find the previous potential cmdlineBaseOffset - it would be + * preceded by '/' or nothing, and delimited by ' ' or '\n' */ + for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) { + if (delimFound) { + if (cmdline[cmdlineBaseOffset - 1] == '/') { + break; + } + } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') { + delimFound = true; + } + } + } while (delimFound); + + return 0; +} + +/* stpcpy, but also converts newlines to spaces */ +static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) { + for (; *srcStr; ++srcStr) { + *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr; + } + *dstStr = 0; + return dstStr; +} + +/* + * 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. + */ +void Process_makeCommandStr(Process *this) { + 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; + + /* 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->cmdlineChanged && + !mc->commChanged && + !mc->exeChanged + ) { + return; + } + + /* The field separtor "│" 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 */ + mc->str = xCalloc(1, mc->maxLen + 2 * SEPARATOR_LEN + 1); + } + + /* Preserve the settings used in this run */ + mc->prevMergeSet = showMergedCommand; + mc->prevPathSet = showProgramPath; + mc->prevCommSet = searchCommInCmdline; + mc->prevCmdlineSet = stripExeFromCmdline; + + /* Mark everything as unchanged */ + mc->cmdlineChanged = false; + mc->commChanged = false; + mc->exeChanged = false; + + /* Reset all locations that need extra handling when actually displaying */ + mc->highlightCount = 0; + memset(mc->highlights, 0, sizeof(mc->highlights)); + + size_t mbMismatch = 0; + #define WRITE_HIGHLIGHT(_offset, _length, _attr, _flags) \ + do { \ + /* Check if we still have capacity */ \ + assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \ + if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \ + continue; \ + \ + mc->highlights[mc->highlightCount].offset = str - strStart + _offset - mbMismatch; \ + mc->highlights[mc->highlightCount].length = _length; \ + mc->highlights[mc->highlightCount].attr = _attr; \ + mc->highlights[mc->highlightCount].flags = _flags; \ + mc->highlightCount++; \ + } while (0) + + #define WRITE_SEPARATOR \ + do { \ + WRITE_HIGHLIGHT(0, 1, CRT_colors[FAILED_READ], CMDLINE_HIGHLIGHT_FLAG_SEPARATOR); \ + mbMismatch += SEPARATOR_LEN - 1; \ + str = stpcpy(str, SEPARATOR); \ + } 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 delAttr = CRT_colors[FAILED_READ]; + + /* Establish some shortcuts to data we need */ + const char *cmdline = this->cmdline; + const char *procComm = this->procComm; + const char *procExe = this->procExe; + + char *strStart = mc->str; + char *str = strStart; + + int cmdlineBasenameStart = this->cmdlineBasenameStart; + int cmdlineBasenameEnd = this->cmdlineBasenameEnd; + + if (!cmdline) { + cmdlineBasenameStart = 0; + cmdlineBasenameEnd = 0; + cmdline = "(zombie)"; + } + + assert(cmdlineBasenameStart >= 0); + assert(cmdlineBasenameStart <= (int)strlen(cmdline)); + + if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ + if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column 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); + + WRITE_SEPARATOR; + } + } + + if (showProgramPath) { + WRITE_HIGHLIGHT(cmdlineBasenameStart, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); + (void)stpcpyWithNewlineConversion(str, cmdline); + } else { + WRITE_HIGHLIGHT(0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); + (void)stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameStart); + } + + return; + } + + int exeLen = strlen(this->procExe); + int exeBasenameOffset = this->procExeBasenameOffset; + int exeBasenameLen = exeLen - exeBasenameOffset; + + assert(exeBasenameOffset >= 0); + assert(exeBasenameOffset <= (int)strlen(procExe)); + + bool haveCommInExe = false; + if (procExe && procComm) { + haveCommInExe = strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0; + } + + /* Start with copying exe */ + if (showProgramPath) { + if (haveCommInExe) + WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); + WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); + if (this->procExeDeleted) + WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); + str = stpcpy(str, procExe); + } else { + if (haveCommInExe) + WRITE_HIGHLIGHT(0, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); + WRITE_HIGHLIGHT(0, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); + if (this->procExeDeleted) + WRITE_HIGHLIGHT(0, exeBasenameLen, delAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); + str = stpcpy(str, procExe + exeBasenameOffset); + } + + bool haveCommInCmdline = false; + int commStart = 0; + int commEnd = 0; + + /* Try to match procComm with procExe's basename: This is reliable (predictable) */ + if (searchCommInCmdline) { + /* commStart/commEnd will be adjusted later along with cmdline */ + haveCommInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd); + } + + int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen); + + bool haveCommField = false; + + if (!haveCommInExe && !haveCommInCmdline && procComm) { + WRITE_SEPARATOR; + WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); + str = stpcpy(str, procComm); + haveCommField = true; + } + + if (matchLen) { + /* strip the matched exe prefix */ + cmdline += matchLen; + + commStart -= matchLen; + commEnd -= matchLen; + } + + if (!matchLen || (haveCommField && *cmdline)) { + /* cmdline will be a separate field */ + WRITE_SEPARATOR; + } + + if (!haveCommInExe && haveCommInCmdline && !haveCommField) + 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 WRITE_SEPARATOR + #undef WRITE_HIGHLIGHT +} + +void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) { + (void)baseAttr; + + const ProcessMergedCommand *mc = &this->mergedCommand; + + int strStart = RichString_size(str); + + const bool highlightBaseName = this->settings->highlightBaseName; + const bool highlightSeparator = true; + const bool highlightDeleted = true; + + if (!this->mergedCommand.str) { + int len = 0; + const char* cmdline = this->cmdline; + + if (highlightBaseName || !this->settings->showProgramPath) { + int basename = 0; + for (int i = 0; i < this->cmdlineBasenameEnd; i++) { + if (cmdline[i] == '/') { + basename = i + 1; + } else if (cmdline[i] == ':') { + len = i + 1; + break; + } + } + if (len == 0) { + if (this->settings->showProgramPath) { + strStart += basename; + } else { + cmdline += basename; + } + len = this->cmdlineBasenameEnd - basename; + } + } + + RichString_appendWide(str, attr, cmdline); + + if (this->settings->highlightBaseName) { + RichString_setAttrn(str, baseAttr, strStart, len); + } + + return; + } + + RichString_appendWide(str, attr, this->mergedCommand.str); + + for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) { + const ProcessCmdlineHighlight *hl = &mc->highlights[i]; + + if (!hl->length) + continue; + + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_SEPARATOR) + if (!highlightSeparator) + continue; + + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) + if (!highlightBaseName) + continue; + + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_DELETED) + if (!highlightDeleted) + continue; + + RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length); } } diff --git a/Process.h b/Process.h index d07a6d49..487efcc7 100644 --- a/Process.h +++ b/Process.h @@ -372,4 +372,10 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField // Avoid direct calls, use Process_getCommand instead const char *Process_getCommandStr(const Process *this); +/* This function constructs the string that is displayed by + * Process_writeCommand and also returned by Process_getCommandStr */ +void Process_makeCommandStr(Process *this); + +void Process_writeCommand(const Process *this, int attr, int baseAttr, RichString *str); + #endif diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 155bea86..7d32a6d3 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -172,358 +172,6 @@ static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) { } #endif -/* -TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is -not available in an userspace header - so define it. 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 -*/ -#define TASK_COMM_LEN 16 - -static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameStart, int *pCommStart, int *pCommEnd) { - /* Try to find procComm in tokenized cmdline - this might in rare cases - * mis-identify a string or fail, if comm or cmdline had been unsuitably - * modified by the process */ - const char *tokenBase; - size_t tokenLen; - const size_t commLen = strlen(comm); - - if (cmdlineBasenameStart < 0) - return false; - - for (const char *token = cmdline + cmdlineBasenameStart; *token;) { - for (tokenBase = token; *token && *token != '\n'; ++token) { - if (*token == '/') { - tokenBase = token + 1; - } - } - tokenLen = token - tokenBase; - - if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) && - strncmp(tokenBase, comm, commLen) == 0) { - *pCommStart = tokenBase - cmdline; - *pCommEnd = token - cmdline; - return true; - } - if (*token) { - do { - ++token; - } while (*token && '\n' == *token); - } - } - return false; -} - -static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) { - int matchLen; /* matching length to be returned */ - char delim; /* delimiter following basename */ - - /* cmdline prefix is an absolute path: it must match whole exe. */ - if (cmdline[0] == '/') { - matchLen = exeBaseLen + exeBaseOffset; - if (strncmp(cmdline, exe, matchLen) == 0) { - delim = cmdline[matchLen]; - if (delim == 0 || delim == '\n' || delim == ' ') { - return matchLen; - } - } - return 0; - } - - /* cmdline prefix is a relative path: We need to first match the basename at - * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe - * suffix. But there is a catch: Some processes modify their cmdline in ways - * that make htop's identification of the basename in cmdline unreliable. - * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to - * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with - * proccmdlineBasenameEnd at "gdm-autologin]". This issue could arise with - * chrome as well as it stores in cmdline its concatenated argument vector, - * without NUL delimiter between the arguments (which may contain a '/') - * - * So if needed, we adjust cmdlineBaseOffset to the previous (if any) - * component of the cmdline relative path, and retry the procedure. */ - bool delimFound; /* if valid basename delimiter found */ - do { - /* match basename */ - matchLen = exeBaseLen + cmdlineBaseOffset; - if (cmdlineBaseOffset < exeBaseOffset && - strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) { - delim = cmdline[matchLen]; - if (delim == 0 || delim == '\n' || delim == ' ') { - int i, j; - /* reverse match the cmdline prefix and exe suffix */ - for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1; - i >= 0 && j >= 0 && cmdline[i] == exe[j]; --i, --j) - ; - /* full match, with exe suffix being a valid relative path */ - if (i < 0 && j >= 0 && exe[j] == '/') { - return matchLen; - } - } - } - - /* Try to find the previous potential cmdlineBaseOffset - it would be - * preceded by '/' or nothing, and delimited by ' ' or '\n' */ - for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) { - if (delimFound) { - if (cmdline[cmdlineBaseOffset - 1] == '/') { - break; - } - } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') { - delimFound = true; - } - } - } while (delimFound); - - return 0; -} - -/* stpcpy, but also converts newlines to spaces */ -static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) { - for (; *srcStr; ++srcStr) { - *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr; - } - *dstStr = 0; - return dstStr; -} - -/* -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 -LinuxProcess_writeCommand() for coloring. The merged Command string is also -returned by LinuxProcess_getCommandStr() for searching, sorting and filtering. -*/ -void LinuxProcess_makeCommandStr(Process* this) { - 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; - - /* lp->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->cmdlineChanged && - !mc->commChanged && - !mc->exeChanged - ) { - return; - } - - /* The field separtor "│" 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 */ - mc->str = xCalloc(1, mc->maxLen + 2*SEPARATOR_LEN + 1); - } - - /* Preserve the settings used in this run */ - mc->prevMergeSet = showMergedCommand; - mc->prevPathSet = showProgramPath; - mc->prevCommSet = searchCommInCmdline; - mc->prevCmdlineSet = stripExeFromCmdline; - - /* Mark everything as unchanged */ - mc->cmdlineChanged = false; - mc->commChanged = false; - mc->exeChanged = false; - - /* Reset all locations that need extra handling when actually displaying */ - mc->highlightCount = 0; - memset(mc->highlights, 0, sizeof(mc->highlights)); - - size_t mbMismatch = 0; - #define WRITE_HIGHLIGHT(_offset, _length, _attr, _flags) \ - do { \ - /* Check if we still have capacity */ \ - assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \ - if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \ - continue; \ - \ - mc->highlights[mc->highlightCount].offset = str - strStart + _offset - mbMismatch; \ - mc->highlights[mc->highlightCount].length = _length; \ - mc->highlights[mc->highlightCount].attr = _attr; \ - mc->highlights[mc->highlightCount].flags = _flags; \ - mc->highlightCount++; \ - } while (0) - - #define WRITE_SEPARATOR \ - do { \ - WRITE_HIGHLIGHT(0, 1, CRT_colors[FAILED_READ], CMDLINE_HIGHLIGHT_FLAG_SEPARATOR); \ - mbMismatch += SEPARATOR_LEN - 1; \ - str = stpcpy(str, SEPARATOR); \ - } 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 delAttr = CRT_colors[FAILED_READ]; - - /* Establish some shortcuts to data we need */ - const char *cmdline = this->cmdline; - const char *procComm = this->procComm; - const char *procExe = this->procExe; - - char *strStart = mc->str; - char *str = strStart; - - int cmdlineBasenameStart = this->cmdlineBasenameStart; - int cmdlineBasenameEnd = this->cmdlineBasenameEnd; - - if (!cmdline) { - cmdlineBasenameStart = 0; - cmdlineBasenameEnd = 0; - cmdline = "(zombie)"; - } - - assert(cmdlineBasenameStart >= 0); - assert(cmdlineBasenameStart <= (int)strlen(cmdline)); - - if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ - if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column 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); - - WRITE_SEPARATOR; - } - } - - if (showProgramPath) { - WRITE_HIGHLIGHT(cmdlineBasenameStart, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); - (void)stpcpyWithNewlineConversion(str, cmdline); - } else { - WRITE_HIGHLIGHT(0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); - (void)stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameStart); - } - - return; - } - - int exeLen = strlen(this->procExe); - int exeBasenameOffset = this->procExeBasenameOffset; - int exeBasenameLen = exeLen - exeBasenameOffset; - - assert(exeBasenameOffset >= 0); - assert(exeBasenameOffset <= (int)strlen(procExe)); - - bool haveCommInExe = false; - if (procExe && procComm) { - haveCommInExe = strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0; - } - - /* Start with copying exe */ - if (showProgramPath) { - if (haveCommInExe) - WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); - WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); - if (this->procExeDeleted) - WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); - str = stpcpy(str, procExe); - } else { - if (haveCommInExe) - WRITE_HIGHLIGHT(0, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); - WRITE_HIGHLIGHT(0, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); - if (this->procExeDeleted) - WRITE_HIGHLIGHT(0, exeBasenameLen, delAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED); - str = stpcpy(str, procExe + exeBasenameOffset); - } - - bool haveCommInCmdline = false; - int commStart = 0; - int commEnd = 0; - - /* Try to match procComm with procExe's basename: This is reliable (predictable) */ - if (searchCommInCmdline) { - /* commStart/commEnd will be adjusted later along with cmdline */ - haveCommInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd); - } - - int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen); - - bool haveCommField = false; - - if (!haveCommInExe && !haveCommInCmdline && procComm) { - WRITE_SEPARATOR; - WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); - str = stpcpy(str, procComm); - haveCommField = true; - } - - if (matchLen) { - /* strip the matched exe prefix */ - cmdline += matchLen; - - commStart -= matchLen; - commEnd -= matchLen; - } - - if (!matchLen || (haveCommField && *cmdline)) { - /* cmdline will be a separate field */ - WRITE_SEPARATOR; - } - - if (!haveCommInExe && haveCommInCmdline && !haveCommField) - 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 WRITE_SEPARATOR - #undef WRITE_HIGHLIGHT -} - -static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) { - (void)baseAttr; - - const ProcessMergedCommand *mc = &this->mergedCommand; - - int strStart = RichString_size(str); - -// int commAttr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM]; - - const bool highlightBaseName = this->settings->highlightBaseName; - const bool highlightSeparator = true; - const bool highlightDeleted = true; - - RichString_appendWide(str, attr, this->mergedCommand.str); - - for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) - { - const ProcessCmdlineHighlight* hl = &mc->highlights[i]; - - if (!hl->length) - continue; - - if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_SEPARATOR) - if (!highlightSeparator) - continue; - - if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) - if (!highlightBaseName) - continue; - - if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_DELETED) - if (!highlightDeleted) - continue; - - RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length); - } -} - static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) { /* This code is from Process_writeField for COMM, but we invoke * LinuxProcess_writeCommand to display @@ -534,7 +182,7 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str, baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } if (!this->settings->treeView || this->indent == 0) { - LinuxProcess_writeCommand(this, attr, baseattr, str); + Process_writeCommand(this, attr, baseattr, str); } else { char* buf = buffer; int maxIndent = 0; @@ -566,7 +214,7 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str, 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] ); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer); - LinuxProcess_writeCommand(this, attr, baseattr, str); + Process_writeCommand(this, attr, baseattr, str); } } diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 90ce7c50..fe9d88e2 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -119,10 +119,6 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); bool LinuxProcess_setIOPriority(Process* this, Arg ioprio); -/* This function constructs the string that is displayed by - * LinuxProcess_writeCommand and also returned by LinuxProcess_getCommandStr */ -void LinuxProcess_makeCommandStr(Process *this); - bool Process_isThread(const Process* this); #endif diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 020fc0a5..ca2f2b80 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -1028,7 +1028,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc for (int i = 0; i < amtRead; i++) { /* newline used as delimiter - when forming the mergedCommand, newline is - * converted to space by LinuxProcess_makeCommandStr */ + * converted to space by Process_makeCommandStr */ if (command[i] == '\0') { command[i] = '\n'; } else { @@ -1432,7 +1432,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (!Process_isKernelThread(proc) && (proc->state != 'Z' || proc->mergedCommand.str) && (!Process_isUserlandThread(proc) || !settings->showThreadNames)) { - LinuxProcess_makeCommandStr(proc); + Process_makeCommandStr(proc); } #ifdef HAVE_DELAYACCT