Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down
18 changes: 17 additions & 1 deletion htop.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
317 changes: 317 additions & 0 deletions linux/CGroupUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
/*
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"


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 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";
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_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.";

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) {
if ('/' == *cgroup) {
while ('/' == *cgroup)
cgroup++;

if (!w(s, '/'))
return false;

continue;
}

const char* labelStart = cgroup;
const char* nextSlash = strchrnul(labelStart, '/');
const size_t labelLen = nextSlash - labelStart;

if (Label_checkEqual(labelStart, labelLen, str_system_slice)) {
cgroup = nextSlash;

if (!StrBuf_putsz(s, w, "[S]"))
return false;

continue;
}

if (Label_checkEqual(labelStart, labelLen, str_machine_slice)) {
cgroup = nextSlash;

if (!StrBuf_putsz(s, w, "[M]"))
return false;

continue;
}

if (Label_checkEqual(labelStart, labelLen, str_user_slice)) {
cgroup = nextSlash;

if (!StrBuf_putsz(s, w, "[U]"))
return false;

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));

s->pos--;
if (!w(s, ':'))
return false;

if (!StrBuf_putsn(s, w, cgroup + strlen(str_user_slice_prefix), sliceNameLen))
return false;

if (!w(s, ']'))
return false;

cgroup = userSliceSlash;

continue;
}

if (Label_checkSuffix(labelStart, labelLen, str_slice_suffix)) {
const size_t sliceNameLen = labelLen - strlen(str_slice_suffix);

if (!w(s, '['))
return false;

if (!StrBuf_putsn(s, w, cgroup, sliceNameLen))
return false;

if (!w(s, ']'))
return false;

cgroup = nextSlash;

continue;
}

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:"))
return false;

if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
return false;

if (!w(s, ']'))
return false;

cgroup = nextSlash;

continue;
}

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:"))
return false;

if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_monitor_prefix), cgroupNameLen))
return false;

if (!w(s, ']'))
return false;

cgroup = nextSlash;

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);

if (String_startsWith(cgroup, "user@")) {
cgroup = nextSlash;

while(*cgroup == '/')
cgroup++;

continue;
}

if (!StrBuf_putsn(s, w, cgroup, serviceNameLen))
return false;

cgroup = nextSlash;

continue;
}

if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);

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);

if (!StrBuf_putsz(s, w, is_monitor ? "[SNC:" : "[snc:"))
return false;

if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
return false;

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 (!w(s, '!'))
return false;

if (!StrBuf_putsn(s, w, cgroup, scopeNameLen))
return false;

cgroup = nextSlash;

continue;
}

// Default behavior: Copy the full label
cgroup = labelStart;

if (!StrBuf_putsn(s, w, cgroup, labelLen))
return false;

cgroup = nextSlash;
}

return true;
}

char* CGroup_filterName(const char *cgroup) {
StrBuf_state s = {
.buf = NULL,
.size = 0,
.pos = 0,
};

if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) {
Comment thread
BenBE marked this conversation as resolved.
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;
}

s.buf[s.size] = '\0';
return s.buf;
}
16 changes: 16 additions & 0 deletions linux/CGroupUtils.h
Original file line number Diff line number Diff line change
@@ -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 <stdbool.h>
#include <stddef.h>


char* CGroup_filterName(const char *cgroup);

#endif /* HEADER_CGroupUtils */
Loading