diff --git a/BacktraceScreen.c b/BacktraceScreen.c index 96778063ee..e382615ea4 100644 --- a/BacktraceScreen.c +++ b/BacktraceScreen.c @@ -322,12 +322,14 @@ static void BacktracePanelRow_displayInformation(const Object* super, RichString const char* processName = ""; if (process->mergedCommand.str) { processName = process->mergedCommand.str; - for (size_t i = 0; i < process->mergedCommand.highlightCount; i++) { - const ProcessCmdlineHighlight* highlight = process->mergedCommand.highlights; - if (highlight->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) { - highlightLen = highlight->length; - highlightOffset = highlight->offset; - break; + if (process->mergedCommand.highlights) { + for (size_t i = 0; i < process->mergedCommand.highlightCount; i++) { + const ProcessCmdlineHighlight* highlight = &process->mergedCommand.highlights[i]; + if (highlight->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) { + highlightLen = highlight->length; + highlightOffset = highlight->offset; + break; + } } } } else if (process->cmdline) { diff --git a/CRT.c b/CRT.c index 389d7fb9c0..b345df68f3 100644 --- a/CRT.c +++ b/CRT.c @@ -1263,6 +1263,7 @@ IGNORE_WCASTQUAL_BEGIN } define_key("\033[I", KEY_FOCUS_IN); define_key("\033[O", KEY_FOCUS_OUT); + full_write_str(STDOUT_FILENO, "\033[?1004h"); #ifdef HTOP_NETBSD IGNORE_WCASTQUAL_END #undef define_key @@ -1318,6 +1319,11 @@ void CRT_done(void) { curs_set(1); endwin(); + const char* termType = getenv("TERM"); + if (terminalSupportsDefinedKeys(termType)) { + full_write_str(STDOUT_FILENO, "\033[?1004l"); + } + dumpStderr(); } diff --git a/CommandLine.c b/CommandLine.c index bbeaef070f..5dbe7c7c59 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -513,6 +513,8 @@ int CommandLine_run(int argc, char** argv) { if (settings->ss->allBranchesCollapsed) Table_collapseAllBranches(&pt->super); + xTrimMemory(); + ScreenManager_run(scr, NULL, NULL, NULL); Platform_done(); diff --git a/Hashtable.c b/Hashtable.c index 92b2c0387b..ff1cc1789c 100644 --- a/Hashtable.c +++ b/Hashtable.c @@ -25,7 +25,7 @@ in the source distribution for its full text. typedef struct HashtableItem_ { ht_key_t key; - size_t probe; + unsigned int probe; void* value; } HashtableItem; @@ -48,7 +48,7 @@ static void Hashtable_dump(const Hashtable* this) { size_t items = 0; for (size_t i = 0; i < this->size; i++) { - fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n", + fprintf(stderr, " item %5zu: key = %5u probe = %2u value = %p\n", i, this->buckets[i].key, this->buckets[i].probe, @@ -151,7 +151,7 @@ void Hashtable_clear(Hashtable* this) { static void insert(Hashtable* this, ht_key_t key, void* value) { size_t index = key % this->size; - size_t probe = 0; + unsigned int probe = 0; #ifndef NDEBUG size_t origIndex = index; #endif @@ -299,9 +299,24 @@ void* Hashtable_remove(Hashtable* this, ht_key_t key) { return res; } +void Hashtable_shrink(Hashtable* this) { + assert(Hashtable_isConsistent(this)); + + /* shrink on load-factor < 0.4 if size can be reduced */ + if (this->size > 61 && 10 * this->items < 4 * this->size) { + size_t targetSize = this->items ? (this->items * 2) : 13; + size_t newSize = nextPrime(targetSize); + if (newSize < this->size) { + Hashtable_setSize(this, newSize); + } + } + + assert(Hashtable_isConsistent(this)); +} + void* Hashtable_get(Hashtable* this, ht_key_t key) { size_t index = key % this->size; - size_t probe = 0; + unsigned int probe = 0; void* res = NULL; #ifndef NDEBUG size_t origIndex = index; diff --git a/Hashtable.h b/Hashtable.h index f51a29476a..0f4408f0f2 100644 --- a/Hashtable.h +++ b/Hashtable.h @@ -35,6 +35,8 @@ void Hashtable_put(Hashtable* this, ht_key_t key, void* value); void* Hashtable_remove(Hashtable* this, ht_key_t key); +void Hashtable_shrink(Hashtable* this); + void* Hashtable_get(Hashtable* this, ht_key_t key); void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); diff --git a/Machine.c b/Machine.c index cf824b499a..eb740debf5 100644 --- a/Machine.c +++ b/Machine.c @@ -25,6 +25,8 @@ void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId) { this->htopUserId = getuid(); + this->inBackground = false; + // discover fixed column width limits Row_setPidColumnWidth(Platform_getMaxPid()); diff --git a/Machine.h b/Machine.h index d7c71b94b1..21c6fac38a 100644 --- a/Machine.h +++ b/Machine.h @@ -70,6 +70,8 @@ typedef struct Machine_ { Table **tables; Table *activeTable; Table *processTable; + + bool inBackground; } Machine; diff --git a/Meter.h b/Meter.h index 2428fd9b21..94459b7eba 100644 --- a/Meter.h +++ b/Meter.h @@ -19,7 +19,7 @@ in the source distribution for its full text. #include "Object.h" -#define METER_TXTBUFFER_LEN 256 +#define METER_TXTBUFFER_LEN 127 #define MAX_METER_GRAPHDATA_VALUES 32768 #define METER_BUFFER_CHECK(buffer, size, written) \ @@ -118,17 +118,18 @@ struct Meter_ { const Machine* host; char* caption; + const int* curAttributes; + double* values; + double total; + void* meterData; + GraphData drawData; + MeterModeId mode; unsigned int param; - GraphData drawData; int h; int columnWidthCount; /**< only used internally by the Header */ uint8_t curItems; - const int* curAttributes; char txtBuffer[METER_TXTBUFFER_LEN]; - double* values; - double total; - void* meterData; }; typedef enum { diff --git a/Process.c b/Process.c index ee6df701e7..9f6b73de4d 100644 --- a/Process.c +++ b/Process.c @@ -41,12 +41,15 @@ in the source distribution for its full text. static const char* const kthreadID = "KTHREAD"; void Process_fillStarttimeBuffer(Process* this) { + (void)this; +} + +static void Process_formatStarttime(const Process* this, char* buffer, size_t n) { struct tm date; - time_t now = this->super.host->realtime.tv_sec; + time_t now = this->super.host ? this->super.host->realtime.tv_sec : time(NULL); (void) localtime_r(&this->starttime_ctime, &date); - strftime(this->starttime_show, - sizeof(this->starttime_show) - 1, + strftime(buffer, n, (this->starttime_ctime > now - 86400) ? "%R " : (this->starttime_ctime > now - 364 * 86400) ? "%b%d " : " %Y ", &date); } @@ -190,7 +193,7 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { bool showThreadNames = settings->showThreadNames; bool shadowDistPathPrefix = settings->shadowDistPathPrefix; - uint64_t settingsStamp = settings->lastUpdate; + uint32_t settingsStamp = (uint32_t)settings->lastUpdate; /* Nothing to do to (Re)Generate the Command string, if the process is: * - a kernel thread, or @@ -200,6 +203,15 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { return; if (this->state == ZOMBIE && !this->mergedCommand.str) return; + if (Process_isUserlandThread(this) && !showThreadNames) { + free(mc->str); + mc->str = NULL; + free(mc->highlights); + mc->highlights = NULL; + mc->highlightCount = 0; + mc->lastUpdate = settingsStamp; + return; + } /* this->mergedCommand.str needs updating only if its state or contents changed. * Its content is based on the fields cmdline, comm, and exe. */ @@ -216,7 +228,7 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { /* 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->procComm[0] ? strlen(this->procComm) : 0; maxLen += this->procExe ? strlen(this->procExe) : 0; free(mc->str); @@ -224,18 +236,19 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { /* 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 { \ + if (!mc->highlights) \ + mc->highlights = xCalloc(PROCESS_MAX_HIGHLIGHTS, sizeof(ProcessCmdlineHighlight));\ /* Check if we still have capacity */ \ - assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \ - if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \ + assert(mc->highlightCount < PROCESS_MAX_HIGHLIGHTS); \ + if (mc->highlightCount >= PROCESS_MAX_HIGHLIGHTS) \ break; \ \ - mc->highlights[mc->highlightCount].offset = str - strStart + (_offset) - mbMismatch; \ - mc->highlights[mc->highlightCount].length = _length; \ + mc->highlights[mc->highlightCount].offset = (uint32_t)(str - strStart + (_offset) - mbMismatch); \ + mc->highlights[mc->highlightCount].length = (uint32_t)(_length); \ mc->highlights[mc->highlightCount].attr = _attr; \ mc->highlights[mc->highlightCount].flags = _flags; \ mc->highlightCount++; \ @@ -311,7 +324,7 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { /* Establish some shortcuts to data we need */ const char* cmdline = this->cmdline; - const char* procComm = this->procComm; + const char* procComm = this->procComm[0] ? this->procComm : NULL; const char* procExe = this->procExe; char* strStart = mc->str; @@ -485,6 +498,16 @@ void Process_makeCommandStr(Process* this, const Settings* settings) { if (*cmdline) (void)stpcpyWithNewlineConversion(str, cmdline); + size_t actualLen = strlen(mc->str) + 1; + mc->str = xRealloc(mc->str, actualLen); + + if (mc->highlightCount == 0 && mc->highlights) { + free(mc->highlights); + mc->highlights = NULL; + } else if (mc->highlightCount > 0 && mc->highlightCount < PROCESS_MAX_HIGHLIGHTS) { + mc->highlights = xReallocArray(mc->highlights, mc->highlightCount, sizeof(ProcessCmdlineHighlight)); + } + #undef CHECK_AND_MARK_DIST_PATH_PREFIXES #undef CHECK_AND_MARK #undef WRITE_SEPARATOR @@ -539,7 +562,10 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin RichString_appendWide(str, attr, mergedCommand); - for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) { + if (!mc->highlights) + return; + + for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, PROCESS_MAX_HIGHLIGHTS); i < hlCount; i++) { const ProcessCmdlineHighlight* hl = &mc->highlights[i]; if (!hl->length) @@ -642,7 +668,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { } case PROC_COMM: { const char* procComm; - if (this->procComm) { + if (this->procComm[0]) { attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM]; procComm = this->procComm; } else { @@ -714,7 +740,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { if (this->nlwp == 1) attr = CRT_colors[PROCESS_SHADOW]; - xSnprintf(buffer, n, "%4ld ", this->nlwp); + xSnprintf(buffer, n, "%4d ", this->nlwp); break; case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break; case PERCENT_NORM_CPU: { @@ -730,7 +756,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { if (this->priority <= -100) xSnprintf(buffer, n, " RT "); else - xSnprintf(buffer, n, "%3ld ", this->priority); + xSnprintf(buffer, n, "%3d ", this->priority); break; case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(settings, this->processor)); break; case SCHEDULERPOLICY: { @@ -743,7 +769,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { break; } case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break; - case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break; + case STARTTIME: Process_formatStarttime(this, buffer, n); break; case STATE: xSnprintf(buffer, n, "%c ", processStateChar(this->state)); switch (this->state) { @@ -815,14 +841,26 @@ void Process_writeField(const Process* this, RichString* str, RowField field) { RichString_appendAscii(str, attr, buffer); } +void Process_cleanMergedCommand(Process* this) { + free(this->mergedCommand.highlights); + this->mergedCommand.highlights = NULL; + free(this->mergedCommand.str); + this->mergedCommand.str = NULL; + this->mergedCommand.highlightCount = 0; + this->mergedCommand.lastUpdate = 0; +} + void Process_done(Process* this) { assert(this != NULL); - free(this->cmdline); - free(this->procComm); - free(this->procExe); - free(this->procCwd); - free(this->mergedCommand.str); - free(this->tty_name); + RefString_unref(this->cmdline); + this->cmdline = NULL; + RefString_unref(this->procExe); + this->procExe = NULL; + RefString_unref(this->procCwd); + this->procCwd = NULL; + Process_cleanMergedCommand(this); + RefString_unref(this->tty_name); + this->tty_name = NULL; } /* This function returns the string displayed in Command column, so that sorting @@ -903,6 +941,7 @@ void Process_init(Process* this, const Machine* host) { this->cmdlineBasenameEnd = 0; this->st_uid = (uid_t)-1; + this->procComm[0] = '\0'; } static bool Process_setPriority(Process* this, int priority) { @@ -975,8 +1014,8 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField case COMM: return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2)); case PROC_COMM: { - const char* comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : ""); - const char* comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : ""); + const char* comm1 = p1->procComm[0] ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : ""); + const char* comm2 = p2->procComm[0] ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : ""); return SPACESHIP_NULLSTR(comm1, comm2); } case PROC_EXE: { @@ -1041,15 +1080,18 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField } void Process_updateComm(Process* this, const char* comm) { - if (!this->procComm && !comm) + if (!comm) { + if (this->procComm[0] == '\0') + return; + this->procComm[0] = '\0'; + this->mergedCommand.lastUpdate = 0; return; + } - if (this->procComm && comm && String_eq(this->procComm, comm)) + if (String_eq(this->procComm, comm)) return; - free(this->procComm); - this->procComm = comm ? xStrdup(comm) : NULL; - + String_safeStrncpy(this->procComm, comm, sizeof(this->procComm)); this->mergedCommand.lastUpdate = 0; } @@ -1085,20 +1127,29 @@ void Process_updateCmdline(Process* this, const char* cmdline, size_t basenameSt if (this->cmdline && cmdline && String_eq(this->cmdline, cmdline)) return; - free(this->cmdline); - this->cmdline = cmdline ? xStrdup(cmdline) : NULL; + RefString_assignNew(&this->cmdline, cmdline); if (Process_isKernelThread(this)) { /* kernel threads have no basename */ this->cmdlineBasenameStart = 0; this->cmdlineBasenameEnd = 0; } else { - this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd); - this->cmdlineBasenameEnd = basenameEnd; + this->cmdlineBasenameStart = (uint32_t)((basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd)); + this->cmdlineBasenameEnd = (uint32_t)basenameEnd; } this->mergedCommand.lastUpdate = 0; } +void Process_updateCmdlineShared(Process* this, char* cmdline, size_t basenameStart, size_t basenameEnd) { + if (this->cmdline == cmdline) + return; + + RefString_assign(&this->cmdline, cmdline); + this->cmdlineBasenameStart = (uint32_t)basenameStart; + this->cmdlineBasenameEnd = (uint32_t)basenameEnd; + this->mergedCommand.lastUpdate = 0; +} + void Process_updateExe(Process* this, const char* exe) { if (!this->procExe && !exe) return; @@ -1106,19 +1157,42 @@ void Process_updateExe(Process* this, const char* exe) { if (this->procExe && exe && String_eq(this->procExe, exe)) return; - free(this->procExe); + RefString_assignNew(&this->procExe, exe); if (exe) { - this->procExe = xStrdup(exe); const char* lastSlash = strrchr(exe, '/'); - this->procExeBasenameOffset = (lastSlash && *(lastSlash + 1) != '\0' && lastSlash != exe) ? (size_t)(lastSlash - exe + 1) : 0; + this->procExeBasenameOffset = (lastSlash && *(lastSlash + 1) != '\0' && lastSlash != exe) ? (uint32_t)(lastSlash - exe + 1) : 0; } else { - this->procExe = NULL; this->procExeBasenameOffset = 0; } this->mergedCommand.lastUpdate = 0; } +void Process_updateExeShared(Process* this, char* exe, size_t basenameOffset) { + if (this->procExe == exe) + return; + + RefString_assign(&this->procExe, exe); + this->procExeBasenameOffset = (uint32_t)basenameOffset; + this->mergedCommand.lastUpdate = 0; +} + +void Process_updateTty(Process* this, const char* tty_name) { + RefString_assignNew(&this->tty_name, tty_name); +} + +void Process_updateTtyShared(Process* this, char* tty_name) { + RefString_assign(&this->tty_name, tty_name); +} + +void Process_updateCwd(Process* this, const char* cwd) { + RefString_assignNew(&this->procCwd, cwd); +} + +void Process_updateCwdShared(Process* this, char* cwd) { + RefString_assign(&this->procCwd, cwd); +} + void Process_updateCPUFieldWidths(float percentage) { if (!isgreaterequal(percentage, 99.9F)) { Row_updateFieldWidth(PERCENT_CPU, 4); diff --git a/Process.h b/Process.h index 5bfdfe990d..43a4611cd2 100644 --- a/Process.h +++ b/Process.h @@ -61,64 +61,38 @@ struct Settings_; // IWYU pragma: keep /* Holds information about regions of the cmdline that should be * highlighted (e.g. program basename, delimiter, comm). */ typedef struct ProcessCmdlineHighlight_ { - size_t offset; /* first character to highlight */ - size_t length; /* How many characters to highlight, zero if unused */ - int attr; /* The attributes used to highlight */ - int flags; /* Special flags used for selective highlighting, zero for always */ + uint32_t offset; /* first character to highlight */ + uint32_t length; /* How many characters to highlight, zero if unused */ + int attr; /* The attributes used to highlight */ + int flags; /* Special flags used for selective highlighting, zero for always */ } ProcessCmdlineHighlight; +#define PROCESS_MAX_HIGHLIGHTS 8 + /* ProcessMergedCommand is populated by Process_makeCommandStr: It * contains the merged Command string, and the information needed by * Process_writeCommand to color the string. str will be NULL for kernel * threads and zombies */ typedef struct ProcessMergedCommand_ { - uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */ char* str; /* merged Command string */ - size_t highlightCount; /* how many portions of cmdline to highlight */ - ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */ + ProcessCmdlineHighlight* highlights; /* which portions of cmdline to highlight */ + uint32_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */ + uint32_t highlightCount; /* how many portions of cmdline to highlight */ } ProcessMergedCommand; typedef struct Process_ { /* Super object for emulated OOP */ Row super; - /* Process group identifier */ - int pgrp; - - /* Session identifier */ - int session; - - /* Foreground group identifier of the controlling terminal */ - int tpgid; - - /* This is a kernel (helper) task */ - bool isKernelThread; - - /* This is a userland thread / LWP */ - bool isUserlandThread; - - /* This process is running inside a container */ - Tristate isRunningInContainer; - /* Controlling terminal identifier of the process */ unsigned long int tty_nr; /* Controlling terminal name of the process */ char* tty_name; - /* User identifier */ - uid_t st_uid; - /* User name */ const char* user; - /* Non root owned process with elevated privileges - * Linux: - * - from file capabilities - * - inherited from the ambient set - */ - Tristate elevated_priv; - /* Process runtime (in hundredth of a second) */ unsigned long long int time; @@ -128,76 +102,101 @@ typedef struct Process_ { */ char* cmdline; - /* End Offset in cmdline of the process basename */ - size_t cmdlineBasenameEnd; - - /* Start Offset in cmdline of the process basename */ - size_t cmdlineBasenameStart; - - /* The process' "command" name */ - char* procComm; - /* The main process executable */ char* procExe; /* The process/thread working directory */ char* procCwd; + /* Process start time (in seconds elapsed since the Epoch) */ + time_t starttime_ctime; + + /* Total program size (in kilobytes) */ + long m_virt; + + /* Resident set size (in kilobytes) */ + long m_resident; + + /* Number of minor faults the process has made which have not required loading a memory page from disk */ + unsigned long int minflt; + + /* Number of major faults the process has made which have required loading a memory page from disk */ + unsigned long int majflt; + + /* The process' "command" name */ + char procComm[24]; + + /* + * Internal state for merged Command display + */ + ProcessMergedCommand mergedCommand; + + /* End Offset in cmdline of the process basename */ + uint32_t cmdlineBasenameEnd; + + /* Start Offset in cmdline of the process basename */ + uint32_t cmdlineBasenameStart; + /* Offset in procExe of the process basename */ - size_t procExeBasenameOffset; + uint32_t procExeBasenameOffset; - /* Tells if the executable has been replaced in the filesystem since start */ - bool procExeDeleted; + /* Scheduling priority */ + int priority; - /* Tells if the process uses replaced shared libraries since start */ - bool usesDeletedLib; + /* Number of threads in this process */ + int nlwp; + + /* Process group identifier */ + int pgrp; + + /* Session identifier */ + int session; + + /* Foreground group identifier of the controlling terminal */ + int tpgid; /* CPU number last executed on */ int processor; + /* User identifier */ + uid_t st_uid; + /* CPU usage during last cycle (in percent) */ float percent_cpu; /* Memory usage during last cycle (in percent) */ float percent_mem; - /* Scheduling priority */ - long int priority; - /* Nice value */ int nice; - /* Number of threads in this process */ - long int nlwp; - - /* Process start time (in seconds elapsed since the Epoch) */ - time_t starttime_ctime; - - /* Process start time (cached formatted string) */ - char starttime_show[8]; + /* Current scheduling policy */ + int scheduling_policy; - /* Total program size (in kilobytes) */ - long m_virt; + /* This process is running inside a container */ + int8_t isRunningInContainer; - /* Resident set size (in kilobytes) */ - long m_resident; + /* Non root owned process with elevated privileges + * Linux: + * - from file capabilities + * - inherited from the ambient set + */ + int8_t elevated_priv; - /* Number of minor faults the process has made which have not required loading a memory page from disk */ - unsigned long int minflt; + /* Process state enum field (platform dependent) */ + uint8_t state; - /* Number of major faults the process has made which have required loading a memory page from disk */ - unsigned long int majflt; + /* This is a kernel (helper) task */ + bool isKernelThread; - /* Process state enum field (platform dependent) */ - ProcessState state; + /* This is a userland thread / LWP */ + bool isUserlandThread; - /* Current scheduling policy */ - int scheduling_policy; + /* Tells if the executable has been replaced in the filesystem since start */ + bool procExeDeleted; - /* - * Internal state for merged Command display - */ - ProcessMergedCommand mergedCommand; + /* Tells if the process uses replaced shared libraries since start */ + bool usesDeletedLib; } Process; typedef struct ProcessFieldData_ { @@ -327,7 +326,15 @@ const char* Process_getCommand(const Process* this); void Process_updateComm(Process* this, const char* comm); void Process_updateCmdline(Process* this, const char* cmdline, size_t basenameStart, size_t basenameEnd); +void Process_updateCmdlineShared(Process* this, char* cmdline, size_t basenameStart, size_t basenameEnd); void Process_updateExe(Process* this, const char* exe); +void Process_updateExeShared(Process* this, char* exe, size_t basenameOffset); +void Process_updateTty(Process* this, const char* tty_name); +void Process_updateTtyShared(Process* this, char* tty_name); +void Process_updateCwd(Process* this, const char* cwd); +void Process_updateCwdShared(Process* this, char* cwd); + +void Process_cleanMergedCommand(Process* this); /* This function constructs the string that is displayed by * Process_writeCommand and also returned by Process_getCommand */ diff --git a/ProcessTable.c b/ProcessTable.c index 0e576f9b0e..f64fb2bffb 100644 --- a/ProcessTable.c +++ b/ProcessTable.c @@ -72,7 +72,9 @@ static void ProcessTable_cleanupEntries(Table* super) { Process* p = (Process*) Vector_get(super->rows, i); // tidy up Process state after refreshing the ProcessTable table - Process_makeCommandStr(p, settings); + if (!host->inBackground) { + Process_makeCommandStr(p, settings); + } // keep track of the highest UID and PID for column scaling if (p->st_uid > host->maxUserId) diff --git a/Row.h b/Row.h index 00a5f88dba..e8580f31fd 100644 --- a/Row.h +++ b/Row.h @@ -41,10 +41,22 @@ typedef struct Row_ { /* Pointer to quasi-global data */ const struct Machine_* host; + /* + * Internal time counts for showing new and exited processes. + */ + uint64_t seenStampMs; + uint64_t tombStampMs; + int id; int group; int parent; + /* + * Internal state for tree-mode. + */ + int32_t indent; + uint16_t tree_depth; + /* Has no known parent */ bool isRoot; @@ -62,18 +74,6 @@ typedef struct Row_ { /* Whether the row was updated during the last scan */ bool updated; - - /* - * Internal state for tree-mode. - */ - int32_t indent; - unsigned int tree_depth; - - /* - * Internal time counts for showing new and exited processes. - */ - uint64_t seenStampMs; - uint64_t tombStampMs; } Row; typedef Row* (*Row_New)(const struct Machine_*); diff --git a/ScreenManager.c b/ScreenManager.c index 91ca7500b9..74bf16c523 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -119,7 +119,7 @@ void ScreenManager_resize(ScreenManager* this) { Panel_move(panel, lastX, y1_header); } -static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) { +static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw, bool inBackground) { Machine* host = this->host; Platform_gettime_realtime(&host->realtime, &host->realtimeMs); @@ -163,9 +163,13 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi } if (*redraw) { - Table_rebuildPanel(host->activeTable); - if (!this->state->hideMeters) - Header_draw(this->header); + if (!inBackground) { + Table_rebuildPanel(host->activeTable); + if (!this->state->hideMeters) + Header_draw(this->header); + } else { + host->activeTable->needsSort = true; + } } *rescan = false; @@ -241,9 +245,30 @@ static void ScreenManager_drawPanels(ScreenManager* this, size_t focus, bool for } } +static void ScreenManager_exitBackground(ScreenManager* this) { + if (this->host) { + this->host->inBackground = false; + for (size_t t = 0; t < this->host->tableCount; t++) { + Table* table = this->host->tables[t]; + if (!table) + continue; + for (int i = 0; i < Vector_size(table->rows); i++) { + Row* row = (Row*) Vector_get(table->rows, i); + if (Object_isA((const Object*) row, (const ObjectClass*) &Process_class)) { + Process_makeCommandStr((Process*) row, this->host->settings); + } + } + } + if (this->host->activeTable) { + Table_rebuildPanel(this->host->activeTable); + } + } +} + void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name) { bool quit = false; size_t focus = 0; + bool inBackground = false; Panel* panelFocus = (Panel*) Vector_get(this->panels, focus); Settings* settings = this->host->settings; @@ -264,11 +289,13 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con while (!quit) { if (this->header) { - checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw); + checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw, inBackground); } if (redraw || force_redraw) { - ScreenManager_drawPanels(this, focus, force_redraw); + if (!inBackground) { + ScreenManager_drawPanels(this, focus, force_redraw); + } force_redraw = false; if (this->host->iterationsRemaining != -1) { if (!--this->host->iterationsRemaining) { @@ -369,9 +396,22 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con closeTimeout = 0; } redraw = false; + + xTrimMemory(); + continue; } + if (ch != KEY_FOCUS_OUT) { + if (inBackground) { + inBackground = false; + ScreenManager_exitBackground(this); + force_redraw = true; + rescan = true; + sortTimeout = 0; + } + } + switch (ch) { case KEY_ALT('H'): ch = KEY_LEFT; break; case KEY_ALT('J'): ch = KEY_DOWN; break; @@ -414,7 +454,40 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con continue; } case KEY_FOCUS_IN: + break; case KEY_FOCUS_OUT: + inBackground = true; + if (this->host) { + this->host->inBackground = true; + for (size_t t = 0; t < this->host->tableCount; t++) { + Table* table = this->host->tables[t]; + if (!table) + continue; + Table_preserveSelection(table); + Vector_prune(table->displayList); + Vector_shrink(table->displayList); + if (table->panel && table->panel->items) { + Vector_prune(table->panel->items); + Vector_shrink(table->panel->items); + } + Vector_shrink(table->rows); + Hashtable_shrink(table->table); + table->needsSort = true; + for (int i = 0; i < Vector_size(table->rows); i++) { + Row* row = (Row*) Vector_get(table->rows, i); + if (Object_isA((const Object*) row, (const ObjectClass*) &Process_class)) { + Process_cleanMergedCommand((Process*) row); + } + } + } + } + for (size_t i = 0; i < this->panelCount; i++) { + Panel* p = (Panel*) Vector_get(this->panels, i); + if (p && p->items) { + Vector_shrink(p->items); + } + } + xTrimMemory(); break; case KEY_LEFT: case KEY_CTRL('B'): diff --git a/Table.c b/Table.c index ef74cf2a6f..748da81379 100644 --- a/Table.c +++ b/Table.c @@ -22,6 +22,7 @@ in the source distribution for its full text. #include "Row.h" #include "RowField.h" #include "Vector.h" +#include "XUtils.h" Table* Table_init(Table* this, const ObjectClass* klass, Machine* host) { @@ -158,7 +159,7 @@ static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, in else row->indent = nextIndent; - row->tree_depth = level + 1; + row->tree_depth = (uint16_t)(level + 1); } } @@ -440,6 +441,11 @@ void Table_rebuildPanel(Table* this) { this->stableId = -1; } } + + Vector_shrink(this->displayList); + if (this->panel && this->panel->items) { + Vector_shrink(this->panel->items); + } } void Table_printHeader(const Settings* settings, RichString* header) { @@ -495,12 +501,12 @@ Row* Table_cleanupRow(Table* table, Row* row, int idx) { if (row->tombStampMs > 0) { // remove tombed process - if (host->monotonicMs >= row->tombStampMs) { + if (host->inBackground || host->monotonicMs >= row->tombStampMs) { goto remove; } } else if (!row->updated) { // process no longer exists - if (settings->highlightChanges && row->wasShown) { + if (!host->inBackground && settings->highlightChanges && row->wasShown) { // mark tombed row->tombStampMs = host->monotonicMs + 1000 * settings->highlightDelaySecs; } else { @@ -515,6 +521,16 @@ Row* Table_cleanupRow(Table* table, Row* row, int idx) { return NULL; } +void Table_compact(Table* this, int dirtyIndex) { + int prevSize = Vector_size(this->rows); + Vector_compact(this->rows, dirtyIndex); + Vector_shrink(this->rows); + Hashtable_shrink(this->table); + this->needsSort = true; + if (Vector_size(this->rows) < prevSize) + xTrimMemory(); +} + void Table_cleanupEntries(Table* this) { // Lowest index of the row that is soft-removed. Used to speed up // compaction. diff --git a/Table.h b/Table.h index 006e74609a..59e9da58a8 100644 --- a/Table.h +++ b/Table.h @@ -91,9 +91,6 @@ void Table_cleanupEntries(Table* this); Row* Table_cleanupRow(Table* this, Row* row, int idx); -static inline void Table_compact(Table* this, int dirtyIndex) { - Vector_compact(this->rows, dirtyIndex); - this->needsSort = true; -} +void Table_compact(Table* this, int dirtyIndex); #endif diff --git a/Vector.c b/Vector.c index d25955af94..2a2ee97285 100644 --- a/Vector.c +++ b/Vector.c @@ -279,6 +279,20 @@ void Vector_compact(Vector* this, int dirtyIndex) { assert(Vector_isConsistent(this)); } +void Vector_shrink(Vector* this) { + assert(Vector_isConsistent(this)); + if (this->arraySize <= VECTOR_DEFAULT_SIZE) + return; + + int targetSize = this->items ? MAXIMUM(this->items + this->growthRate, VECTOR_DEFAULT_SIZE) : VECTOR_DEFAULT_SIZE; + if (this->arraySize > targetSize) { + this->array = (Object**) xRealloc(this->array, targetSize * sizeof(Object*)); + this->arraySize = targetSize; + } + + assert(Vector_isConsistent(this)); +} + void Vector_moveUp(Vector* this, int idx) { assert(idx >= 0 && idx < this->items); assert(Vector_isConsistent(this)); diff --git a/Vector.h b/Vector.h index 9faccd8669..5a0baaec65 100644 --- a/Vector.h +++ b/Vector.h @@ -57,6 +57,8 @@ Object* Vector_softRemove(Vector* this, int idx); /* Vector_compact reclaims space free'd up by Vector_softRemove, if any. */ void Vector_compact(Vector* this, int dirtyIndex); +void Vector_shrink(Vector* this); + void Vector_moveUp(Vector* this, int idx); void Vector_moveDown(Vector* this, int idx); diff --git a/XUtils.c b/XUtils.c index 9e2d6bf1a9..718c2138b6 100644 --- a/XUtils.c +++ b/XUtils.c @@ -25,6 +25,10 @@ in the source distribution for its full text. #include "CRT.h" #include "Macros.h" +#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_TRIM) +#include +#endif + void fail(void) { CRT_done(); @@ -106,6 +110,12 @@ void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size) return ret; } +void xTrimMemory(void) { +#if defined(HAVE_MALLOC_TRIM) + malloc_trim(0); +#endif +} + inline bool String_contains_i(const char* s1, const char* s2, bool multi) { // we have a multi-string search term, handle as special case for performance reasons if (multi && strchr(s2, '|')) { @@ -310,6 +320,66 @@ void free_and_xStrdup(char** ptr, const char* str) { *ptr = xStrdup(str); } +typedef struct RefString_ { + uint32_t refCount; + char data[]; +} RefString; + +static inline RefString* RefString_fromData(char* s) { + return (RefString*)(void*)(s - offsetof(RefString, data)); +} + +char* RefString_new(const char* s) { + if (!s) + return NULL; + + size_t len = strlen(s); + RefString* rs = xMalloc(sizeof(RefString) + len + 1); + rs->refCount = 1; + memcpy(rs->data, s, len + 1); + return rs->data; +} + +char* RefString_ref(char* s) { + if (!s) + return NULL; + + RefString* rs = RefString_fromData(s); + rs->refCount++; + return s; +} + +void RefString_unref(char* s) { + if (!s) + return; + + RefString* rs = RefString_fromData(s); + assert(rs->refCount > 0); + if (--rs->refCount == 0) + free(rs); +} + +void RefString_assign(char** dest, char* src) { + if (*dest == src) + return; + + if (*dest) + RefString_unref(*dest); + + *dest = src ? RefString_ref(src) : NULL; +} + +void RefString_assignNew(char** dest, const char* src) { + if (*dest && src && String_eq(*dest, src)) + return; + + char* newStr = src ? RefString_new(src) : NULL; + if (*dest) + RefString_unref(*dest); + + *dest = newStr; +} + char* xStrndup(const char* str, size_t len) { char* data = strndup(str, len); if (!data) { diff --git a/XUtils.h b/XUtils.h index 9a5a0905dd..e7e8ed4a9a 100644 --- a/XUtils.h +++ b/XUtils.h @@ -46,6 +46,8 @@ void* xReallocArray(void* ptr, size_t nmemb, size_t size); ATTR_RETNONNULL ATTR_ALLOC_SIZE2(3, 4) void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size); +void xTrimMemory(void); + /* * String_startsWith gives better performance if strlen(match) can be computed * at compile time (e.g. when they are immutable string literals). :) @@ -121,6 +123,16 @@ char* xStrdup(const char* str); ATTR_NONNULL void free_and_xStrdup(char** ptr, const char* str); +char* RefString_new(const char* s); + +char* RefString_ref(char* s); + +void RefString_unref(char* s); + +void RefString_assign(char** dest, char* src); + +void RefString_assignNew(char** dest, const char* src); + ATTR_NONNULL ATTR_RETNONNULL ATTR_MALLOC ATTR_ACCESS3_R(1, 2) char* xStrndup(const char* str, size_t len); diff --git a/configure.ac b/configure.ac index 0dae464ae0..0dc28d2cfd 100644 --- a/configure.ac +++ b/configure.ac @@ -218,7 +218,7 @@ m4_version_prereq( # Optional Section -AC_CHECK_HEADERS([execinfo.h]) +AC_CHECK_HEADERS([execinfo.h malloc.h]) if test "$my_htop_platform" = darwin; then AC_CHECK_HEADERS([mach/mach_time.h]) @@ -414,6 +414,7 @@ AC_CHECK_FUNCS([ \ faccessat \ fstatat \ host_get_clock_service \ + malloc_trim \ memfd_create \ openat \ readlinkat \ diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 851a84f540..2c44477cf9 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -120,19 +120,12 @@ static void DarwinProcess_updateCwd(pid_t pid, Process* proc) { struct proc_vnodepathinfo vpi; int r = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)); - if (r <= 0) { - free(proc->procCwd); - proc->procCwd = NULL; + if (r <= 0 || !vpi.pvi_cdir.vip_path[0]) { + Process_updateCwd(proc, NULL); return; } - if (!vpi.pvi_cdir.vip_path[0]) { - free(proc->procCwd); - proc->procCwd = NULL; - return; - } - - free_and_xStrdup(&proc->procCwd, vpi.pvi_cdir.vip_path); + Process_updateCwd(proc, vpi.pvi_cdir.vip_path); } static void DarwinProcess_updateCmdLine(const struct kinfo_proc* k, Process* proc) { @@ -343,7 +336,9 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, * field is enabled in the settings. */ if (settings->ss->flags & PROCESS_FLAG_TTY) { - proc->tty_name = DarwinProcess_getDevname((dev_t)proc->tty_nr); + char* devname = DarwinProcess_getDevname((dev_t)proc->tty_nr); + Process_updateTty(proc, devname); + free(devname); if (!proc->tty_name) { /* devname failed: prevent us from calling it again */ proc->tty_nr = NODEV; @@ -355,7 +350,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) ? ZOMBIE : UNKNOWN; + proc->state = (uint8_t)((ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN); /* Make sure the updated flag is set */ proc->super.updated = true; @@ -507,10 +502,10 @@ void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt) { // TODO: depend on setting #ifdef HAVE_THREAD_EXTENDED_INFO_DATA_T - const char* name = extended_info.pth_name[0] != '\0' ? extended_info.pth_name : proc->procComm; + const char* name = extended_info.pth_name[0] != '\0' ? extended_info.pth_name : (proc->procComm[0] ? proc->procComm : NULL); #else // Not provided in thread_basic_info_data_t; fall back to the process name - const char* name = proc->procComm; + const char* name = proc->procComm[0] ? proc->procComm : NULL; #endif Process_updateCmdline(tprocess, name, 0, name ? strlen(name) : 0); diff --git a/dragonflybsd/DragonFlyBSDProcessTable.c b/dragonflybsd/DragonFlyBSDProcessTable.c index 5c4647991e..d803bb09fc 100644 --- a/dragonflybsd/DragonFlyBSDProcessTable.c +++ b/dragonflybsd/DragonFlyBSDProcessTable.c @@ -81,20 +81,12 @@ static void DragonFlyBSDProcessTable_updateCwd(const struct kinfo_proc* kproc, P const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, kproc->kp_pid }; char buffer[2048]; size_t size = sizeof(buffer); - if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { - free(proc->procCwd); - proc->procCwd = NULL; + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0 || buffer[0] == '\0') { + Process_updateCwd(proc, NULL); return; } - /* Kernel threads return an empty buffer */ - if (buffer[0] == '\0') { - free(proc->procCwd); - proc->procCwd = NULL; - return; - } - - free_and_xStrdup(&proc->procCwd, buffer); + Process_updateCwd(proc, buffer); } static void DragonFlyBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc* kproc, Process* proc) { @@ -176,12 +168,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { proc->tty_nr = kproc->kp_tdev; // control terminal device number const char* name = (kproc->kp_tdev != NODEV) ? devname(kproc->kp_tdev, S_IFCHR) : NULL; - if (!name) { - free(proc->tty_name); - proc->tty_name = NULL; - } else { - free_and_xStrdup(&proc->tty_name, name); - } + Process_updateTty(proc, name); DragonFlyBSDProcessTable_updateExe(kproc, proc); DragonFlyBSDProcessTable_updateProcessName(dhost->kd, kproc, proc); diff --git a/freebsd/FreeBSDProcessTable.c b/freebsd/FreeBSDProcessTable.c index be6356ace8..d71488f50d 100644 --- a/freebsd/FreeBSDProcessTable.c +++ b/freebsd/FreeBSDProcessTable.c @@ -81,22 +81,14 @@ static void FreeBSDProcessTable_updateCwd(const struct kinfo_proc* kproc, Proces const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, kproc->ki_pid }; char buffer[2048]; size_t size = sizeof(buffer); - if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { - free(proc->procCwd); - proc->procCwd = NULL; + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0 || buffer[0] == '\0') { + Process_updateCwd(proc, NULL); return; } - /* Kernel threads return an empty buffer */ - if (buffer[0] == '\0') { - free(proc->procCwd); - proc->procCwd = NULL; - return; - } - - free_and_xStrdup(&proc->procCwd, buffer); + Process_updateCwd(proc, buffer); #else - proc->procCwd = NULL; + Process_updateCwd(proc, NULL); #endif } @@ -204,12 +196,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { proc->tty_nr = kproc->ki_tdev; const char* name = (kproc->ki_tdev != NODEV) ? devname(kproc->ki_tdev, S_IFCHR) : NULL; - if (!name) { - free(proc->tty_name); - proc->tty_name = NULL; - } else { - free_and_xStrdup(&proc->tty_name, name); - } + Process_updateTty(proc, name); } else { if (fp->jid != kproc->ki_jid) { // process can enter jail anytime diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index 4b44c54b8b..e42aff99cf 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -865,24 +865,27 @@ void Machine_scan(Machine* super) { LinuxMachine* this = (LinuxMachine*) super; LinuxMachine_scanMemoryInfo(this); - LinuxMachine_scanZswapInfo(this); - LinuxMachine_scanHugePages(this); - LinuxMachine_scanZfsArcstats(this); - LinuxMachine_scanZramInfo(this); LinuxMachine_scanCPUTime(this); - const Settings* settings = super->settings; - if (settings->showCPUFrequency + if (!super->inBackground) { + LinuxMachine_scanZswapInfo(this); + LinuxMachine_scanHugePages(this); + LinuxMachine_scanZfsArcstats(this); + LinuxMachine_scanZramInfo(this); + + const Settings* settings = super->settings; + if (settings->showCPUFrequency #ifdef HAVE_SENSORS_SENSORS_H - || settings->showCPUTemperature + || settings->showCPUTemperature #endif - ) - LinuxMachine_scanCPUFrequency(this); + ) + LinuxMachine_scanCPUFrequency(this); - #ifdef HAVE_SENSORS_SENSORS_H - if (settings->showCPUTemperature) - LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs); - #endif + #ifdef HAVE_SENSORS_SENSORS_H + if (settings->showCPUTemperature) + LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs); + #endif + } } Machine* Machine_new(UsersTable* usersTable, uid_t userId) { diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 930ef1ff92..9be3954e45 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -119,10 +119,14 @@ Process* LinuxProcess_new(const Machine* host) { void Process_delete(Object* cast) { LinuxProcess* this = (LinuxProcess*) cast; Process_done((Process*)cast); - free(this->container_short); - free(this->cgroup_short); - free(this->cgroup); - free(this->secattr); + RefString_unref(this->container_short); + this->container_short = NULL; + RefString_unref(this->cgroup_short); + this->cgroup_short = NULL; + RefString_unref(this->cgroup); + this->cgroup = NULL; + RefString_unref(this->secattr); + this->secattr = NULL; free(this); } @@ -317,7 +321,7 @@ static void LinuxProcess_rowWriteField(const Row* super, RichString* str, Proces if (lp->ctxt_diff > 1000) { attr |= A_BOLD; } - xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff); + xSnprintf(buffer, n, "%5u ", lp->ctxt_diff); break; case SECATTR: snprintf(buffer, n, "%-*.*s ", Row_fieldWidths[SECATTR], Row_fieldWidths[SECATTR], lp->secattr ? lp->secattr : "N/A"); diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 0a25bc70be..13e4d8c1a7 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -32,7 +32,7 @@ in the source distribution for its full text. typedef struct LinuxProcess_ { Process super; - IOPriority ioPriority; + unsigned long int cminflt; unsigned long int cmajflt; unsigned long long int utime; @@ -49,9 +49,6 @@ typedef struct LinuxProcess_ { long m_drs; long m_lrs; - /* Process flags */ - unsigned long int flags; - /* Data read (in bytes) */ unsigned long long io_rchar; @@ -85,31 +82,40 @@ typedef struct LinuxProcess_ { char* cgroup; char* cgroup_short; char* container_short; - unsigned int oom; + #ifdef HAVE_DELAYACCT unsigned long long int delay_read_time; unsigned long long cpu_delay_total; unsigned long long blkio_delay_total; unsigned long long swapin_delay_total; - float cpu_delay_percent; - float blkio_delay_percent; - float swapin_delay_percent; #endif + unsigned long ctxt_total; - unsigned long ctxt_diff; char* secattr; unsigned long long int last_mlrs_calctime; /* Total GPU time used in nano seconds */ unsigned long long int gpu_time; - /* GPU utilization in percent */ - float gpu_percent; /* Activity of GPU: 0 if active, otherwise time of last scan in milliseconds */ uint64_t gpu_activityMs; /* Autogroup scheduling (CFS) information */ long int autogroup_id; + + IOPriority ioPriority; + unsigned int oom; + + /* GPU utilization in percent */ + float gpu_percent; + int autogroup_nice; + uint32_t ctxt_diff; + + #ifdef HAVE_DELAYACCT + float cpu_delay_percent; + float blkio_delay_percent; + float swapin_delay_percent; + #endif } LinuxProcess; extern int pageSize; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index fc43baac30..634b64c508 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -129,12 +129,6 @@ static int fast_strtoi_dec(char** str, size_t maxlen) { return (int)result; } -static long fast_strtol_dec(char** str, size_t maxlen) { - long long result = fast_strtoll_dec(str, maxlen); - assert(result <= LONG_MAX); - assert(result >= LONG_MIN); - return (long)result; -} static unsigned long fast_strtoul_dec(char** str, size_t maxlen) { unsigned long long result = fast_strtoull_dec(str, maxlen); @@ -360,7 +354,7 @@ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd location = end + 2; /* (3) state - %c */ - process->state = LinuxProcessTable_getProcessState(location[0]); + process->state = (uint8_t)LinuxProcessTable_getProcessState(location[0]); if (!location[0] || !location[1]) return false; @@ -408,7 +402,9 @@ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd location += 1; /* (9) flags - %u */ - lp->flags = fast_strtoul_dec(&location, 0); + unsigned long flags = fast_strtoul_dec(&location, 0); + if (flags & PF_KTHREAD) + process->isKernelThread = true; if (!location[0]) return false; @@ -480,7 +476,7 @@ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd location += 1; /* (18) priority - %ld */ - process->priority = fast_strtol_dec(&location, 0); + process->priority = fast_strtoi_dec(&location, 0); if (!location[0]) return false; @@ -496,7 +492,7 @@ static bool LinuxProcessTable_readStatFile(LinuxProcess* lp, openat_arg_t procFd location += 1; /* (20) num_threads - %ld */ - process->nlwp = fast_strtol_dec(&location, 0); + process->nlwp = fast_strtoi_dec(&location, 0); if (!location[0]) return false; @@ -596,7 +592,8 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc fclose(statusfile); - lp->ctxt_diff = (ctxt > lp->ctxt_total) ? (ctxt - lp->ctxt_total) : 0; + uint64_t diff = (ctxt > lp->ctxt_total) ? (ctxt - lp->ctxt_total) : 0; + lp->ctxt_diff = (diff > UINT32_MAX) ? UINT32_MAX : (uint32_t)diff; lp->ctxt_total = ctxt; return true; @@ -915,21 +912,19 @@ static bool LinuxProcessTable_readSmapsFile(LinuxProcess* process, openat_arg_t /* * Read /proc//cgroup (thread-specific data) */ -static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) { +static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { + if (mainTask) { + RefString_assign(&process->cgroup, mainTask->cgroup); + RefString_assign(&process->cgroup_short, mainTask->cgroup_short); + RefString_assign(&process->container_short, mainTask->container_short); + return; + } + FILE* file = fopenat(procFd, "cgroup", "r"); if (!file) { - if (process->cgroup) { - free(process->cgroup); - process->cgroup = NULL; - } - if (process->cgroup_short) { - free(process->cgroup_short); - process->cgroup_short = NULL; - } - if (process->container_short) { - free(process->container_short); - process->container_short = NULL; - } + RefString_assign(&process->cgroup, NULL); + RefString_assign(&process->cgroup_short, NULL); + RefString_assign(&process->container_short, NULL); return; } char output[PROC_LINE_LENGTH + 1]; @@ -974,7 +969,9 @@ static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t bool changed = !process->cgroup || !String_eq(process->cgroup, output); Row_updateFieldWidth(CGROUP, strlen(output)); - free_and_xStrdup(&process->cgroup, output); + if (changed) { + RefString_assignNew(&process->cgroup, output); + } if (!changed) { if (process->cgroup_short) { @@ -994,25 +991,23 @@ static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t char* cgroup_short = CGroup_filterName(process->cgroup); if (cgroup_short) { Row_updateFieldWidth(CCGROUP, strlen(cgroup_short)); - free_and_xStrdup(&process->cgroup_short, cgroup_short); + RefString_assignNew(&process->cgroup_short, cgroup_short); free(cgroup_short); } else { //CCGROUP is alias to normal CGROUP if shortening fails Row_updateFieldWidth(CCGROUP, strlen(process->cgroup)); - free(process->cgroup_short); - process->cgroup_short = NULL; + RefString_assign(&process->cgroup_short, NULL); } char* container_short = CGroup_filterContainer(process->cgroup); if (container_short) { Row_updateFieldWidth(CONTAINER, strlen(container_short)); - free_and_xStrdup(&process->container_short, container_short); + RefString_assignNew(&process->container_short, container_short); free(container_short); } else { //CONTAINER is just "N/A" if shortening fails Row_updateFieldWidth(CONTAINER, strlen("N/A")); - free(process->container_short); - process->container_short = NULL; + RefString_assign(&process->container_short, NULL); } } @@ -1076,13 +1071,7 @@ static void LinuxProcessTable_readAutogroup(LinuxProcess* process, openat_arg_t */ static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { if (mainTask) { - const char* mainSecAttr = mainTask->secattr; - if (mainSecAttr) { - free_and_xStrdup(&process->secattr, mainSecAttr); - } else { - free(process->secattr); - process->secattr = NULL; - } + RefString_assign(&process->secattr, mainTask->secattr); return; } @@ -1090,7 +1079,7 @@ static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_ ssize_t attrdata = Compat_readfileat(procFd, "attr/current", buffer, sizeof(buffer)); if (attrdata < 1) { - free(process->secattr); + RefString_unref(process->secattr); process->secattr = NULL; return; } @@ -1102,7 +1091,7 @@ static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_ Row_updateFieldWidth(SECATTR, strlen(buffer)); - free_and_xStrdup(&process->secattr, buffer); + RefString_assignNew(&process->secattr, buffer); } /* @@ -1110,13 +1099,7 @@ static void LinuxProcessTable_readSecattrData(LinuxProcess* process, openat_arg_ */ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd, const LinuxProcess* mainTask) { if (mainTask) { - const char* mainCwd = mainTask->super.procCwd; - if (mainCwd) { - free_and_xStrdup(&process->super.procCwd, mainCwd); - } else { - free(process->super.procCwd); - process->super.procCwd = NULL; - } + Process_updateCwdShared(&process->super, mainTask->super.procCwd); return; } @@ -1129,14 +1112,13 @@ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd #endif if (r < 0) { - free(process->super.procCwd); - process->super.procCwd = NULL; + Process_updateCwd(&process->super, NULL); return; } pathBuffer[r] = '\0'; - free_and_xStrdup(&process->super.procCwd, pathBuffer); + Process_updateCwd(&process->super, pathBuffer); } /* @@ -1144,7 +1126,7 @@ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd */ static void LinuxProcessList_readExe(Process* process, openat_arg_t procFd, const LinuxProcess* mainTask) { if (mainTask) { - Process_updateExe(process, mainTask->super.procExe); + Process_updateExeShared(process, mainTask->super.procExe, mainTask->super.procExeBasenameOffset); process->procExeDeleted = mainTask->super.procExeDeleted; return; } @@ -1187,22 +1169,28 @@ static void LinuxProcessList_readExe(Process* process, openat_arg_t procFd, cons } /* - * Helper function to read a file with dynamic buffer allocation. - * Returns the buffer and sets *amtRead to bytes read. Caller must free the buffer in the success case. + * Helper function to read /proc//cmdline. + * Uses the caller's stackBuf if the contents fit, or allocates dynamically + * if the contents exceed stackBufSize. + * Returns the buffer and sets *amtRead to bytes read. Caller must free if returned buffer != stackBuf. * Returns NULL on error. */ -static char* readFileDynamic(openat_arg_t procFd, const char* filename, ssize_t* amtRead) { - size_t bufferSize = 512; - char* buffer = xMalloc(bufferSize); +static char* readCmdlineFileBuffer(openat_arg_t procFd, ssize_t* amtRead, char* stackBuf, size_t stackBufSize) { + *amtRead = Compat_readfileat(procFd, "cmdline", stackBuf, stackBufSize); + if (*amtRead <= 0) + return NULL; - *amtRead = Compat_readfileat(procFd, filename, buffer, bufferSize); + if ((size_t)*amtRead < stackBufSize - 1) + return stackBuf; + + size_t bufferSize = stackBufSize * 2; + char* buffer = xMalloc(bufferSize); + *amtRead = Compat_readfileat(procFd, "cmdline", buffer, bufferSize); - // If buffer was full, the file might be larger, so retry with a bigger buffer - // Limit to MAX_CMDLINE_BUFFER_SIZE to prevent excessive memory allocation while (*amtRead > 0 && (size_t)*amtRead == bufferSize - 1 && bufferSize < MAX_CMDLINE_BUFFER_SIZE) { bufferSize *= 2; buffer = xRealloc(buffer, bufferSize); - *amtRead = Compat_readfileat(procFd, filename, buffer, bufferSize); + *amtRead = Compat_readfileat(procFd, "cmdline", buffer, bufferSize); } if (*amtRead <= 0) { @@ -1219,8 +1207,14 @@ static char* readFileDynamic(openat_arg_t procFd, const char* filename, ssize_t* static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd, const LinuxProcess* mainTask) { LinuxProcessList_readExe(process, procFd, mainTask); + if (mainTask) { + Process_updateCmdlineShared(process, mainTask->super.cmdline, mainTask->super.cmdlineBasenameStart, mainTask->super.cmdlineBasenameEnd); + return true; + } + + char stackBuf[1024]; ssize_t amtRead; - char* command = readFileDynamic(procFd, "cmdline", &amtRead); + char* command = readCmdlineFileBuffer(procFd, &amtRead, stackBuf, sizeof(stackBuf)); if (!command) return false; @@ -1386,7 +1380,8 @@ static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t pro Process_updateCmdline(process, command, tokenStart, tokenEnd); - free(command); + if (command != stackBuf) + free(command); return true; } @@ -1394,13 +1389,15 @@ static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t pro * Read /proc//comm (thread-specific data) */ static void LinuxProcessList_readComm(Process* process, openat_arg_t procFd) { - ssize_t amtRead; - char* command = readFileDynamic(procFd, "comm", &amtRead); + char buffer[32]; + ssize_t amtRead = Compat_readfileat(procFd, "comm", buffer, sizeof(buffer) - 1); - if (command) { - command[amtRead - 1] = '\0'; - Process_updateComm(process, command); - free(command); + if (amtRead > 0) { + if (buffer[amtRead - 1] == '\n') + buffer[amtRead - 1] = '\0'; + else + buffer[amtRead] = '\0'; + Process_updateComm(process, buffer); } else { Process_updateComm(process, NULL); } @@ -1553,13 +1550,6 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); assert(proc->isUserlandThread == (mainTask != NULL)); - if (!mainTask) { - // As the list of tasks/threads is presented as a flat view in procfs - // below each directories main entry, it makes no sense to - // look for further directories that will not be there. - LinuxProcessTable_recurseProcTree(this, procFd, lhost, "task", lp); - } - /* * These conditions will not trigger on first occurrence, cause we need to * add the process to the ProcessTable and do all one time scans @@ -1597,7 +1587,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar { bool prev = proc->usesDeletedLib; - if (!proc->isKernelThread && !proc->isUserlandThread && + if (!host->inBackground && !proc->isKernelThread && !proc->isUserlandThread && ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(proc, 10)))) { // Check if we really should recalculate the M_LRS value for this process @@ -1625,13 +1615,14 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar if (!LinuxProcessTable_readStatFile(lp, procFd, lhost, scanMainThread, statCommand, sizeof(statCommand))) goto errorReadingProcess; - if (lp->flags & PF_KTHREAD) { - proc->isKernelThread = true; - } - - if (last_tty_nr != proc->tty_nr && this->ttyDrivers) { - free(proc->tty_name); - proc->tty_name = LinuxProcessTable_updateTtyDevice(this->ttyDrivers, proc->tty_nr); + if (mainTask) { + if (last_tty_nr != proc->tty_nr) { + Process_updateTtyShared(proc, mainTask->super.tty_name); + } + } else if (last_tty_nr != proc->tty_nr && this->ttyDrivers) { + char* ttyDevice = LinuxProcessTable_updateTtyDevice(this->ttyDrivers, proc->tty_nr); + Process_updateTty(proc, ttyDevice); + free(ttyDevice); } proc->percent_cpu = NAN; @@ -1661,7 +1652,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } - if (ss->flags & PROCESS_FLAG_LINUX_CTXT + if ((!host->inBackground && (ss->flags & PROCESS_FLAG_LINUX_CTXT)) || ((hideRunningInContainer || ss->flags & PROCESS_FLAG_LINUX_CONTAINER) && proc->isRunningInContainer == TRI_INITIAL) ) { proc->isRunningInContainer = TRI_OFF; @@ -1684,7 +1675,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar ProcessTable_add(pt, proc); } else { - if (settings->updateProcessNames && proc->state != ZOMBIE) { + if (!host->inBackground && settings->updateProcessNames && proc->state != ZOMBIE) { if (proc->isKernelThread) { Process_updateCmdline(proc, NULL, 0, 0); } else { @@ -1702,7 +1693,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar */ /* Gather permitted capabilities (thread-specific data) for non-root process. */ - if (proc->st_uid != 0 && proc->elevated_priv != TRI_OFF) { + if (!host->inBackground && proc->st_uid != 0 && proc->elevated_priv != TRI_OFF) { struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = Process_getPid(proc) }; struct __user_cap_data_struct data; @@ -1714,10 +1705,10 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } - if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) - LinuxProcessTable_readCGroupFile(lp, procFd); + if ((!host->inBackground || !preExisting || !lp->cgroup) && (ss->flags & PROCESS_FLAG_LINUX_CGROUP)) + LinuxProcessTable_readCGroupFile(lp, procFd, mainTask); - if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { + if (!host->inBackground && (ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { if (!mainTask) { // Read smaps file of each process only every second pass to improve performance static int smaps_flag = 0; @@ -1735,47 +1726,49 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } } - if (ss->flags & PROCESS_FLAG_IO) { + if ((!host->inBackground || !preExisting) && (ss->flags & PROCESS_FLAG_IO)) { LinuxProcessTable_readIoFile(lp, procFd, scanMainThread); } - #ifdef HAVE_DELAYACCT - if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { - LibNl_readDelayAcctData(this, lp); - } - #endif + if (!host->inBackground) { + #ifdef HAVE_DELAYACCT + if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { + LibNl_readDelayAcctData(this, lp); + } + #endif - if (ss->flags & PROCESS_FLAG_LINUX_OOM) { - LinuxProcessTable_readOomData(lp, procFd, mainTask); - } + if (ss->flags & PROCESS_FLAG_LINUX_OOM) { + LinuxProcessTable_readOomData(lp, procFd, mainTask); + } - if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { - LinuxProcess_updateIOPriority(proc); - } + if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { + LinuxProcess_updateIOPriority(proc); + } - if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { - LinuxProcessTable_readSecattrData(lp, procFd, mainTask); - } + if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { + LinuxProcessTable_readSecattrData(lp, procFd, mainTask); + } - if (ss->flags & PROCESS_FLAG_CWD) { - LinuxProcessTable_readCwd(lp, procFd, mainTask); - } + if (ss->flags & PROCESS_FLAG_CWD) { + LinuxProcessTable_readCwd(lp, procFd, mainTask); + } - if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) { - LinuxProcessTable_readAutogroup(lp, procFd, mainTask); - } + if ((ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) && this->haveAutogroup) { + LinuxProcessTable_readAutogroup(lp, procFd, mainTask); + } - #ifdef SCHEDULER_SUPPORT - if (ss->flags & PROCESS_FLAG_SCHEDPOL) { - Scheduling_readProcessPolicy(proc); - } - #endif + #ifdef SCHEDULER_SUPPORT + if (ss->flags & PROCESS_FLAG_SCHEDPOL) { + Scheduling_readProcessPolicy(proc); + } + #endif - if (ss->flags & PROCESS_FLAG_LINUX_GPU || GPUMeter_active()) { - if (mainTask) { - lp->gpu_time = mainTask->gpu_time; - } else { - GPU_readProcessData(this, lp, procFd); + if (ss->flags & PROCESS_FLAG_LINUX_GPU || GPUMeter_active()) { + if (mainTask) { + lp->gpu_time = mainTask->gpu_time; + } else { + GPU_readProcessData(this, lp, procFd); + } } } @@ -1788,6 +1781,13 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); } + if (!mainTask) { + // As the list of tasks/threads is presented as a flat view in procfs + // below each directories main entry, it makes no sense to + // look for further directories that will not be there. + LinuxProcessTable_recurseProcTree(this, procFd, lhost, "task", lp); + } + proc->super.updated = true; Compat_openatArgClose(procFd); diff --git a/netbsd/NetBSDProcessTable.c b/netbsd/NetBSDProcessTable.c index 5fed6c3164..a96ec10cfd 100644 --- a/netbsd/NetBSDProcessTable.c +++ b/netbsd/NetBSDProcessTable.c @@ -75,20 +75,12 @@ static void NetBSDProcessTable_updateCwd(const struct kinfo_proc2* kproc, Proces const int mib[] = { CTL_KERN, KERN_PROC_ARGS, kproc->p_pid, KERN_PROC_CWD }; char buffer[2048]; size_t size = sizeof(buffer); - if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { - free(proc->procCwd); - proc->procCwd = NULL; + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0 || buffer[0] == '\0') { + Process_updateCwd(proc, NULL); return; } - /* Kernel threads return an empty buffer */ - if (buffer[0] == '\0') { - free(proc->procCwd); - proc->procCwd = NULL; - return; - } - - free_and_xStrdup(&proc->procCwd, buffer); + Process_updateCwd(proc, buffer); } static void NetBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc2* kproc, Process* proc) { @@ -203,12 +195,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { // KERN_PROC_TTY_NODEV is a negative constant but the type of // kproc->p_tdev may be unsigned. const char* name = ((dev_t)~kproc->p_tdev != (dev_t)~(KERN_PROC_TTY_NODEV)) ? devname(kproc->p_tdev, S_IFCHR) : NULL; - if (!name) { - free(proc->tty_name); - proc->tty_name = NULL; - } else { - free_and_xStrdup(&proc->tty_name, name); - } + Process_updateTty(proc, name); NetBSDProcessTable_updateExe(kproc, proc); NetBSDProcessTable_updateProcessName(nhost->kd, kproc, proc); @@ -246,7 +233,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */ switch (kproc->p_realstat) { case SIDL: proc->state = IDLE; break; - case SACTIVE: proc->state = get_active_status(nhost, kproc); break; + case SACTIVE: proc->state = (uint8_t) get_active_status(nhost, kproc); break; case SSTOP: proc->state = STOPPED; break; case SZOMB: proc->state = ZOMBIE; break; case SDEAD: proc->state = DEFUNCT; break; diff --git a/openbsd/OpenBSDProcessTable.c b/openbsd/OpenBSDProcessTable.c index 89ff21d46b..270d681515 100644 --- a/openbsd/OpenBSDProcessTable.c +++ b/openbsd/OpenBSDProcessTable.c @@ -55,20 +55,12 @@ static void OpenBSDProcessTable_updateCwd(const struct kinfo_proc* kproc, Proces const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->p_pid }; char buffer[2048]; size_t size = sizeof(buffer); - if (sysctl(mib, 3, buffer, &size, NULL, 0) != 0) { - free(proc->procCwd); - proc->procCwd = NULL; + if (sysctl(mib, 3, buffer, &size, NULL, 0) != 0 || buffer[0] == '\0') { + Process_updateCwd(proc, NULL); return; } - /* Kernel threads return an empty buffer */ - if (buffer[0] == '\0') { - free(proc->procCwd); - proc->procCwd = NULL; - return; - } - - free_and_xStrdup(&proc->procCwd, buffer); + Process_updateCwd(proc, buffer); } static void OpenBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc* kproc, Process* proc) { @@ -178,12 +170,10 @@ static void OpenBSDProcessTable_scanProcs(OpenBSDProcessTable* this) { proc->tty_nr = kproc->p_tdev; const char* name = ((dev_t)kproc->p_tdev != NODEV) ? devname(kproc->p_tdev, S_IFCHR) : NULL; - if (!name || String_eq(name, "??")) { - free(proc->tty_name); - proc->tty_name = NULL; - } else { - free_and_xStrdup(&proc->tty_name, name); + if (name && String_eq(name, "??")) { + name = NULL; } + Process_updateTty(proc, name); } else { if (settings->updateProcessNames) { OpenBSDProcessTable_updateProcessName(ohost->kd, kproc, proc); diff --git a/pcp/PCPProcessTable.c b/pcp/PCPProcessTable.c index c227dca08c..6bb2ff2986 100644 --- a/pcp/PCPProcessTable.c +++ b/pcp/PCPProcessTable.c @@ -133,7 +133,7 @@ static inline ProcessState PCPProcessTable_getProcessState(char state) { static void PCPProcessTable_updateID(Process* process, int pid, int offset) { Process_setThreadGroup(process, (pid_t) Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1)); Process_setParent(process, (pid_t) Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1)); - process->state = PCPProcessTable_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?')); + process->state = (uint8_t) PCPProcessTable_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?')); } static void PCPProcessTable_updateInfo(PCPProcess* pp, int pid, int offset, char* command, size_t commLen) { @@ -157,9 +157,9 @@ static void PCPProcessTable_updateInfo(PCPProcess* pp, int pid, int offset, char pp->stime = Metric_instance_time(PCP_PROC_STIME, pid, offset); pp->cutime = Metric_instance_time(PCP_PROC_CUTIME, pid, offset); pp->cstime = Metric_instance_time(PCP_PROC_CSTIME, pid, offset); - process->priority = Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0); + process->priority = (int) Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0); process->nice = (int) Metric_instance_s32(PCP_PROC_NICE, pid, offset, 0); - process->nlwp = Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0); + process->nlwp = (int) Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0); process->starttime_ctime = Metric_instance_time(PCP_PROC_STARTTIME, pid, offset); process->processor = (int) Metric_instance_u32(PCP_PROC_PROCESSOR, pid, offset, 0); @@ -251,7 +251,11 @@ static char* setString(Metric metric, int pid, int offset, char* string) { } static void PCPProcessTable_updateTTY(Process* process, int pid, int offset) { - process->tty_name = setString(PCP_PROC_TTYNAME, pid, offset, process->tty_name); + pmAtomValue value; + if (Metric_instance(PCP_PROC_TTYNAME, pid, offset, &value, PM_TYPE_STRING)) + Process_updateTty(process, value.cp); + else + Process_updateTty(process, NULL); } static void PCPProcessTable_readCGroups(PCPProcess* pp, int pid, int offset) { @@ -294,7 +298,11 @@ static void PCPProcessTable_readSecattrData(PCPProcess* pp, int pid, int offset) } static void PCPProcessTable_readCwd(PCPProcess* pp, int pid, int offset) { - pp->super.procCwd = setString(PCP_PROC_CWD, pid, offset, pp->super.procCwd); + pmAtomValue value; + if (Metric_instance(PCP_PROC_CWD, pid, offset, &value, PM_TYPE_STRING)) + Process_updateCwd(&pp->super, value.cp); + else + Process_updateCwd(&pp->super, NULL); } static void PCPProcessTable_updateUsername(Process* process, int pid, int offset, UsersTable* users) { diff --git a/solaris/SolarisProcessTable.c b/solaris/SolarisProcessTable.c index 29c5d938f9..effe2e8e91 100644 --- a/solaris/SolarisProcessTable.c +++ b/solaris/SolarisProcessTable.c @@ -81,11 +81,13 @@ static void SolarisProcessTable_updateCwd(pid_t pid, Process* proc) { char target[PATH_MAX]; ssize_t ret = readlink(path, target, sizeof(target) - 1); - if (ret <= 0) + if (ret <= 0) { + Process_updateCwd(proc, NULL); return; + } target[ret] = '\0'; - free_and_xStrdup(&proc->procCwd, target); + Process_updateCwd(proc, target); } /* Taken from: https://docs.oracle.com/cd/E19253-01/817-6223/6mlkidlom/index.html#tbl-sched-state */ @@ -142,7 +144,7 @@ static int SolarisProcessTable_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 = SolarisProcessTable_getProcessState(_lwpsinfo->pr_sname); + proc->state = (uint8_t) SolarisProcessTable_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) @@ -153,12 +155,7 @@ static int SolarisProcessTable_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo proc->tty_nr = _psinfo->pr_ttydev; const char* name = (_psinfo->pr_ttydev != PRNODEV) ? ttyname(_psinfo->pr_ttydev) : NULL; - if (!name) { - free(proc->tty_name); - proc->tty_name = NULL; - } else { - free_and_xStrdup(&proc->tty_name, name); - } + Process_updateTty(proc, name); proc->m_resident = _psinfo->pr_rssize; // KB proc->m_virt = _psinfo->pr_size; // KB diff --git a/unsupported/UnsupportedProcessTable.c b/unsupported/UnsupportedProcessTable.c index dfb2acda5f..3985ec4fd4 100644 --- a/unsupported/UnsupportedProcessTable.c +++ b/unsupported/UnsupportedProcessTable.c @@ -50,7 +50,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { const Settings* settings = super->super.host->settings; if (settings->ss->flags & PROCESS_FLAG_CWD) { - free_and_xStrdup(&proc->procCwd, "/current/working/directory"); + Process_updateCwd(proc, "/current/working/directory"); } proc->super.updated = true;