2006-03-04 18:16:49 +00:00
|
|
|
/*
|
|
|
|
htop - Process.c
|
2015-03-21 19:52:54 +00:00
|
|
|
(C) 2004-2015 Hisham H. Muhammad
|
2020-08-19 23:35:24 +00:00
|
|
|
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
2021-09-22 09:33:00 +00:00
|
|
|
Released under the GNU GPLv2+, see the COPYING file
|
2006-03-04 18:16:49 +00:00
|
|
|
in the source distribution for its full text.
|
|
|
|
*/
|
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2011-12-26 21:35:57 +00:00
|
|
|
#include "Process.h"
|
2015-03-17 02:01:48 +00:00
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2021-04-29 15:12:43 +00:00
|
|
|
#include <string.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/resource.h>
|
2018-12-30 04:18:27 +00:00
|
|
|
|
2006-03-04 18:16:49 +00:00
|
|
|
#include "CRT.h"
|
2020-11-18 13:26:30 +00:00
|
|
|
#include "Macros.h"
|
2014-11-27 22:10:23 +00:00
|
|
|
#include "Platform.h"
|
2020-11-18 13:26:30 +00:00
|
|
|
#include "ProcessList.h"
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
#include "DynamicColumn.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "RichString.h"
|
|
|
|
#include "Settings.h"
|
2020-10-14 18:21:09 +00:00
|
|
|
#include "XUtils.h"
|
2006-03-04 18:16:49 +00:00
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#if defined(MAJOR_IN_MKDEV)
|
2018-02-26 13:15:05 +00:00
|
|
|
#include <sys/mkdev.h>
|
|
|
|
#endif
|
2006-03-04 18:16:49 +00:00
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
|
2021-04-18 17:25:56 +00:00
|
|
|
/* Used to identify kernel threads in Comm and Exe columns */
|
2021-07-14 17:24:18 +00:00
|
|
|
static const char* const kthreadID = "KTHREAD";
|
2021-04-18 17:25:56 +00:00
|
|
|
|
2020-11-04 16:46:04 +00:00
|
|
|
static uid_t Process_getuid = (uid_t)-1;
|
2006-07-12 01:35:59 +00:00
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
int Process_pidDigits = PROCESS_MIN_PID_DIGITS;
|
|
|
|
int Process_uidDigits = PROCESS_MIN_UID_DIGITS;
|
2015-08-20 03:32:47 +00:00
|
|
|
|
|
|
|
void Process_setupColumnWidths() {
|
|
|
|
int maxPid = Platform_getMaxPid();
|
2020-11-01 00:09:51 +00:00
|
|
|
if (maxPid == -1)
|
|
|
|
return;
|
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
if (maxPid < (int)pow(10, PROCESS_MIN_PID_DIGITS)) {
|
|
|
|
Process_pidDigits = PROCESS_MIN_PID_DIGITS;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-15 18:44:52 +00:00
|
|
|
Process_pidDigits = ceil(log10(maxPid));
|
|
|
|
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
|
2015-08-20 03:32:47 +00:00
|
|
|
}
|
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
void Process_setUidColumnWidth(uid_t maxUid) {
|
|
|
|
if (maxUid < (uid_t)pow(10, PROCESS_MIN_UID_DIGITS)) {
|
|
|
|
Process_uidDigits = PROCESS_MIN_UID_DIGITS;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Process_uidDigits = ceil(log10(maxUid));
|
|
|
|
assert(Process_uidDigits <= PROCESS_MAX_UID_DIGITS);
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:16:16 +00:00
|
|
|
void Process_printBytes(RichString* str, unsigned long long number, bool coloring) {
|
|
|
|
char buffer[16];
|
2006-03-04 18:16:49 +00:00
|
|
|
int len;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-01-27 14:11:48 +00:00
|
|
|
int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
|
|
|
|
int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
|
|
|
|
int processGigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS];
|
|
|
|
int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
|
2014-04-24 18:00:09 +00:00
|
|
|
int processColor = CRT_colors[PROCESS];
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-01-27 14:11:48 +00:00
|
|
|
if (number == ULLONG_MAX) {
|
|
|
|
//Invalid number
|
|
|
|
RichString_appendAscii(str, shadowColor, " N/A ");
|
2021-04-14 18:16:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
number /= ONE_K;
|
|
|
|
|
|
|
|
if (number < 1000) {
|
2020-09-13 21:50:24 +00:00
|
|
|
//Plain number, no markings
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
} else if (number < 100000) {
|
|
|
|
//2 digit MB, 3 digit KB
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 1000);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
2006-03-04 18:16:49 +00:00
|
|
|
number %= 1000;
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
} else if (number < 1000 * ONE_K) {
|
|
|
|
//3 digit MB
|
|
|
|
number /= ONE_K;
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
} else if (number < 10000 * ONE_K) {
|
|
|
|
//1 digit GB, 3 digit MB
|
|
|
|
number /= ONE_K;
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
number %= 1000;
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
2020-09-20 17:54:53 +00:00
|
|
|
} else if (number < 100000 * ONE_K) {
|
2020-09-13 21:50:24 +00:00
|
|
|
//2 digit GB, 1 digit MB
|
|
|
|
number /= 100 * ONE_K;
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
number %= 10;
|
2020-11-29 13:14:46 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
|
|
|
RichString_appendAscii(str, processGigabytesColor, "G ");
|
2020-09-13 21:50:24 +00:00
|
|
|
} else if (number < 1000 * ONE_M) {
|
|
|
|
//3 digit GB
|
|
|
|
number /= ONE_M;
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
Process.{h,c}: Use integer types that are more portable
When building on a 32-bit system, the compiler warned that the
following line uses a constant whose value is the overflow result
of a compile-time computation:
Process.c (line 109): } else if (number < 10000 * ONE_M) {
Namely, this constant expression:
10000 * ONE_M
was intended to produce the following value:
10485760000
However, the result overflowed to produce:
1895825408
The reason for this overflow is as follows:
o The macros are expanded:
10000 * (ONE_K * ONE_K)
10000 * (1024L * 1024L)
o The untyped constant expression "10000" is typed:
10000U * (1024L * 1024L)
o The parenthesized expression is evaluated:
10000U * (1048576L)
o The left operand ("10000U") is converted:
10000L * (1048576L)
Unbound by integer sizes, that last multiplication
would produce the following value:
10485760000
However, on a 32-bit machine, where a long is 32 bits
(really 31 bits when talking about positive numbers),
the maximum value that can be computed is 2**31-1:
2147483647
Consequently, the computation overflows.
o The compiler produces a long int value that is the
the result of overflow (10485760000 % 2**31):
1895825408L
Actually, I think this overflow is implementation-defined,
so it's not even a portable description of what happens.
The solution is to use a long long int (or, even better,
an unsigned long long int) type for the constant expression;
the C standard mandates a sufficiently large maximum value
for such types.
Hence, the following change is made to the bad line:
- } else if (number < 10000 * ONE_M) {
+ } else if (number < 10000ULL * ONE_M) {
However, the whole line is now patently silly, because the
variable "number" is typed "unsigned long", and so it will
always be less than the constant expression (the compiler
will warn about this, too).
Hence, "number" must be typed "unsigned long long"; however,
this necessitates changing all of the string formats from
something like "%lu" to something like "%llu".
Et voila! This commit is born.
Then, for the sake of completeness, the declared types of the
constant-expression macros are updated:
o ONE_K is made unsigned (a "UL" instead of "L")
o ONE_T is computed by introducing "1ULL *"
o Similar changes are made for ONE_DECIMAL_{K,T}
Also, a non-portable overflow-conversion to a signed value
has been replaced with a portable comparison:
- if ((long long) number == -1LL) {
+ if (number == ULLONG_MAX) {
It might be worth reviewing the rest of the code for other
cases where overflows are not handled correctly; even at
runtime, it's often necessary to check for overflow unless
such behavior is expected (especially for signed integer
values, for which overflow has implementation-defined
behavior).
2020-09-29 14:04:22 +00:00
|
|
|
} else if (number < 10000ULL * ONE_M) {
|
2020-09-13 21:50:24 +00:00
|
|
|
//1 digit TB, 3 digit GB
|
|
|
|
number /= ONE_M;
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
number %= 1000;
|
2020-11-28 16:57:51 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
2021-04-14 18:16:16 +00:00
|
|
|
} else if (number < 100000 * ONE_M) {
|
|
|
|
//2 digit TB, 1 digit GB
|
|
|
|
number /= 100 * ONE_M;
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10);
|
2021-04-14 18:16:16 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
|
|
|
number %= 10;
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
|
|
|
|
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
|
|
|
|
RichString_appendAscii(str, largeNumberColor, "T ");
|
|
|
|
} else if (number < 10000ULL * ONE_G) {
|
|
|
|
//3 digit TB or 1 digit PB, 3 digit TB
|
|
|
|
number /= ONE_G;
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4lluT ", number);
|
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
2020-09-13 21:50:24 +00:00
|
|
|
} else {
|
2021-04-14 18:16:16 +00:00
|
|
|
//2 digit PB and above
|
2021-07-14 17:18:27 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfP ", (double)number / ONE_T);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:16:16 +00:00
|
|
|
void Process_printKBytes(RichString* str, unsigned long long number, bool coloring) {
|
|
|
|
if (number == ULLONG_MAX)
|
|
|
|
Process_printBytes(str, ULLONG_MAX, coloring);
|
|
|
|
else
|
|
|
|
Process_printBytes(str, number * ONE_K, coloring);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process_printCount(RichString* str, unsigned long long number, bool coloring) {
|
2020-11-28 16:57:51 +00:00
|
|
|
char buffer[13];
|
2014-04-24 18:00:09 +00:00
|
|
|
|
2021-01-27 14:11:46 +00:00
|
|
|
int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
|
|
|
|
int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
|
2014-04-24 18:00:09 +00:00
|
|
|
int processColor = CRT_colors[PROCESS];
|
2021-01-27 14:11:46 +00:00
|
|
|
int processShadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
|
2014-04-24 18:00:09 +00:00
|
|
|
|
Process.{h,c}: Use integer types that are more portable
When building on a 32-bit system, the compiler warned that the
following line uses a constant whose value is the overflow result
of a compile-time computation:
Process.c (line 109): } else if (number < 10000 * ONE_M) {
Namely, this constant expression:
10000 * ONE_M
was intended to produce the following value:
10485760000
However, the result overflowed to produce:
1895825408
The reason for this overflow is as follows:
o The macros are expanded:
10000 * (ONE_K * ONE_K)
10000 * (1024L * 1024L)
o The untyped constant expression "10000" is typed:
10000U * (1024L * 1024L)
o The parenthesized expression is evaluated:
10000U * (1048576L)
o The left operand ("10000U") is converted:
10000L * (1048576L)
Unbound by integer sizes, that last multiplication
would produce the following value:
10485760000
However, on a 32-bit machine, where a long is 32 bits
(really 31 bits when talking about positive numbers),
the maximum value that can be computed is 2**31-1:
2147483647
Consequently, the computation overflows.
o The compiler produces a long int value that is the
the result of overflow (10485760000 % 2**31):
1895825408L
Actually, I think this overflow is implementation-defined,
so it's not even a portable description of what happens.
The solution is to use a long long int (or, even better,
an unsigned long long int) type for the constant expression;
the C standard mandates a sufficiently large maximum value
for such types.
Hence, the following change is made to the bad line:
- } else if (number < 10000 * ONE_M) {
+ } else if (number < 10000ULL * ONE_M) {
However, the whole line is now patently silly, because the
variable "number" is typed "unsigned long", and so it will
always be less than the constant expression (the compiler
will warn about this, too).
Hence, "number" must be typed "unsigned long long"; however,
this necessitates changing all of the string formats from
something like "%lu" to something like "%llu".
Et voila! This commit is born.
Then, for the sake of completeness, the declared types of the
constant-expression macros are updated:
o ONE_K is made unsigned (a "UL" instead of "L")
o ONE_T is computed by introducing "1ULL *"
o Similar changes are made for ONE_DECIMAL_{K,T}
Also, a non-portable overflow-conversion to a signed value
has been replaced with a portable comparison:
- if ((long long) number == -1LL) {
+ if (number == ULLONG_MAX) {
It might be worth reviewing the rest of the code for other
cases where overflows are not handled correctly; even at
runtime, it's often necessary to check for overflow unless
such behavior is expected (especially for signed integer
values, for which overflow has implementation-defined
behavior).
2020-09-29 14:04:22 +00:00
|
|
|
if (number == ULLONG_MAX) {
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (number >= 100000LL * ONE_DECIMAL_T) {
|
2020-11-28 16:42:02 +00:00
|
|
|
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, 12);
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (number >= 100LL * ONE_DECIMAL_T) {
|
2020-11-28 16:42:02 +00:00
|
|
|
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, 8);
|
2021-07-14 17:18:27 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer + 8, 4);
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (number >= 10LL * ONE_DECIMAL_G) {
|
2020-11-28 16:42:02 +00:00
|
|
|
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, 5);
|
2021-07-14 17:18:27 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer + 5, 3);
|
|
|
|
RichString_appendnAscii(str, processColor, buffer + 8, 4);
|
2011-05-26 16:31:18 +00:00
|
|
|
} else {
|
2020-11-28 16:42:02 +00:00
|
|
|
xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, 2);
|
2021-07-14 17:18:27 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer + 2, 3);
|
|
|
|
RichString_appendnAscii(str, processColor, buffer + 5, 3);
|
|
|
|
RichString_appendnAscii(str, processShadowColor, buffer + 8, 4);
|
2011-05-26 16:31:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:16:16 +00:00
|
|
|
void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring) {
|
|
|
|
char buffer[10];
|
|
|
|
int len;
|
2006-03-04 18:16:49 +00:00
|
|
|
|
2021-04-14 18:16:16 +00:00
|
|
|
unsigned long long totalSeconds = totalHundredths / 100;
|
2015-03-17 02:01:48 +00:00
|
|
|
unsigned long long hours = totalSeconds / 3600;
|
2021-04-10 12:02:59 +00:00
|
|
|
unsigned long long days = totalSeconds / 86400;
|
2015-03-17 02:01:48 +00:00
|
|
|
int minutes = (totalSeconds / 60) % 60;
|
|
|
|
int seconds = totalSeconds % 60;
|
|
|
|
int hundredths = totalHundredths - (totalSeconds * 100);
|
2021-04-14 18:16:16 +00:00
|
|
|
|
|
|
|
int yearColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
|
|
|
|
int dayColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS];
|
|
|
|
int hourColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
|
|
|
|
int defColor = CRT_colors[PROCESS];
|
|
|
|
|
|
|
|
if (days >= /* Ignore leapyears */365) {
|
|
|
|
int years = days / 365;
|
|
|
|
int daysLeft = days - 365 * years;
|
|
|
|
|
|
|
|
if (daysLeft >= 100) {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%3dy", years);
|
|
|
|
RichString_appendnAscii(str, yearColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft);
|
|
|
|
RichString_appendnAscii(str, dayColor, buffer, len);
|
|
|
|
} else if (daysLeft >= 10) {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4dy", years);
|
|
|
|
RichString_appendnAscii(str, yearColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2dd ", daysLeft);
|
|
|
|
RichString_appendnAscii(str, dayColor, buffer, len);
|
|
|
|
} else {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%5dy", years);
|
|
|
|
RichString_appendnAscii(str, yearColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%1dd ", daysLeft);
|
|
|
|
RichString_appendnAscii(str, dayColor, buffer, len);
|
|
|
|
}
|
|
|
|
} else if (days >= 100) {
|
|
|
|
int hoursLeft = hours - days * 24;
|
|
|
|
|
|
|
|
if (hoursLeft >= 10) {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4llud", days);
|
|
|
|
RichString_appendnAscii(str, dayColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2dh ", hoursLeft);
|
|
|
|
RichString_appendnAscii(str, hourColor, buffer, len);
|
|
|
|
} else {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%5llud", days);
|
|
|
|
RichString_appendnAscii(str, dayColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%1dh ", hoursLeft);
|
|
|
|
RichString_appendnAscii(str, hourColor, buffer, len);
|
|
|
|
}
|
2021-04-10 12:02:59 +00:00
|
|
|
} else if (hours >= 100) {
|
2021-04-14 18:16:16 +00:00
|
|
|
int minutesLeft = totalSeconds / 60 - hours * 60;
|
|
|
|
|
|
|
|
if (minutesLeft >= 10) {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%4lluh", hours);
|
|
|
|
RichString_appendnAscii(str, hourColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2dm ", minutesLeft);
|
|
|
|
RichString_appendnAscii(str, defColor, buffer, len);
|
2011-12-14 23:29:07 +00:00
|
|
|
} else {
|
2021-04-14 18:16:16 +00:00
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%5lluh", hours);
|
|
|
|
RichString_appendnAscii(str, hourColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%1dm ", minutesLeft);
|
|
|
|
RichString_appendnAscii(str, defColor, buffer, len);
|
2011-12-14 23:29:07 +00:00
|
|
|
}
|
2021-04-14 18:16:16 +00:00
|
|
|
} else if (hours > 0) {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
|
|
|
|
RichString_appendnAscii(str, hourColor, buffer, len);
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
|
|
|
|
RichString_appendnAscii(str, defColor, buffer, len);
|
|
|
|
} else {
|
|
|
|
len = xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
|
|
|
|
RichString_appendnAscii(str, defColor, buffer, len);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 12:26:40 +00:00
|
|
|
void Process_fillStarttimeBuffer(Process* this) {
|
|
|
|
struct tm date;
|
|
|
|
(void) localtime_r(&this->starttime_ctime, &date);
|
|
|
|
strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date);
|
|
|
|
}
|
|
|
|
|
2021-04-10 11:31:39 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
static bool findCommInCmdline(const char* comm, const char* cmdline, int cmdlineBasenameStart, int* pCommStart, int* pCommEnd) {
|
2021-04-10 11:31:39 +00:00
|
|
|
/* 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 */
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* tokenBase;
|
2021-04-10 11:31:39 +00:00
|
|
|
size_t tokenLen;
|
|
|
|
const size_t commLen = strlen(comm);
|
|
|
|
|
|
|
|
if (cmdlineBasenameStart < 0)
|
|
|
|
return false;
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
for (const char* token = cmdline + cmdlineBasenameStart; *token;) {
|
2021-04-10 11:31:39 +00:00
|
|
|
for (tokenBase = token; *token && *token != '\n'; ++token) {
|
|
|
|
if (*token == '/') {
|
|
|
|
tokenBase = token + 1;
|
2014-02-27 20:11:23 +00:00
|
|
|
}
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2021-04-10 11:31:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
static int matchCmdlinePrefixWithExeSuffix(const char* cmdline, int cmdlineBaseOffset, const char* exe, int exeBaseOffset, int exeBaseLen) {
|
2021-04-10 11:31:39 +00:00
|
|
|
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 */
|
2021-07-14 17:24:18 +00:00
|
|
|
static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr) {
|
2021-04-10 11:31:39 +00:00
|
|
|
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.
|
|
|
|
*/
|
2021-07-14 17:24:18 +00:00
|
|
|
void Process_makeCommandStr(Process* this) {
|
|
|
|
ProcessMergedCommand* mc = &this->mergedCommand;
|
|
|
|
const Settings* settings = this->settings;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
bool showMergedCommand = settings->showMergedCommand;
|
|
|
|
bool showProgramPath = settings->showProgramPath;
|
|
|
|
bool searchCommInCmdline = settings->findCommInCmdline;
|
|
|
|
bool stripExeFromCmdline = settings->stripExeFromCmdline;
|
2021-07-17 18:59:50 +00:00
|
|
|
bool showThreadNames = settings->showThreadNames;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
2021-04-18 16:10:04 +00:00
|
|
|
/* Nothing to do to (Re)Generate the Command string, if the process is:
|
|
|
|
* - a kernel thread, or
|
|
|
|
* - a zombie from before being under htop's watch, or
|
|
|
|
* - a user thread and showThreadNames is not set */
|
|
|
|
if (Process_isKernelThread(this))
|
|
|
|
return;
|
2021-10-11 22:45:09 +00:00
|
|
|
if (this->state == ZOMBIE && !this->mergedCommand.str)
|
2021-04-18 16:10:04 +00:00
|
|
|
return;
|
2021-08-08 14:04:26 +00:00
|
|
|
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames))
|
2021-04-18 16:10:04 +00:00
|
|
|
return;
|
|
|
|
|
2021-04-10 11:31:39 +00:00
|
|
|
/* this->mergedCommand.str needs updating only if its state or contents changed.
|
|
|
|
* Its content is based on the fields cmdline, comm, and exe. */
|
|
|
|
if (
|
2021-07-14 17:15:09 +00:00
|
|
|
mc->prevMergeSet == showMergedCommand &&
|
|
|
|
mc->prevPathSet == showProgramPath &&
|
|
|
|
mc->prevCommSet == searchCommInCmdline &&
|
|
|
|
mc->prevCmdlineSet == stripExeFromCmdline &&
|
2021-07-17 18:59:50 +00:00
|
|
|
mc->prevShowThreadNames == showThreadNames &&
|
2021-07-14 17:15:09 +00:00
|
|
|
!mc->cmdlineChanged &&
|
|
|
|
!mc->commChanged &&
|
|
|
|
!mc->exeChanged
|
2021-04-10 11:31:39 +00:00
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The field separtor "│" has been chosen such that it will not match any
|
|
|
|
* valid string used for searching or filtering */
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
|
2021-04-10 11:31:39 +00:00
|
|
|
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 */
|
2021-05-17 21:15:24 +00:00
|
|
|
size_t maxLen = 2 * SEPARATOR_LEN + 1;
|
|
|
|
maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
|
|
|
|
maxLen += this->procComm ? strlen(this->procComm) : 0;
|
|
|
|
maxLen += this->procExe ? strlen(this->procExe) : 0;
|
|
|
|
|
|
|
|
mc->str = xCalloc(1, maxLen);
|
2021-04-10 11:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Preserve the settings used in this run */
|
|
|
|
mc->prevMergeSet = showMergedCommand;
|
|
|
|
mc->prevPathSet = showProgramPath;
|
|
|
|
mc->prevCommSet = searchCommInCmdline;
|
|
|
|
mc->prevCmdlineSet = stripExeFromCmdline;
|
2021-07-17 18:59:50 +00:00
|
|
|
mc->prevShowThreadNames = showThreadNames;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
/* 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;
|
2021-05-15 19:54:46 +00:00
|
|
|
#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)) \
|
2021-08-22 15:14:36 +00:00
|
|
|
break; \
|
2021-05-15 19:54:46 +00:00
|
|
|
\
|
|
|
|
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++; \
|
2021-04-10 11:31:39 +00:00
|
|
|
} while (0)
|
|
|
|
|
2021-05-15 19:54:46 +00:00
|
|
|
#define WRITE_SEPARATOR \
|
|
|
|
do { \
|
|
|
|
WRITE_HIGHLIGHT(0, 1, CRT_colors[FAILED_READ], CMDLINE_HIGHLIGHT_FLAG_SEPARATOR); \
|
|
|
|
mbMismatch += SEPARATOR_LEN - 1; \
|
|
|
|
str = stpcpy(str, SEPARATOR); \
|
2021-04-10 11:31:39 +00:00
|
|
|
} 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];
|
2021-04-04 16:07:26 +00:00
|
|
|
const int delExeAttr = CRT_colors[FAILED_READ];
|
|
|
|
const int delLibAttr = CRT_colors[PROCESS_TAG];
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
/* Establish some shortcuts to data we need */
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* cmdline = this->cmdline;
|
|
|
|
const char* procComm = this->procComm;
|
|
|
|
const char* procExe = this->procExe;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
char* strStart = mc->str;
|
|
|
|
char* str = strStart;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
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 */
|
2021-08-08 14:04:26 +00:00
|
|
|
if (showMergedCommand && (!Process_isUserlandThread(this) || showThreadNames) && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
|
2021-04-10 11:31:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-15 19:55:14 +00:00
|
|
|
if (cmdlineBasenameEnd > cmdlineBasenameStart)
|
|
|
|
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
|
2021-10-06 14:38:45 +00:00
|
|
|
|
|
|
|
if (this->procExeDeleted)
|
|
|
|
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
|
|
|
else if (this->usesDeletedLib)
|
|
|
|
WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
|
|
|
|
2021-05-15 19:55:14 +00:00
|
|
|
(void)stpcpyWithNewlineConversion(str, cmdline + (showProgramPath ? 0 : cmdlineBasenameStart));
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
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;
|
2021-08-08 14:04:26 +00:00
|
|
|
if (procExe && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
|
2021-04-10 11:31:39 +00:00
|
|
|
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)
|
2021-04-04 16:07:26 +00:00
|
|
|
WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
|
|
|
else if (this->usesDeletedLib)
|
|
|
|
WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
2021-04-10 11:31:39 +00:00
|
|
|
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)
|
2021-04-04 16:07:26 +00:00
|
|
|
WRITE_HIGHLIGHT(0, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
|
|
|
else if (this->usesDeletedLib)
|
|
|
|
WRITE_HIGHLIGHT(0, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
|
2021-04-10 11:31:39 +00:00
|
|
|
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 */
|
2021-08-08 14:04:26 +00:00
|
|
|
haveCommInCmdline = (!Process_isUserlandThread(this) || showThreadNames) && findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd);
|
2021-04-10 11:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen);
|
|
|
|
|
|
|
|
bool haveCommField = false;
|
|
|
|
|
2021-08-08 14:04:26 +00:00
|
|
|
if (!haveCommInExe && !haveCommInCmdline && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
|
2021-04-10 11:31:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-08 14:04:26 +00:00
|
|
|
if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames))
|
2021-04-10 11:31:39 +00:00
|
|
|
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;
|
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
const ProcessMergedCommand* mc = &this->mergedCommand;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
int strStart = RichString_size(str);
|
|
|
|
|
|
|
|
const bool highlightBaseName = this->settings->highlightBaseName;
|
|
|
|
const bool highlightSeparator = true;
|
2020-12-19 15:46:00 +00:00
|
|
|
const bool highlightDeleted = this->settings->highlightDeletedExe;
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
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;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2015-07-29 19:14:29 +00:00
|
|
|
}
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
RichString_appendWide(str, attr, cmdline);
|
|
|
|
|
|
|
|
if (this->settings->highlightBaseName) {
|
|
|
|
RichString_setAttrn(str, baseAttr, strStart, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2015-07-29 19:14:29 +00:00
|
|
|
|
2021-04-10 11:31:39 +00:00
|
|
|
RichString_appendWide(str, attr, this->mergedCommand.str);
|
|
|
|
|
|
|
|
for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) {
|
2021-07-14 17:24:18 +00:00
|
|
|
const ProcessCmdlineHighlight* hl = &mc->highlights[i];
|
2021-04-10 11:31:39 +00:00
|
|
|
|
|
|
|
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;
|
2015-07-29 19:14:29 +00:00
|
|
|
|
2021-04-10 11:31:39 +00:00
|
|
|
RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2021-04-14 18:16:16 +00:00
|
|
|
void Process_printRate(RichString* str, double rate, bool coloring) {
|
|
|
|
char buffer[16];
|
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
int largeNumberColor = CRT_colors[LARGE_NUMBER];
|
|
|
|
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
|
|
|
|
int processColor = CRT_colors[PROCESS];
|
2021-04-14 18:16:16 +00:00
|
|
|
int shadowColor = CRT_colors[PROCESS_SHADOW];
|
2020-11-28 16:42:02 +00:00
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
if (!coloring) {
|
|
|
|
largeNumberColor = CRT_colors[PROCESS];
|
|
|
|
processMegabytesColor = CRT_colors[PROCESS];
|
|
|
|
}
|
2020-11-28 16:42:02 +00:00
|
|
|
|
2020-09-07 09:53:58 +00:00
|
|
|
if (isnan(rate)) {
|
2021-04-14 18:16:16 +00:00
|
|
|
RichString_appendAscii(str, shadowColor, " N/A ");
|
|
|
|
} else if (rate < 0.005) {
|
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
|
|
|
|
RichString_appendnAscii(str, shadowColor, buffer, len);
|
2016-02-20 04:22:57 +00:00
|
|
|
} else if (rate < ONE_K) {
|
2021-04-14 18:16:16 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processColor, buffer, len);
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (rate < ONE_M) {
|
2021-04-14 18:16:16 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processColor, buffer, len);
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (rate < ONE_G) {
|
2021-04-14 18:16:16 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
|
2018-12-30 12:18:35 +00:00
|
|
|
} else if (rate < ONE_T) {
|
2021-04-14 18:16:16 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G);
|
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
|
|
|
} else if (rate < ONE_P) {
|
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
2015-01-22 01:27:31 +00:00
|
|
|
} else {
|
2021-04-14 18:16:16 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P);
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendnAscii(str, largeNumberColor, buffer, len);
|
2008-03-14 18:50:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-10 14:57:46 +00:00
|
|
|
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
|
2021-01-14 08:59:11 +00:00
|
|
|
int columns = width;
|
|
|
|
RichString_appendnWideColumns(str, attr, content, strlen(content), &columns);
|
|
|
|
RichString_appendChr(str, attr, ' ', width + 1 - columns);
|
2021-01-10 14:57:46 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 02:44:19 +00:00
|
|
|
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
|
|
|
|
if (val >= 0) {
|
|
|
|
if (val < 99.9F) {
|
|
|
|
if (val < 0.05F) {
|
|
|
|
*attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
}
|
|
|
|
xSnprintf(buffer, n, "%4.1f ", val);
|
|
|
|
} else if (val < 999) {
|
2021-09-05 06:46:01 +00:00
|
|
|
*attr = CRT_colors[PROCESS_MEGABYTES];
|
2021-09-03 02:44:19 +00:00
|
|
|
xSnprintf(buffer, n, "%3d. ", (int)val);
|
|
|
|
} else {
|
2021-09-14 01:16:34 +00:00
|
|
|
*attr = CRT_colors[PROCESS_MEGABYTES];
|
2021-09-03 02:44:19 +00:00
|
|
|
xSnprintf(buffer, n, "%4d ", (int)val);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
xSnprintf(buffer, n, " N/A ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 22:45:09 +00:00
|
|
|
static inline char processStateChar(ProcessState state) {
|
|
|
|
switch (state) {
|
|
|
|
case UNKNOWN: return '?';
|
|
|
|
case RUNNABLE: return 'U';
|
|
|
|
case RUNNING: return 'R';
|
|
|
|
case QUEUED: return 'Q';
|
|
|
|
case WAITING: return 'W';
|
|
|
|
case UNINTERRUPTIBLE_WAIT: return 'D';
|
|
|
|
case BLOCKED: return 'B';
|
|
|
|
case PAGING: return 'P';
|
|
|
|
case STOPPED: return 'T';
|
|
|
|
case TRACED: return 't';
|
|
|
|
case ZOMBIE: return 'Z';
|
|
|
|
case DEFUNCT: return 'X';
|
|
|
|
case IDLE: return 'I';
|
|
|
|
case SLEEPING: return 'S';
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
return '!';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 10:28:11 +00:00
|
|
|
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
|
2021-01-27 14:11:46 +00:00
|
|
|
char buffer[256];
|
|
|
|
size_t n = sizeof(buffer);
|
2006-03-04 18:16:49 +00:00
|
|
|
int attr = CRT_colors[DEFAULT_COLOR];
|
2015-01-22 01:27:31 +00:00
|
|
|
bool coloring = this->settings->highlightMegabytes;
|
2006-03-04 18:16:49 +00:00
|
|
|
|
|
|
|
switch (field) {
|
|
|
|
case COMM: {
|
2021-01-27 14:11:46 +00:00
|
|
|
int baseattr = CRT_colors[PROCESS_BASENAME];
|
2015-01-22 01:27:31 +00:00
|
|
|
if (this->settings->highlightThreads && Process_isThread(this)) {
|
2008-03-08 23:39:48 +00:00
|
|
|
attr = CRT_colors[PROCESS_THREAD];
|
|
|
|
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
|
|
|
|
}
|
2015-01-22 01:27:31 +00:00
|
|
|
if (!this->settings->treeView || this->indent == 0) {
|
2008-03-08 23:39:48 +00:00
|
|
|
Process_writeCommand(this, attr, baseattr, str);
|
2006-03-04 18:16:49 +00:00
|
|
|
return;
|
2021-01-27 14:11:46 +00:00
|
|
|
}
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-01-27 14:11:46 +00:00
|
|
|
char* buf = buffer;
|
|
|
|
int maxIndent = 0;
|
|
|
|
bool lastItem = (this->indent < 0);
|
|
|
|
int indent = (this->indent < 0 ? -this->indent : this->indent);
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
|
|
if (indent & (1U << i)) {
|
2021-07-14 17:18:27 +00:00
|
|
|
maxIndent = i + 1;
|
2006-07-11 06:13:32 +00:00
|
|
|
}
|
2021-01-27 14:11:46 +00:00
|
|
|
}
|
2020-12-08 20:24:19 +00:00
|
|
|
|
2021-01-27 14:11:46 +00:00
|
|
|
for (int i = 0; i < maxIndent - 1; i++) {
|
|
|
|
int written, ret;
|
|
|
|
if (indent & (1 << i)) {
|
|
|
|
ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
|
|
|
|
} else {
|
|
|
|
ret = xSnprintf(buf, n, " ");
|
|
|
|
}
|
|
|
|
if (ret < 0 || (size_t)ret >= n) {
|
|
|
|
written = n;
|
|
|
|
} else {
|
|
|
|
written = ret;
|
|
|
|
}
|
|
|
|
buf += written;
|
|
|
|
n -= written;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2021-01-27 14:11:46 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
Process_writeCommand(this, attr, baseattr, str);
|
|
|
|
return;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2021-04-18 17:25:56 +00:00
|
|
|
case PROC_COMM: {
|
|
|
|
const char* procComm;
|
|
|
|
if (this->procComm) {
|
|
|
|
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
|
|
|
|
procComm = this->procComm;
|
|
|
|
} else {
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
procComm = Process_isKernelThread(this) ? kthreadID : "N/A";
|
|
|
|
}
|
|
|
|
|
|
|
|
Process_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case PROC_EXE: {
|
|
|
|
const char* procExe;
|
|
|
|
if (this->procExe) {
|
|
|
|
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
|
2021-06-10 21:23:03 +00:00
|
|
|
if (this->settings->highlightDeletedExe) {
|
|
|
|
if (this->procExeDeleted)
|
|
|
|
attr = CRT_colors[FAILED_READ];
|
|
|
|
else if (this->usesDeletedLib)
|
|
|
|
attr = CRT_colors[PROCESS_TAG];
|
|
|
|
}
|
2021-04-18 17:25:56 +00:00
|
|
|
procExe = this->procExe + this->procExeBasenameOffset;
|
|
|
|
} else {
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
procExe = Process_isKernelThread(this) ? kthreadID : "N/A";
|
|
|
|
}
|
|
|
|
|
|
|
|
Process_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1);
|
|
|
|
return;
|
|
|
|
}
|
2021-05-25 17:02:12 +00:00
|
|
|
case CWD: {
|
|
|
|
const char* cwd;
|
|
|
|
if (!this->procCwd) {
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
cwd = "N/A";
|
|
|
|
} else if (String_startsWith(this->procCwd, "/proc/") && strstr(this->procCwd, " (deleted)") != NULL) {
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
cwd = "main thread terminated";
|
|
|
|
} else {
|
|
|
|
cwd = this->procCwd;
|
|
|
|
}
|
|
|
|
Process_printLeftAlignedField(str, attr, cwd, 25);
|
|
|
|
return;
|
|
|
|
}
|
2021-05-02 11:29:39 +00:00
|
|
|
case ELAPSED: Process_printTime(str, /* convert to hundreds of a second */ this->processList->realtimeMs / 10 - 100 * this->starttime_ctime, coloring); return;
|
2021-04-14 18:16:16 +00:00
|
|
|
case MAJFLT: Process_printCount(str, this->majflt, coloring); return;
|
|
|
|
case MINFLT: Process_printCount(str, this->minflt, coloring); return;
|
|
|
|
case M_RESIDENT: Process_printKBytes(str, this->m_resident, coloring); return;
|
|
|
|
case M_VIRT: Process_printKBytes(str, this->m_virt, coloring); return;
|
2021-01-27 14:11:46 +00:00
|
|
|
case NICE:
|
2017-07-27 19:07:50 +00:00
|
|
|
xSnprintf(buffer, n, "%3ld ", this->nice);
|
2015-03-16 04:43:04 +00:00
|
|
|
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
|
|
|
|
: this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
|
2021-01-27 14:11:46 +00:00
|
|
|
: CRT_colors[PROCESS_SHADOW];
|
|
|
|
break;
|
|
|
|
case NLWP:
|
|
|
|
if (this->nlwp == 1)
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
|
|
|
|
xSnprintf(buffer, n, "%4ld ", this->nlwp);
|
|
|
|
break;
|
2021-09-03 02:44:19 +00:00
|
|
|
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
|
2021-01-27 14:11:46 +00:00
|
|
|
case PERCENT_NORM_CPU: {
|
2021-09-03 02:44:19 +00:00
|
|
|
float cpuPercentage = this->percent_cpu / this->processList->activeCPUs;
|
|
|
|
Process_printPercentage(cpuPercentage, buffer, n, &attr);
|
2015-03-16 04:43:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-09-03 02:44:19 +00:00
|
|
|
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
|
2020-12-15 18:44:52 +00:00
|
|
|
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
|
|
|
|
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
|
|
|
|
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
|
2021-01-27 14:11:46 +00:00
|
|
|
case PRIORITY:
|
|
|
|
if (this->priority <= -100)
|
2017-07-27 19:07:50 +00:00
|
|
|
xSnprintf(buffer, n, " RT ");
|
2015-03-16 04:43:04 +00:00
|
|
|
else
|
2017-07-27 19:07:50 +00:00
|
|
|
xSnprintf(buffer, n, "%3ld ", this->priority);
|
2015-03-16 04:43:04 +00:00
|
|
|
break;
|
2017-07-27 19:07:50 +00:00
|
|
|
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
|
2020-12-15 18:44:52 +00:00
|
|
|
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
|
2017-07-27 19:07:50 +00:00
|
|
|
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
|
2021-01-27 14:11:46 +00:00
|
|
|
case STATE:
|
2021-10-11 22:45:09 +00:00
|
|
|
xSnprintf(buffer, n, "%c ", processStateChar(this->state));
|
2021-01-27 14:11:46 +00:00
|
|
|
switch (this->state) {
|
2021-10-11 22:45:09 +00:00
|
|
|
case RUNNABLE:
|
|
|
|
case RUNNING:
|
|
|
|
case TRACED:
|
2021-08-30 17:20:42 +00:00
|
|
|
attr = CRT_colors[PROCESS_RUN_STATE];
|
2021-01-27 14:11:46 +00:00
|
|
|
break;
|
2021-10-11 22:45:09 +00:00
|
|
|
|
|
|
|
case BLOCKED:
|
|
|
|
case DEFUNCT:
|
|
|
|
case STOPPED:
|
|
|
|
case ZOMBIE:
|
2021-01-27 14:11:46 +00:00
|
|
|
attr = CRT_colors[PROCESS_D_STATE];
|
|
|
|
break;
|
2021-10-11 22:45:09 +00:00
|
|
|
|
|
|
|
case QUEUED:
|
|
|
|
case WAITING:
|
|
|
|
case UNINTERRUPTIBLE_WAIT:
|
|
|
|
case IDLE:
|
|
|
|
case SLEEPING:
|
2021-01-27 14:11:46 +00:00
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
break;
|
2021-10-11 22:45:09 +00:00
|
|
|
|
|
|
|
case UNKNOWN:
|
|
|
|
case PAGING:
|
|
|
|
break;
|
2014-10-14 01:30:17 +00:00
|
|
|
}
|
2006-03-04 18:16:49 +00:00
|
|
|
break;
|
2021-08-16 20:50:36 +00:00
|
|
|
case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
|
2021-04-14 18:16:16 +00:00
|
|
|
case TIME: Process_printTime(str, this->time, coloring); return;
|
2021-01-27 14:11:46 +00:00
|
|
|
case TGID:
|
|
|
|
if (this->tgid == this->pid)
|
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
|
|
|
|
|
|
|
xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid);
|
|
|
|
break;
|
2020-12-15 18:44:52 +00:00
|
|
|
case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
|
2021-03-21 18:40:56 +00:00
|
|
|
case TTY:
|
|
|
|
if (!this->tty_name) {
|
2021-01-27 14:11:42 +00:00
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
2021-03-21 18:40:56 +00:00
|
|
|
xSnprintf(buffer, n, "(no tty) ");
|
2021-01-27 14:11:42 +00:00
|
|
|
} else {
|
2021-03-21 18:40:56 +00:00
|
|
|
const char* name = String_startsWith(this->tty_name, "/dev/") ? (this->tty_name + strlen("/dev/")) : this->tty_name;
|
|
|
|
xSnprintf(buffer, n, "%-8s ", name);
|
2021-01-27 14:11:42 +00:00
|
|
|
}
|
|
|
|
break;
|
2021-01-27 14:11:46 +00:00
|
|
|
case USER:
|
2020-11-04 16:46:04 +00:00
|
|
|
if (Process_getuid != this->st_uid)
|
2006-03-04 18:16:49 +00:00
|
|
|
attr = CRT_colors[PROCESS_SHADOW];
|
2020-12-23 11:21:29 +00:00
|
|
|
|
2006-07-12 01:35:59 +00:00
|
|
|
if (this->user) {
|
2021-08-16 20:50:36 +00:00
|
|
|
Process_printLeftAlignedField(str, attr, this->user, 10);
|
2020-12-23 11:21:29 +00:00
|
|
|
return;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2020-12-23 11:21:29 +00:00
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
xSnprintf(buffer, n, "%-10d ", this->st_uid);
|
2006-03-04 18:16:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
if (DynamicColumn_writeField(this, str, field))
|
|
|
|
return;
|
2021-04-18 13:52:28 +00:00
|
|
|
assert(0 && "Process_writeField: default key reached"); /* should never be reached */
|
2017-07-27 19:07:50 +00:00
|
|
|
xSnprintf(buffer, n, "- ");
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
break;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2021-04-14 18:54:38 +00:00
|
|
|
RichString_appendAscii(str, attr, buffer);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 10:28:11 +00:00
|
|
|
void Process_display(const Object* cast, RichString* out) {
|
|
|
|
const Process* this = (const Process*) cast;
|
|
|
|
const ProcessField* fields = this->settings->fields;
|
2008-03-09 08:58:38 +00:00
|
|
|
for (int i = 0; fields[i]; i++)
|
2015-04-01 02:23:10 +00:00
|
|
|
As_Process(this)->writeField(this, out, fields[i]);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2020-11-04 16:46:04 +00:00
|
|
|
if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) {
|
2008-03-09 08:58:38 +00:00
|
|
|
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this->tag == true) {
|
2008-03-09 08:58:38 +00:00
|
|
|
RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 01:56:16 +00:00
|
|
|
if (this->settings->highlightChanges) {
|
2020-11-16 11:13:47 +00:00
|
|
|
if (Process_isTomb(this)) {
|
2020-10-31 01:56:16 +00:00
|
|
|
out->highlightAttr = CRT_colors[PROCESS_TOMB];
|
2020-11-16 11:13:47 +00:00
|
|
|
} else if (Process_isNew(this)) {
|
2020-11-01 00:36:53 +00:00
|
|
|
out->highlightAttr = CRT_colors[PROCESS_NEW];
|
2020-11-16 11:13:47 +00:00
|
|
|
}
|
2020-10-31 01:56:16 +00:00
|
|
|
}
|
2020-11-16 11:13:47 +00:00
|
|
|
|
2021-03-12 15:46:04 +00:00
|
|
|
assert(RichString_size(out) > 0);
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
2015-02-20 16:52:10 +00:00
|
|
|
void Process_done(Process* this) {
|
2008-03-09 08:58:38 +00:00
|
|
|
assert (this != NULL);
|
2020-12-19 15:21:08 +00:00
|
|
|
free(this->cmdline);
|
2021-01-30 14:31:59 +00:00
|
|
|
free(this->procComm);
|
|
|
|
free(this->procExe);
|
2021-05-25 17:02:12 +00:00
|
|
|
free(this->procCwd);
|
2021-04-10 09:10:50 +00:00
|
|
|
free(this->mergedCommand.str);
|
2021-03-21 18:40:56 +00:00
|
|
|
free(this->tty_name);
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 10:06:49 +00:00
|
|
|
/* This function returns the string displayed in Command column, so that sorting
|
|
|
|
* happens on what is displayed - whether comm, full path, basename, etc.. So
|
|
|
|
* this follows Process_writeField(COMM) and Process_writeCommand */
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* Process_getCommandStr(const Process* this) {
|
2021-04-24 10:06:49 +00:00
|
|
|
if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !this->mergedCommand.str) {
|
|
|
|
return this->cmdline;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this->mergedCommand.str;
|
2020-10-17 10:54:45 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 11:19:50 +00:00
|
|
|
const ProcessClass Process_class = {
|
2015-04-01 02:23:10 +00:00
|
|
|
.super = {
|
|
|
|
.extends = Class(Object),
|
|
|
|
.display = Process_display,
|
|
|
|
.delete = Process_delete,
|
|
|
|
.compare = Process_compare
|
|
|
|
},
|
|
|
|
.writeField = Process_writeField,
|
2020-10-17 10:54:45 +00:00
|
|
|
.getCommandStr = Process_getCommandStr,
|
2012-12-05 15:12:20 +00:00
|
|
|
};
|
|
|
|
|
2021-01-26 17:41:04 +00:00
|
|
|
void Process_init(Process* this, const Settings* settings) {
|
2015-01-22 01:27:31 +00:00
|
|
|
this->settings = settings;
|
2008-03-09 08:58:38 +00:00
|
|
|
this->tag = false;
|
2010-06-17 19:02:03 +00:00
|
|
|
this->showChildren = true;
|
2010-11-22 12:40:20 +00:00
|
|
|
this->show = true;
|
2008-03-09 08:58:38 +00:00
|
|
|
this->updated = false;
|
2021-04-10 09:46:57 +00:00
|
|
|
this->cmdlineBasenameEnd = -1;
|
2021-06-09 09:13:39 +00:00
|
|
|
this->st_uid = (uid_t)-1;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2020-11-04 16:46:04 +00:00
|
|
|
if (Process_getuid == (uid_t)-1) {
|
2020-11-01 00:09:51 +00:00
|
|
|
Process_getuid = getuid();
|
|
|
|
}
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Process_toggleTag(Process* this) {
|
2020-12-08 21:37:15 +00:00
|
|
|
this->tag = !this->tag;
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 01:56:16 +00:00
|
|
|
bool Process_isNew(const Process* this) {
|
2020-11-01 00:36:53 +00:00
|
|
|
assert(this->processList);
|
2021-03-30 04:55:48 +00:00
|
|
|
if (this->processList->monotonicMs >= this->seenStampMs) {
|
|
|
|
return this->processList->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)this->processList->settings->highlightDelaySecs;
|
2020-11-16 11:13:47 +00:00
|
|
|
}
|
2020-10-31 01:56:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Process_isTomb(const Process* this) {
|
2021-07-14 17:15:09 +00:00
|
|
|
return this->tombStampMs > 0;
|
2020-10-31 01:56:16 +00:00
|
|
|
}
|
|
|
|
|
2008-03-09 08:58:38 +00:00
|
|
|
bool Process_setPriority(Process* this, int priority) {
|
2021-01-21 19:27:37 +00:00
|
|
|
if (Settings_isReadonly())
|
|
|
|
return false;
|
|
|
|
|
2015-12-07 19:10:09 +00:00
|
|
|
int old_prio = getpriority(PRIO_PROCESS, this->pid);
|
|
|
|
int err = setpriority(PRIO_PROCESS, this->pid, priority);
|
2021-02-16 18:44:59 +00:00
|
|
|
|
2015-12-07 19:10:09 +00:00
|
|
|
if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
|
|
|
|
this->nice = priority;
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
2015-12-07 19:10:09 +00:00
|
|
|
return (err == 0);
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 23:35:24 +00:00
|
|
|
bool Process_changePriorityBy(Process* this, Arg delta) {
|
|
|
|
return Process_setPriority(this, this->nice + delta.i);
|
2012-10-04 23:59:45 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 23:35:24 +00:00
|
|
|
bool Process_sendSignal(Process* this, Arg sgn) {
|
2021-02-16 18:44:59 +00:00
|
|
|
return kill(this->pid, sgn.i) == 0;
|
2008-03-09 08:58:38 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
int Process_pidCompare(const void* v1, const void* v2) {
|
2020-09-23 12:15:51 +00:00
|
|
|
const Process* p1 = (const Process*)v1;
|
|
|
|
const Process* p2 = (const Process*)v2;
|
2020-12-08 21:37:15 +00:00
|
|
|
|
|
|
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2006-07-11 06:13:32 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
int Process_compare(const void* v1, const void* v2) {
|
2021-07-14 17:24:18 +00:00
|
|
|
const Process* p1 = (const Process*)v1;
|
|
|
|
const Process* p2 = (const Process*)v2;
|
2020-11-04 16:46:24 +00:00
|
|
|
|
2021-07-14 17:24:18 +00:00
|
|
|
const Settings* settings = p1->settings;
|
2020-11-04 16:46:24 +00:00
|
|
|
|
2020-12-18 14:03:31 +00:00
|
|
|
ProcessField key = Settings_getActiveSortKey(settings);
|
2020-12-17 22:08:56 +00:00
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
int result = Process_compareByKey(p1, p2, key);
|
2020-12-18 21:12:26 +00:00
|
|
|
|
|
|
|
// Implement tie-breaker (needed to make tree mode more stable)
|
|
|
|
if (!result)
|
2021-01-21 19:57:34 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2020-12-18 21:12:26 +00:00
|
|
|
|
2021-03-12 15:44:46 +00:00
|
|
|
return (Settings_getActiveDirection(settings) == 1) ? result : -result;
|
2020-12-18 21:12:26 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
|
2020-12-18 21:12:26 +00:00
|
|
|
int r;
|
|
|
|
|
2020-12-15 18:44:48 +00:00
|
|
|
switch (key) {
|
2006-03-04 18:16:49 +00:00
|
|
|
case PERCENT_CPU:
|
2020-10-30 16:02:20 +00:00
|
|
|
case PERCENT_NORM_CPU:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->percent_cpu, p2->percent_cpu);
|
2006-03-04 18:16:49 +00:00
|
|
|
case PERCENT_MEM:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
|
2006-03-04 18:16:49 +00:00
|
|
|
case COMM:
|
2020-10-17 10:54:45 +00:00
|
|
|
return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
|
2021-04-18 17:25:56 +00:00
|
|
|
case PROC_COMM: {
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
|
|
|
|
const char* comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
|
2021-04-18 17:25:56 +00:00
|
|
|
return SPACESHIP_NULLSTR(comm1, comm2);
|
|
|
|
}
|
|
|
|
case PROC_EXE: {
|
2021-07-14 17:24:18 +00:00
|
|
|
const char* exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
|
|
|
|
const char* exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
|
2021-04-18 17:25:56 +00:00
|
|
|
return SPACESHIP_NULLSTR(exe1, exe2);
|
|
|
|
}
|
2021-05-25 17:02:12 +00:00
|
|
|
case CWD:
|
|
|
|
return SPACESHIP_NULLSTR(p1->procCwd, p2->procCwd);
|
2021-05-02 11:29:39 +00:00
|
|
|
case ELAPSED:
|
|
|
|
r = -SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
|
|
|
|
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case MAJFLT:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->majflt, p2->majflt);
|
2015-03-16 04:43:04 +00:00
|
|
|
case MINFLT:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->minflt, p2->minflt);
|
2015-03-16 04:43:04 +00:00
|
|
|
case M_RESIDENT:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
|
2020-11-20 16:09:34 +00:00
|
|
|
case M_VIRT:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->m_virt, p2->m_virt);
|
2015-03-16 04:43:04 +00:00
|
|
|
case NICE:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->nice, p2->nice);
|
2007-05-21 19:10:53 +00:00
|
|
|
case NLWP:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
|
2015-03-16 04:43:04 +00:00
|
|
|
case PGRP:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
|
2015-03-16 04:43:04 +00:00
|
|
|
case PID:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case PPID:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->ppid, p2->ppid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case PRIORITY:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->priority, p2->priority);
|
2015-03-17 02:01:48 +00:00
|
|
|
case PROCESSOR:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->processor, p2->processor);
|
2015-03-16 04:43:04 +00:00
|
|
|
case SESSION:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->session, p2->session);
|
|
|
|
case STARTTIME:
|
|
|
|
r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
|
|
|
|
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case STATE:
|
2021-10-11 22:45:09 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->state, p2->state);
|
2015-03-16 04:43:04 +00:00
|
|
|
case ST_UID:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case TIME:
|
2021-01-21 13:27:23 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->time, p2->time);
|
2015-03-16 04:43:04 +00:00
|
|
|
case TGID:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->tgid, p2->tgid);
|
2015-03-16 04:43:04 +00:00
|
|
|
case TPGID:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
|
2021-03-21 18:40:56 +00:00
|
|
|
case TTY:
|
|
|
|
/* Order no tty last */
|
|
|
|
return SPACESHIP_DEFAULTSTR(p1->tty_name, p2->tty_name, "\x7F");
|
2015-03-16 04:43:04 +00:00
|
|
|
case USER:
|
2020-11-04 16:46:24 +00:00
|
|
|
return SPACESHIP_NULLSTR(p1->user, p2->user);
|
2006-03-04 18:16:49 +00:00
|
|
|
default:
|
2021-04-18 13:52:28 +00:00
|
|
|
assert(0 && "Process_compareByKey_Base: default key reached"); /* should never be reached */
|
2020-12-18 21:12:26 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-18 20:29:25 +00:00
|
|
|
|
|
|
|
void Process_updateComm(Process* this, const char* comm) {
|
|
|
|
if (!this->procComm && !comm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this->procComm && comm && String_eq(this->procComm, comm))
|
|
|
|
return;
|
|
|
|
|
|
|
|
free(this->procComm);
|
|
|
|
this->procComm = comm ? xStrdup(comm) : NULL;
|
|
|
|
this->mergedCommand.commChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int skipPotentialPath(const char* cmdline, int end) {
|
|
|
|
if (cmdline[0] != '/')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int slash = 0;
|
|
|
|
for (int i = 1; i < end; i++) {
|
2021-07-14 17:18:27 +00:00
|
|
|
if (cmdline[i] == '/' && cmdline[i + 1] != '\0') {
|
2021-05-18 20:29:25 +00:00
|
|
|
slash = i + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-14 17:18:27 +00:00
|
|
|
if (cmdline[i] == ' ' && cmdline[i - 1] != '\\')
|
2021-05-18 20:29:25 +00:00
|
|
|
return slash;
|
|
|
|
|
2021-07-14 17:18:27 +00:00
|
|
|
if (cmdline[i] == ':' && cmdline[i + 1] == ' ')
|
2021-05-18 20:29:25 +00:00
|
|
|
return slash;
|
|
|
|
}
|
|
|
|
|
|
|
|
return slash;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd) {
|
|
|
|
assert(basenameStart >= 0);
|
|
|
|
assert((cmdline && basenameStart < (int)strlen(cmdline)) || (!cmdline && basenameStart == 0));
|
2021-05-23 13:53:23 +00:00
|
|
|
assert((basenameEnd > basenameStart) || (basenameEnd == 0 && basenameStart == 0));
|
2021-05-18 20:29:25 +00:00
|
|
|
assert((cmdline && basenameEnd <= (int)strlen(cmdline)) || (!cmdline && basenameEnd == 0));
|
|
|
|
|
|
|
|
if (!this->cmdline && !cmdline)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this->cmdline && cmdline && String_eq(this->cmdline, cmdline))
|
|
|
|
return;
|
|
|
|
|
|
|
|
free(this->cmdline);
|
|
|
|
this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
|
|
|
|
this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
|
|
|
|
this->cmdlineBasenameEnd = basenameEnd;
|
|
|
|
this->mergedCommand.cmdlineChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process_updateExe(Process* this, const char* exe) {
|
|
|
|
if (!this->procExe && !exe)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this->procExe && exe && String_eq(this->procExe, exe))
|
|
|
|
return;
|
|
|
|
|
|
|
|
free(this->procExe);
|
|
|
|
if (exe) {
|
|
|
|
this->procExe = xStrdup(exe);
|
|
|
|
const char* lastSlash = strrchr(exe, '/');
|
|
|
|
this->procExeBasenameOffset = (lastSlash && *(lastSlash + 1) != '\0' && lastSlash != exe) ? (lastSlash - exe + 1) : 0;
|
|
|
|
} else {
|
|
|
|
this->procExe = NULL;
|
|
|
|
this->procExeBasenameOffset = 0;
|
|
|
|
}
|
|
|
|
this->mergedCommand.exeChanged = true;
|
|
|
|
}
|