From 9114939a6ceaec2cbf883f2a8028472e2568aadf Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sat, 6 Nov 2021 17:08:56 +0100 Subject: [PATCH 1/7] Ensure maximum width for CGROUP column --- linux/LinuxProcess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 7b48c9362..e5f70d778 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -81,7 +81,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, - [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, + [CGROUP] = { .name = "CGROUP", .title = "CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, #ifdef HAVE_DELAYACCT @@ -246,7 +246,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces #ifdef HAVE_VSERVER case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif - case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup ? lp->cgroup : ""); break; + case CGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup ? lp->cgroup : "N/A"); break; case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); From b540e95a6f71e561afaf27528b8d807820a0939b Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sat, 6 Nov 2021 17:20:37 +0100 Subject: [PATCH 2/7] Filter leading colons in CGROUP name --- linux/LinuxProcessList.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index a2d3d2bec..6b9f988f7 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -872,9 +872,16 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t if (!ok) break; - char* group = strchr(buffer, ':'); - if (!group) - break; + char* group = buffer; + for (size_t i = 0; i < 2; i++) { + group = strchrnul(group, ':'); + if (!*group) + break; + group++; + } + + char* eol = strchrnul(group, '\n'); + *eol = '\0'; if (at != output) { *at = ';'; From 83a73f5d8abf57433c06ed4af884ef68c376909b Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 19 Oct 2021 23:36:31 +0200 Subject: [PATCH 3/7] Compress cgroup names based on some heuristics --- Makefile.am | 2 + linux/CGroupUtils.c | 285 +++++++++++++++++++++++++++++++++++++++ linux/CGroupUtils.h | 16 +++ linux/LinuxProcess.c | 7 +- linux/LinuxProcess.h | 1 + linux/LinuxProcessList.c | 20 +++ linux/ProcessField.h | 1 + 7 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 linux/CGroupUtils.c create mode 100644 linux/CGroupUtils.h diff --git a/Makefile.am b/Makefile.am index 7ed500ca3..2a9cc29f5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,6 +153,7 @@ linux_platform_headers = \ generic/gettime.h \ generic/hostname.h \ generic/uname.h \ + linux/CGroupUtils.h \ linux/HugePageMeter.h \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ @@ -174,6 +175,7 @@ linux_platform_sources = \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ + linux/CGroupUtils.c \ linux/HugePageMeter.c \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c new file mode 100644 index 000000000..dd91809a9 --- /dev/null +++ b/linux/CGroupUtils.c @@ -0,0 +1,285 @@ +/* +htop - CGroupUtils.h +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "linux/CGroupUtils.h" + +#include "XUtils.h" + + +static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t bufsize) { + const char* str_slice_suffix = ".slice"; + const char* str_system_slice = "system.slice"; + const char* str_user_slice = "user.slice"; + const char* str_machine_slice = "machine.slice"; + const char* str_user_slice_prefix = "/user-"; + + const char* str_lxc_monitor_prefix = "lxc.monitor."; + const char* str_lxc_payload_prefix = "lxc.payload."; + + const char* str_nspawn_scope_prefix = "machine-"; + const char* str_nspawn_monitor_label = "/supervisor"; + + const char* str_service_suffix = ".service"; + const char* str_scope_suffix = ".scope"; + + while(*cgroup) { + if ('/' == *cgroup) { + while ('/' == *cgroup) + cgroup++; + + if (!bufsize) + return false; + + *buf++ = '/'; + bufsize--; + + continue; + } + + const char* labelStart = cgroup; + const char* nextSlash = strchrnul(labelStart, '/'); + const size_t labelLen = nextSlash - labelStart; + + if (String_startsWith(cgroup, str_system_slice)) { + cgroup += strlen(str_system_slice); + + if (*cgroup && *cgroup != '/') + goto handle_default; + + if (bufsize < 3) + return false; + + *buf++ = '['; + *buf++ = 'S'; + *buf++ = ']'; + bufsize -= 3; + + continue; + } + + if (String_startsWith(cgroup, str_machine_slice)) { + cgroup += strlen(str_machine_slice); + + if (*cgroup && *cgroup != '/') + goto handle_default; + + if (bufsize < 3) + return false; + + *buf++ = '['; + *buf++ = 'M'; + *buf++ = ']'; + bufsize -= 3; + + continue; + } + + if (String_startsWith(cgroup, str_user_slice)) { + cgroup += strlen(str_user_slice); + + if (*cgroup && *cgroup != '/') + goto handle_default; + + if (bufsize < 3) + return false; + + *buf++ = '['; + *buf++ = 'U'; + *buf++ = ']'; + bufsize -= 3; + + if (!String_startsWith(cgroup, str_user_slice_prefix)) + continue; + + const char* userSliceSlash = strchrnul(cgroup + strlen(str_user_slice_prefix), '/'); + const char* sliceSpec = userSliceSlash - strlen(str_slice_suffix); + + if (!String_startsWith(sliceSpec, str_slice_suffix)) + continue; + + const size_t sliceNameLen = sliceSpec - (cgroup + strlen(str_user_slice_prefix)); + + if (bufsize < sliceNameLen + 1) + return false; + + buf[-1] = ':'; + + cgroup += strlen(str_user_slice_prefix); + while(cgroup < sliceSpec) { + *buf++ = *cgroup++; + bufsize--; + } + cgroup = userSliceSlash; + + *buf++ = ']'; + bufsize--; + + continue; + } + + if (labelLen > strlen(str_slice_suffix) && String_startsWith(nextSlash - strlen(str_slice_suffix), str_slice_suffix)) { + const size_t sliceNameLen = labelLen - strlen(str_slice_suffix); + if (bufsize < 2 + sliceNameLen) + return false; + + *buf++ = '['; + bufsize--; + + for(size_t i = sliceNameLen; i; i--) { + *buf++ = *cgroup++; + bufsize--; + } + + *buf++ = ']'; + bufsize--; + + cgroup = nextSlash; + + continue; + } + + if (String_startsWith(cgroup, str_lxc_payload_prefix)) { + const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix); + if (bufsize < 6 + cgroupNameLen) + return false; + + *buf++ = '['; + *buf++ = 'l'; + *buf++ = 'x'; + *buf++ = 'c'; + *buf++ = ':'; + bufsize -= 5; + + cgroup += strlen(str_lxc_payload_prefix); + while(cgroup < nextSlash) { + *buf++ = *cgroup++; + bufsize--; + } + + *buf++ = ']'; + bufsize--; + + continue; + } + + if (String_startsWith(cgroup, str_lxc_monitor_prefix)) { + const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix); + if (bufsize < 6 + cgroupNameLen) + return false; + + *buf++ = '['; + *buf++ = 'L'; + *buf++ = 'X'; + *buf++ = 'C'; + *buf++ = ':'; + bufsize -= 5; + + cgroup += strlen(str_lxc_monitor_prefix); + while(cgroup < nextSlash) { + *buf++ = *cgroup++; + bufsize--; + } + + *buf++ = ']'; + bufsize--; + + continue; + } + + if (labelLen > strlen(str_service_suffix) && String_startsWith(nextSlash - strlen(str_service_suffix), str_service_suffix)) { + const size_t serviceNameLen = labelLen - strlen(str_service_suffix); + + if (String_startsWith(cgroup, "user@")) { + cgroup = nextSlash; + + while(*cgroup == '/') + cgroup++; + + continue; + } + + if (bufsize < serviceNameLen) + return false; + + for(size_t i = serviceNameLen; i; i--) { + *buf++ = *cgroup++; + bufsize--; + } + + cgroup = nextSlash; + + continue; + } + + if (labelLen > strlen(str_scope_suffix) && String_startsWith(nextSlash - strlen(str_scope_suffix), str_scope_suffix)) { + const size_t scopeNameLen = labelLen - strlen(str_scope_suffix); + + if (String_startsWith(cgroup, str_nspawn_scope_prefix)) { + const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix); + if (bufsize < 6 + machineScopeNameLen) + return false; + + const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label); + + *buf++ = '['; + *buf++ = is_monitor ? 'S' : 's'; + *buf++ = is_monitor ? 'N' : 'n'; + *buf++ = is_monitor ? 'C' : 'c'; + *buf++ = ':'; + bufsize -= 5; + + cgroup += strlen(str_nspawn_scope_prefix); + for(size_t i = machineScopeNameLen; i; i--) { + *buf++ = *cgroup++; + bufsize--; + } + + *buf++ = ']'; + bufsize--; + + cgroup = nextSlash; + + continue; + } + + if (bufsize < 1 + scopeNameLen) + return false; + + *buf++ = '!'; + bufsize--; + + for(size_t i = scopeNameLen; i; i--) { + *buf++ = *cgroup++; + bufsize--; + } + + cgroup = nextSlash; + + continue; + } + +handle_default: + // Default behavior: Copy the full label + cgroup = labelStart; + + if (bufsize < (size_t)(nextSlash - cgroup)) + return false; + + while(cgroup < nextSlash) { + *buf++ = *cgroup++; + bufsize--; + } + } + + return true; +} + +bool CGroup_filterName(const char *cgroup, char* buf, size_t bufsize) { + memset(buf, 0, bufsize); + + return CGroup_filterName_internal(cgroup, buf, bufsize - 1); +} diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h new file mode 100644 index 000000000..3df428e4c --- /dev/null +++ b/linux/CGroupUtils.h @@ -0,0 +1,16 @@ +#ifndef HEADER_CGroupUtils +#define HEADER_CGroupUtils +/* +htop - CGroupUtils.h +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include + + +bool CGroup_filterName(const char *cgroup, char* buf, size_t bufsize); + +#endif /* HEADER_CGroupUtils */ diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index e5f70d778..ba2dbd463 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -81,7 +81,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, - [CGROUP] = { .name = "CGROUP", .title = "CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, + [CGROUP] = { .name = "CGROUP", .title = "CGROUP (raw) ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, + [CCGROUP] = { .name = "CCGROUP", .title = "CGROUP (compressed) ", .description = "Which cgroup the process is in (condensed to essentials)", .flags = PROCESS_FLAG_LINUX_CGROUP, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, #ifdef HAVE_DELAYACCT @@ -111,6 +112,7 @@ Process* LinuxProcess_new(const Settings* settings) { void Process_delete(Object* cast) { LinuxProcess* this = (LinuxProcess*) cast; Process_done((Process*)cast); + free(this->cgroup_short); free(this->cgroup); #ifdef HAVE_OPENVZ free(this->ctid); @@ -247,6 +249,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif case CGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup ? lp->cgroup : "N/A"); break; + case CCGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break; case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); @@ -370,6 +373,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce #endif case CGROUP: return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup); + case CCGROUP: + return SPACESHIP_NULLSTR(p1->cgroup_short, p2->cgroup_short); case OOM: return SPACESHIP_NUMBER(p1->oom, p2->oom); #ifdef HAVE_DELAYACCT diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index b4e1a8bfe..3e5d38040 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -89,6 +89,7 @@ typedef struct LinuxProcess_ { unsigned int vxid; #endif char* cgroup; + char* cgroup_short; unsigned int oom; #ifdef HAVE_DELAYACCT unsigned long long int delay_read_time; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 6b9f988f7..74566ab87 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -45,6 +45,7 @@ in the source distribution for its full text. #include "Process.h" #include "Settings.h" #include "XUtils.h" +#include "linux/CGroupUtils.h" #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep @@ -860,6 +861,10 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t free(process->cgroup); process->cgroup = NULL; } + if (process->cgroup_short) { + free(process->cgroup_short); + process->cgroup_short = NULL; + } return; } char output[PROC_LINE_LENGTH + 1]; @@ -892,7 +897,22 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t left -= wrote; } fclose(file); + + bool changed = !process->cgroup || !String_eq(process->cgroup, output); + free_and_xStrdup(&process->cgroup, output); + + if (!changed) + return; + + char* cgroup_short = xCalloc(strlen(process->cgroup) + 1, 1); + if (CGroup_filterName(process->cgroup, cgroup_short, strlen(process->cgroup) + 1)) { + free_and_xStrdup(&process->cgroup_short, cgroup_short); + } else { + free(process->cgroup_short); + process->cgroup_short = NULL; + } + free(cgroup_short); } #ifdef HAVE_VSERVER diff --git a/linux/ProcessField.h b/linux/ProcessField.h index 70475929c..17cafa964 100644 --- a/linux/ProcessField.h +++ b/linux/ProcessField.h @@ -45,6 +45,7 @@ in the source distribution for its full text. SECATTR = 123, \ AUTOGROUP_ID = 127, \ AUTOGROUP_NICE = 128, \ + CCGROUP = 129, \ // End of list From 12fd2b197710d13fc243e4f8b96cf7857b47dc45 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Wed, 20 Oct 2021 20:19:00 +0200 Subject: [PATCH 4/7] Extract string writing/buffer handling into some callback --- linux/CGroupUtils.c | 244 +++++++++++++++++++-------------------- linux/CGroupUtils.h | 2 +- linux/LinuxProcessList.c | 6 +- 3 files changed, 121 insertions(+), 131 deletions(-) diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c index dd91809a9..5d9d20273 100644 --- a/linux/CGroupUtils.c +++ b/linux/CGroupUtils.c @@ -10,7 +10,45 @@ in the source distribution for its full text. #include "XUtils.h" -static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t bufsize) { +typedef struct StrBuf_state { + char *buf; + size_t size; + size_t pos; +} StrBuf_state; + +typedef bool (*StrBuf_putc_t)(StrBuf_state* p, char c); + +static bool StrBuf_putc_count(StrBuf_state* p, ATTR_UNUSED char c) { + p->pos++; + return true; +} + +static bool StrBuf_putc_write(StrBuf_state* p, char c) { + if (p->pos >= p->size) + return false; + + p->buf[p->pos] = c; + p->pos++; + return true; +} + +static bool StrBuf_putsn(StrBuf_state* p, StrBuf_putc_t w, const char* s, size_t count) { + while (count--) + if (!w(p, *s++)) + return false; + + return true; +} + +static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) { + while (*s) + if (!w(p, *s++)) + return false; + + return true; +} + +static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrBuf_putc_t w) { const char* str_slice_suffix = ".slice"; const char* str_system_slice = "system.slice"; const char* str_user_slice = "user.slice"; @@ -22,21 +60,19 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf const char* str_nspawn_scope_prefix = "machine-"; const char* str_nspawn_monitor_label = "/supervisor"; + const char* str_nspawn_payload_label = "/payload"; const char* str_service_suffix = ".service"; const char* str_scope_suffix = ".scope"; - while(*cgroup) { + while (*cgroup) { if ('/' == *cgroup) { while ('/' == *cgroup) cgroup++; - if (!bufsize) + if (!w(s, '/')) return false; - *buf++ = '/'; - bufsize--; - continue; } @@ -44,54 +80,30 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf const char* nextSlash = strchrnul(labelStart, '/'); const size_t labelLen = nextSlash - labelStart; - if (String_startsWith(cgroup, str_system_slice)) { - cgroup += strlen(str_system_slice); - - if (*cgroup && *cgroup != '/') - goto handle_default; + if (labelLen == strlen(str_system_slice) && String_startsWith(cgroup, str_system_slice)) { + cgroup = nextSlash; - if (bufsize < 3) + if (!StrBuf_putsz(s, w, "[S]")) return false; - *buf++ = '['; - *buf++ = 'S'; - *buf++ = ']'; - bufsize -= 3; - continue; } - if (String_startsWith(cgroup, str_machine_slice)) { - cgroup += strlen(str_machine_slice); - - if (*cgroup && *cgroup != '/') - goto handle_default; + if (labelLen == strlen(str_machine_slice) && String_startsWith(cgroup, str_machine_slice)) { + cgroup = nextSlash; - if (bufsize < 3) + if (!StrBuf_putsz(s, w, "[M]")) return false; - *buf++ = '['; - *buf++ = 'M'; - *buf++ = ']'; - bufsize -= 3; - continue; } - if (String_startsWith(cgroup, str_user_slice)) { - cgroup += strlen(str_user_slice); - - if (*cgroup && *cgroup != '/') - goto handle_default; + if (labelLen == strlen(str_user_slice) && String_startsWith(cgroup, str_user_slice)) { + cgroup = nextSlash; - if (bufsize < 3) + if (!StrBuf_putsz(s, w, "[U]")) return false; - *buf++ = '['; - *buf++ = 'U'; - *buf++ = ']'; - bufsize -= 3; - if (!String_startsWith(cgroup, str_user_slice_prefix)) continue; @@ -103,39 +115,32 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf const size_t sliceNameLen = sliceSpec - (cgroup + strlen(str_user_slice_prefix)); - if (bufsize < sliceNameLen + 1) + s->pos--; + if (!w(s, ':')) return false; - buf[-1] = ':'; + if (!StrBuf_putsn(s, w, cgroup + strlen(str_user_slice_prefix), sliceNameLen)) + return false; - cgroup += strlen(str_user_slice_prefix); - while(cgroup < sliceSpec) { - *buf++ = *cgroup++; - bufsize--; - } - cgroup = userSliceSlash; + if (!w(s, ']')) + return false; - *buf++ = ']'; - bufsize--; + cgroup = userSliceSlash; continue; } if (labelLen > strlen(str_slice_suffix) && String_startsWith(nextSlash - strlen(str_slice_suffix), str_slice_suffix)) { const size_t sliceNameLen = labelLen - strlen(str_slice_suffix); - if (bufsize < 2 + sliceNameLen) - return false; - *buf++ = '['; - bufsize--; + if (!w(s, '[')) + return false; - for(size_t i = sliceNameLen; i; i--) { - *buf++ = *cgroup++; - bufsize--; - } + if (!StrBuf_putsn(s, w, cgroup, sliceNameLen)) + return false; - *buf++ = ']'; - bufsize--; + if (!w(s, ']')) + return false; cgroup = nextSlash; @@ -144,48 +149,34 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf if (String_startsWith(cgroup, str_lxc_payload_prefix)) { const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix); - if (bufsize < 6 + cgroupNameLen) + + if (!StrBuf_putsz(s, w, "[lxc:")) return false; - *buf++ = '['; - *buf++ = 'l'; - *buf++ = 'x'; - *buf++ = 'c'; - *buf++ = ':'; - bufsize -= 5; - - cgroup += strlen(str_lxc_payload_prefix); - while(cgroup < nextSlash) { - *buf++ = *cgroup++; - bufsize--; - } + if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen)) + return false; - *buf++ = ']'; - bufsize--; + if (!w(s, ']')) + return false; + + cgroup = nextSlash; continue; } if (String_startsWith(cgroup, str_lxc_monitor_prefix)) { const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix); - if (bufsize < 6 + cgroupNameLen) + + if (!StrBuf_putsz(s, w, "[LXC:")) return false; - *buf++ = '['; - *buf++ = 'L'; - *buf++ = 'X'; - *buf++ = 'C'; - *buf++ = ':'; - bufsize -= 5; - - cgroup += strlen(str_lxc_monitor_prefix); - while(cgroup < nextSlash) { - *buf++ = *cgroup++; - bufsize--; - } + if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_monitor_prefix), cgroupNameLen)) + return false; - *buf++ = ']'; - bufsize--; + if (!w(s, ']')) + return false; + + cgroup = nextSlash; continue; } @@ -202,14 +193,9 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf continue; } - if (bufsize < serviceNameLen) + if (!StrBuf_putsn(s, w, cgroup, serviceNameLen)) return false; - for(size_t i = serviceNameLen; i; i--) { - *buf++ = *cgroup++; - bufsize--; - } - cgroup = nextSlash; continue; @@ -220,66 +206,70 @@ static bool CGroup_filterName_internal(const char *cgroup, char* buf, size_t buf if (String_startsWith(cgroup, str_nspawn_scope_prefix)) { const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix); - if (bufsize < 6 + machineScopeNameLen) - return false; const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label); - *buf++ = '['; - *buf++ = is_monitor ? 'S' : 's'; - *buf++ = is_monitor ? 'N' : 'n'; - *buf++ = is_monitor ? 'C' : 'c'; - *buf++ = ':'; - bufsize -= 5; + if (!StrBuf_putsz(s, w, is_monitor ? "[SNC:" : "[snc:")) + return false; - cgroup += strlen(str_nspawn_scope_prefix); - for(size_t i = machineScopeNameLen; i; i--) { - *buf++ = *cgroup++; - bufsize--; - } + if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen)) + return false; - *buf++ = ']'; - bufsize--; + if (!w(s, ']')) + return false; cgroup = nextSlash; + if (String_startsWith(nextSlash, str_nspawn_monitor_label)) + cgroup += strlen(str_nspawn_monitor_label); + else if (String_startsWith(nextSlash, str_nspawn_payload_label)) + cgroup += strlen(str_nspawn_payload_label); continue; } - if (bufsize < 1 + scopeNameLen) + if (!w(s, '!')) return false; - *buf++ = '!'; - bufsize--; - - for(size_t i = scopeNameLen; i; i--) { - *buf++ = *cgroup++; - bufsize--; - } + if (!StrBuf_putsn(s, w, cgroup, scopeNameLen)) + return false; cgroup = nextSlash; continue; } -handle_default: // Default behavior: Copy the full label cgroup = labelStart; - if (bufsize < (size_t)(nextSlash - cgroup)) + if (!StrBuf_putsn(s, w, cgroup, labelLen)) return false; - while(cgroup < nextSlash) { - *buf++ = *cgroup++; - bufsize--; - } + cgroup = nextSlash; } return true; } -bool CGroup_filterName(const char *cgroup, char* buf, size_t bufsize) { - memset(buf, 0, bufsize); +char* CGroup_filterName(const char *cgroup) { + StrBuf_state s = { + .buf = NULL, + .size = 0, + .pos = 0, + }; + + if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) { + return NULL; + } + + s.buf = xCalloc(s.pos + 1, sizeof(char)); + s.size = s.pos; + s.pos = 0; + + if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_write)) { + free(s.buf); + return NULL; + } - return CGroup_filterName_internal(cgroup, buf, bufsize - 1); + s.buf[s.size] = '\0'; + return s.buf; } diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h index 3df428e4c..db2df7f43 100644 --- a/linux/CGroupUtils.h +++ b/linux/CGroupUtils.h @@ -11,6 +11,6 @@ in the source distribution for its full text. #include -bool CGroup_filterName(const char *cgroup, char* buf, size_t bufsize); +char* CGroup_filterName(const char *cgroup); #endif /* HEADER_CGroupUtils */ diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 74566ab87..3bfe7db50 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -905,14 +905,14 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t if (!changed) return; - char* cgroup_short = xCalloc(strlen(process->cgroup) + 1, 1); - if (CGroup_filterName(process->cgroup, cgroup_short, strlen(process->cgroup) + 1)) { + char* cgroup_short = CGroup_filterName(process->cgroup); + if (cgroup_short) { free_and_xStrdup(&process->cgroup_short, cgroup_short); + free(cgroup_short); } else { free(process->cgroup_short); process->cgroup_short = NULL; } - free(cgroup_short); } #ifdef HAVE_VSERVER From fc49796afa81b79930321c8d8c30c43e19380a58 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 21 Nov 2021 19:23:52 +0100 Subject: [PATCH 5/7] Document CCGROUP column --- htop.1.in | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/htop.1.in b/htop.1.in index 702f7c232..49c4a52d9 100644 --- a/htop.1.in +++ b/htop.1.in @@ -488,7 +488,23 @@ The I/O rate of write(2) in bytes per second, for the process. The I/O rate, IO_READ_RATE + IO_WRITE_RATE (see above). .TP .B CGROUP -Which cgroup the process is in. +Which cgroup the process is in. For a shortened view see the CCGROUP column below. +.TP +.B CCGROUP +Shortened view of the cgroup name that the process is in. +This performs some pattern-based replacements to shorten the displayed string and thus condense the information. + \fB/*.slice\fR is shortened to \fB/[*]\fR (exceptions below) + \fB/system.slice\fR is shortened to \fB/[S]\fR + \fB/user.slice\fR is shortened to \fB/[U]\fR + \fB/user-*.slice\fR is shortened to \fB/[U:*]\fR (directly preceeding \fB/[U]\fR before dropped) + \fB/machine.slice\fR is shortened to \fB/[M]\fR + \fB/machine-*.scope\fR is shortened to \fB/[SNC:*]\fR (SNC: systemd nspawn container), uppercase for the monitor + \fB/lxc.monitor.*\fR is shortened to \fB/[LXC:*]\fR + \fB/lxc.payload.*\fR is shortened to \fB/[lxc:*]\fR + \fB/*.scope\fR is shortened to \fB/!*\fR + \fB/*.service\fR is shortened to \fB/*\fR (suffix removed) + +Encountered escape sequences (e.g. from systemd) inside the cgroup name are not decoded. .TP .B OOM OOM killer score. From 5c3cf78f0e4cab72dd20419ad7c625fa250bbe07 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 21 Nov 2021 19:57:44 +0100 Subject: [PATCH 6/7] Use helpers for parsing of cgroup labels --- linux/CGroupUtils.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c index 5d9d20273..1c8b05ff2 100644 --- a/linux/CGroupUtils.c +++ b/linux/CGroupUtils.c @@ -48,6 +48,18 @@ static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) { return true; } +static bool Label_checkEqual(const char* labelStart, size_t labelLen, const char* expected) { + return labelLen == strlen(expected) && String_startsWith(labelStart, expected); +} + +static bool Label_checkPrefix(const char* labelStart, size_t labelLen, const char* expected) { + return labelLen > strlen(expected) && String_startsWith(labelStart, expected); +} + +static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const char* expected) { + return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected); +} + static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrBuf_putc_t w) { const char* str_slice_suffix = ".slice"; const char* str_system_slice = "system.slice"; @@ -80,7 +92,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB const char* nextSlash = strchrnul(labelStart, '/'); const size_t labelLen = nextSlash - labelStart; - if (labelLen == strlen(str_system_slice) && String_startsWith(cgroup, str_system_slice)) { + if (Label_checkEqual(labelStart, labelLen, str_system_slice)) { cgroup = nextSlash; if (!StrBuf_putsz(s, w, "[S]")) @@ -89,7 +101,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (labelLen == strlen(str_machine_slice) && String_startsWith(cgroup, str_machine_slice)) { + if (Label_checkEqual(labelStart, labelLen, str_machine_slice)) { cgroup = nextSlash; if (!StrBuf_putsz(s, w, "[M]")) @@ -98,7 +110,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (labelLen == strlen(str_user_slice) && String_startsWith(cgroup, str_user_slice)) { + if (Label_checkEqual(labelStart, labelLen, str_user_slice)) { cgroup = nextSlash; if (!StrBuf_putsz(s, w, "[U]")) @@ -130,7 +142,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (labelLen > strlen(str_slice_suffix) && String_startsWith(nextSlash - strlen(str_slice_suffix), str_slice_suffix)) { + if (Label_checkSuffix(labelStart, labelLen, str_slice_suffix)) { const size_t sliceNameLen = labelLen - strlen(str_slice_suffix); if (!w(s, '[')) @@ -147,7 +159,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (String_startsWith(cgroup, str_lxc_payload_prefix)) { + if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) { const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix); if (!StrBuf_putsz(s, w, "[lxc:")) @@ -164,7 +176,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (String_startsWith(cgroup, str_lxc_monitor_prefix)) { + if (Label_checkPrefix(labelStart, labelLen, str_lxc_monitor_prefix)) { const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix); if (!StrBuf_putsz(s, w, "[LXC:")) @@ -181,7 +193,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (labelLen > strlen(str_service_suffix) && String_startsWith(nextSlash - strlen(str_service_suffix), str_service_suffix)) { + if (Label_checkSuffix(labelStart, labelLen, str_service_suffix)) { const size_t serviceNameLen = labelLen - strlen(str_service_suffix); if (String_startsWith(cgroup, "user@")) { @@ -201,10 +213,10 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } - if (labelLen > strlen(str_scope_suffix) && String_startsWith(nextSlash - strlen(str_scope_suffix), str_scope_suffix)) { + if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) { const size_t scopeNameLen = labelLen - strlen(str_scope_suffix); - if (String_startsWith(cgroup, str_nspawn_scope_prefix)) { + if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) { const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix); const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label); From 10fef9364712a1a50daebe259603e7776ed968dc Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 28 Nov 2021 20:08:45 +0100 Subject: [PATCH 7/7] Include support for legacy LXC cgroup naming --- linux/CGroupUtils.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c index 1c8b05ff2..6f3b6fe7e 100644 --- a/linux/CGroupUtils.c +++ b/linux/CGroupUtils.c @@ -67,6 +67,8 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB const char* str_machine_slice = "machine.slice"; const char* str_user_slice_prefix = "/user-"; + const char* str_lxc_monitor_legacy = "lxc.monitor"; + const char* str_lxc_payload_legacy = "lxc.payload"; const char* str_lxc_monitor_prefix = "lxc.monitor."; const char* str_lxc_payload_prefix = "lxc.payload."; @@ -193,6 +195,34 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB continue; } + // LXC legacy cgroup naming + if (Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy) || + Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) { + bool isMonitor = Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy); + + labelStart = nextSlash; + while (*labelStart == '/') + labelStart++; + + nextSlash = strchrnul(labelStart, '/'); + if (nextSlash - labelStart > 0) { + if (!StrBuf_putsz(s, w, isMonitor ? "[LXC:" : "[lxc:")) + return false; + + if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart)) + return false; + + if (!w(s, ']')) + return false; + + cgroup = nextSlash; + continue; + } + + labelStart = cgroup; + nextSlash = labelStart + labelLen; + } + if (Label_checkSuffix(labelStart, labelLen, str_service_suffix)) { const size_t serviceNameLen = labelLen - strlen(str_service_suffix);