initctl: new command 'cgroup' to show cgroup config

This is intended to be used with the 'top' command to aid in setting up
and verifying proper limits for services.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2021-04-06 12:03:20 +02:00
1 parent 02936de993
commit d59f55b90c
4 files changed
+231 -77

No files matched your search

+152 -53
View File
@@ -32,6 +32,7 @@
#include <lite/lite.h> #include <lite/lite.h>
#include <sys/sysinfo.h> /* sysinfo() */ #include <sys/sysinfo.h> /* sysinfo() */
#include "cgutil.h"
#include "initctl.h" #include "initctl.h"
#define CDIM plain ? "" : "\e[2m" #define CDIM plain ? "" : "\e[2m"
@@ -44,18 +45,6 @@
uint64_t total_ram; /* From sysinfo() */ uint64_t total_ram; /* From sysinfo() */
struct cg {
struct cg *cg_next;
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.usage_in_bytes */
float cg_mem; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
};
struct cg dummy; /* empty result "NULL" */ struct cg dummy; /* empty result "NULL" */
struct cg *list; struct cg *list;
@@ -129,28 +118,49 @@ char *pid_cgroup(int pid, char *buf, size_t len)
return ptr; return ptr;
} }
static uint64_t cgroup_uint64(char *path, char *file) static char *cgroup_val(char *path, char *file, char *buf, size_t len)
{ {
uint64_t val = 0; char *val = NULL;
char buf[42];
FILE *fp; FILE *fp;
fp = fopenf("r", "%s/%s", path, file); fp = fopenf("r", "%s/%s", path, file);
if (!fp) if (fp) {
return val; if (fgets(buf, len, fp)) {
val = chomp(buf);
len = strcspn(val, " \t");
val[len] = 0;
}
if (fgets(buf, sizeof(buf), fp)) { fclose(fp);
chomp(buf);
val = strtoull(buf, NULL, 10);
} }
fclose(fp);
return val; return val;
} }
static int cgroup_weight(char *path) static uint64_t cgroup_uint64(char *path, char *file)
{ {
return (int)cgroup_uint64(path, "cpu.weight"); uint64_t val = 0;
char buf[42];
if (cgroup_val(path, file, buf, sizeof(buf)))
val = strtoull(buf, NULL, 10);
return val;
}
static char *cgroup_memval(char *path, char *file, char *buf, size_t len)
{
char data[42];
if (cgroup_val(path, file, data, sizeof(data))) {
if (!strcmp(data, "max"))
strlcpy(buf, data, len);
else
memsz(strtoull(data, NULL, 10), buf, len);
} else
buf[0] = 0;
return buf;
} }
static uint64_t cgroup_memuse(struct cg *cg) static uint64_t cgroup_memuse(struct cg *cg)
@@ -160,22 +170,49 @@ static uint64_t cgroup_memuse(struct cg *cg)
fp = fopenf("r", "%s/memory.stat", cg->cg_path); fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) { if (fp) {
cg->cg_rss = 0;
cg->cg_vmlib = 0;
while (fgets(buf, sizeof(buf), fp)) { while (fgets(buf, sizeof(buf), fp)) {
chomp(buf); chomp(buf);
if (!strncmp(buf, "anon", 4)) { if (!strncmp(buf, "anon", 4)) {
cg->cg_rss = strtoull(&buf[5], NULL, 10); cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "slab", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "kernel_stack", 12)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "pagetables", 10)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "percpu", 6)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "sock", 4)) {
cg->cg_rss += strtoull(&buf[5], NULL, 10);
continue; continue;
} }
if (!strncmp(buf, "file", 4)) { if (!strncmp(buf, "file", 4)) {
cg->cg_vmlib = strtoull(&buf[5], NULL, 10); cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
break; /* done */ continue;
}
if (!strncmp(buf, "file_mapped", 11)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
} }
} }
fclose(fp); fclose(fp);
} }
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram); cg->cg_memshare = (float)(cg->cg_rss * 100 / total_ram);
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current"); return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.current");
} }
@@ -185,7 +222,6 @@ uint64_t cgroup_memory(char *group)
char path[256]; char path[256];
paste(path, sizeof(path), FINIT_CGPATH, group); paste(path, sizeof(path), FINIT_CGPATH, group);
printf("DBG: %s (%s)\n", path, group);
return cgroup_uint64(path, "memory.current"); return cgroup_uint64(path, "memory.current");
} }
@@ -267,7 +303,8 @@ static struct cg *find(char *path)
return append(path); return append(path);
} }
static struct cg *cg_update(char *path) /* update stats */
struct cg *cg_stats(char *path)
{ {
struct cg *cg; struct cg *cg;
@@ -281,6 +318,21 @@ static struct cg *cg_update(char *path)
return cg; return cg;
} }
/* query config */
struct cg *cg_conf(char *path)
{
static struct cg cg;
cgroup_memval(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
return &cg;
}
static int cgroup_filter(const struct dirent *entry) static int cgroup_filter(const struct dirent *entry)
{ {
/* Skip current dir ".", and prev dir "..", from list of files */ /* Skip current dir ".", and prev dir "..", from list of files */
@@ -297,7 +349,7 @@ static int cgroup_filter(const struct dirent *entry)
return 1; return 1;
} }
int cgroup_tree(char *path, char *pfx, int top) int cgroup_tree(char *path, char *pfx, int mode, int pos)
{ {
struct dirent **namelist = NULL; struct dirent **namelist = NULL;
char s[32], r[32], l[32]; char s[32], r[32], l[32];
@@ -312,7 +364,7 @@ int cgroup_tree(char *path, char *pfx, int top)
int i, n; int i, n;
int num; int num;
if (top >= screen_rows) if (pos >= screen_rows)
return 0; return 0;
if (-1 == lstat(path, &st)) if (-1 == lstat(path, &st))
@@ -332,15 +384,26 @@ int cgroup_tree(char *path, char *pfx, int top)
if (!pfx) { if (!pfx) {
pfx = ""; pfx = "";
if (top) { switch (mode) {
cg = cg_update(path); case 1:
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]", cg = cg_stats(path);
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f %s",
memsz(cg->cg_vmsize, s, sizeof(s)), memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)), memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)), memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load, path); cg->cg_memshare, cg->cg_load, path);
} else break;
case 2:
cg = cg_conf(path);
snprintf(row, rplen, "%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
cg->cg_cpu.weight, cg->cg_cpu.max, path);
break;
default:
strlcpy(row, path, rplen); strlcpy(row, path, rplen);
break;
}
puts(row); puts(row);
} }
@@ -349,38 +412,47 @@ int cgroup_tree(char *path, char *pfx, int top)
if (n > 0) { if (n > 0) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name; char *nm = namelist[i]->d_name;
char prefix[80], tmp[42]; char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm); snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top) { switch (mode) {
cg = cg_update(buf); case 1:
cg = cg_stats(buf);
snprintf(row, rplen, snprintf(row, rplen,
" %6.6s %6.6s %6.6s %5.1f %5.1f ", " %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)), memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)), memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)), memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load); cg->cg_memshare, cg->cg_load);
} else break;
case 2:
cg = cg_conf(buf);
snprintf(row, rplen, "%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
memsz(cg->cg_vmsize, s, sizeof(s)),
cg->cg_mem.min, cg->cg_mem.max,
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
break;
default:
row[0] = 0; row[0] = 0;
break;
}
strlcat(row, pfx, rplen); strlcat(row, pfx, rplen);
if (i + 1 == n) { if (i + 1 == n) {
strlcat(row, END, rplen); strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx); snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else { } else {
strlcat(row, FORK, rplen); strlcat(row, FORK, rplen);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE); snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
} }
strlcat(row, " ", rplen); strlcat(row, " ", rplen);
strlcat(row, nm, rplen); strlcat(row, nm, rplen);
strlcat(row, "/ ", rplen); strlcat(row, "/ ", rplen);
snprintf(tmp, sizeof(tmp), "[cpu.weight: %d]", cgroup_weight(buf));
strlcat(row, tmp, rplen);
puts(row); puts(row);
rc += cgroup_tree(buf, prefix, top ? top + i : 0); rc += cgroup_tree(buf, prefix, mode, pos + i);
free(namelist[i]); free(namelist[i]);
} }
@@ -406,10 +478,17 @@ int cgroup_tree(char *path, char *pfx, int top)
char proc[screen_cols]; char proc[screen_cols];
int len; int len;
if (top) switch (mode) {
case 1:
snprintf(row, rplen, "%37s", " "); snprintf(row, rplen, "%37s", " ");
else break;
case 2:
snprintf(row, rplen, " --.-- [ ] [ ] ");
break;
default:
row[0] = 0; row[0] = 0;
break;
}
strlcat(row, pfx, rplen); strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen); strlcat(row, ++i == num ? END : FORK, rlen);
@@ -432,9 +511,9 @@ int cgroup_tree(char *path, char *pfx, int top)
puts(row); puts(row);
} }
if (top) { if (mode == 1) {
top += i; pos += i;
if (top >= screen_rows) if (pos >= screen_rows)
break; break;
} }
} }
@@ -458,13 +537,19 @@ int show_cgroup(char *arg)
arg = path; arg = path;
} }
return cgroup_tree(arg, NULL, 0); /* memory.current memory.min memory.max cpuset.cpus cpu.weight cpu.max */
if (heading)
print_header(" MEM [MIN MAX] CPU [WEIGHT MAX] GROUP");
return cgroup_tree(arg, NULL, 2, 0);
}
static void cgtop(uev_t *w, void *arg, int events) static void cgtop(uev_t *w, void *arg, int events)
{ {
fputs("\e[2J\e[1;1H", stdout); fputs("\e[2J\e[1;1H", stdout);
if (heading) if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP"); print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1); cgroup_tree(arg, NULL, 1, 0);
} }
int show_cgtop(char *arg) int show_cgtop(char *arg)
@@ -493,6 +578,20 @@ int show_cgtop(char *arg)
return uev_run(&ctx, 0); return uev_run(&ctx, 0);
} }
int show_cgps(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return cgroup_tree(arg, NULL, 0, 0);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+30 -1
View File
@@ -27,16 +27,45 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
struct cg {
struct cg *cg_next;
/* stats */
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.current */
float cg_memshare; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
/* config */
struct {
char min[32];
char max[32];
} cg_mem;
struct {
char set[16];
char weight[32];
char max[32];
} cg_cpu;
};
char *pid_cmdline (int pid, char *buf, size_t len); char *pid_cmdline (int pid, char *buf, size_t len);
char *pid_comm (int pid, char *buf, size_t len); char *pid_comm (int pid, char *buf, size_t len);
char *pid_cgroup (int pid, char *buf, size_t len); char *pid_cgroup (int pid, char *buf, size_t len);
uint64_t cgroup_memory(char *group); uint64_t cgroup_memory(char *group);
int cgroup_tree (char *path, char *pfx, int top); struct cg *cg_stats(char *path);
struct cg *cg_conf(char *path);
int cgroup_tree (char *path, char *pfx, int mode, int pos);
int show_cgroup (char *arg); int show_cgroup (char *arg);
int show_cgtop (char *arg); int show_cgtop (char *arg);
int show_cgps (char *arg);
#endif /* FINIT_CGUTIL_H_ */ #endif /* FINIT_CGUTIL_H_ */
+48 -22
View File
@@ -612,18 +612,32 @@ static char *svc_environ(svc_t *svc, char *buf, size_t len)
return buf; return buf;
} }
static char *status(svc_t *svc) static char *status(svc_t *svc, int pretty)
{ {
static char buf[25]; static char buf[25];
int bold = !plain; const char *color;
char *s; char *s;
s = svc_status(svc); s = svc_status(svc);
if (svc->state != SVC_HALTED_STATE) switch (svc->state) {
bold = 0; case SVC_HALTED_STATE:
color = "\e[1m";
break;
case SVC_RUNNING_STATE:
color = "\e[1;32m";
break;
default:
color = "\e[1;33m";
break;
}
if (!pretty || plain)
color = NULL;
snprintf(buf, sizeof(buf), "%s%-8.8s%s", snprintf(buf, sizeof(buf), "%s%-8.8s%s",
bold ? "\e[1m" : "", s, bold ? "\e[0m" : ""); color ? color : "", s, color ? "\e[0m" : "");
return buf; return buf;
} }
@@ -640,7 +654,7 @@ static void show_cgroup_tree(char *group, char *pfx)
strlcpy(path, FINIT_CGPATH, sizeof(path)); strlcpy(path, FINIT_CGPATH, sizeof(path));
strlcat(path, group, sizeof(path)); strlcat(path, group, sizeof(path));
cgroup_tree(path, pfx, 0); cgroup_tree(path, pfx, 0, 0);
} }
static int show_status(char *arg) static int show_status(char *arg)
@@ -654,6 +668,9 @@ static int show_status(char *arg)
if (arg && arg[0]) { if (arg && arg[0]) {
long now = jiffies(); long now = jiffies();
char uptm[42] = "N/A"; char uptm[42] = "N/A";
char grbuf[128];
char path[256];
struct cg *cg;
char *group; char *group;
svc = client_svc_find(arg); svc = client_svc_find(arg);
@@ -663,23 +680,30 @@ static int show_status(char *arg)
if (quiet) if (quiet)
return svc->state != SVC_RUNNING_STATE; return svc->state != SVC_RUNNING_STATE;
printf("Status : %s\n", status(svc)); group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf));
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident))); snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
cg = cg_conf(path);
printf(" Status : %s\n", status(svc, 1));
printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
printf("Description : %s\n", svc->desc); printf("Description : %s\n", svc->desc);
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in"); printf(" Origin : %s\n", svc->file[0] ? svc->file : "built-in");
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf))); printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf))); printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf))); printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf("PID file : %s\n", svc->pidfile); printf(" PID file : %s\n", svc->pidfile);
printf("PID : %d\n", svc->pid); printf(" PID : %d\n", svc->pid);
printf("User : %s\n", svc->username); printf(" User : %s\n", svc->username);
printf("Group : %s\n", svc->group); printf(" Group : %s\n", svc->group);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm); printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
group = pid_cgroup(svc->pid, buf, sizeof(buf)); if (svc->pid > 1) {
printf("Memory; : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm))); printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
printf("CGroup : %s\n", group); printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
show_cgroup_tree(group, " "); group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
cg->cg_mem.min, cg->cg_mem.max);
show_cgroup_tree(group, " ");
}
printf("\n"); printf("\n");
return do_log(svc->cmd); return do_log(svc->cmd);
@@ -705,7 +729,7 @@ static int show_status(char *arg)
printf("%-*d ", pw, svc->pid); printf("%-*d ", pw, svc->pid);
svc_ident(svc, ident, sizeof(ident)); svc_ident(svc, ident, sizeof(ident));
printf("%-*s %s ", iw, ident, status(svc)); printf("%-*s %s ", iw, ident, status(svc, 0));
lvls = runlevel_string(runlevel, svc->runlevels); lvls = runlevel_string(runlevel, svc->runlevels);
if (strchr(lvls, '\e')) if (strchr(lvls, '\e'))
@@ -821,6 +845,7 @@ static int usage(int rc)
" status <NAME>[:ID] Show service status, by name\n" " status <NAME>[:ID] Show service status, by name\n"
" status Show status of services, default command\n" " status Show status of services, default command\n"
"\n" "\n"
" cgroup List cgroup config overview\n"
" ps List processes based on cgroups\n" " ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n" " top Show top-like listing based on cgroups\n"
"\n" "\n"
@@ -938,7 +963,8 @@ int main(int argc, char *argv[])
{ "stop", NULL, do_stop }, { "stop", NULL, do_stop },
{ "restart", NULL, do_restart }, { "restart", NULL, do_restart },
{ "ps", NULL, show_cgroup }, { "cgroup", NULL, show_cgroup },
{ "ps", NULL, show_cgps },
{ "top", NULL, show_cgtop }, { "top", NULL, show_cgtop },
{ "runlevel", NULL, do_runlevel }, { "runlevel", NULL, do_runlevel },
+1 -1
View File
@@ -201,7 +201,7 @@ char *memsz(uint64_t sz, char *buf, size_t len)
int gb, mb, kb, b; int gb, mb, kb, b;
if (!sz) { if (!sz) {
strlcpy(buf, "-.-", len); strlcpy(buf, "--.--", len);
return buf; return buf;
} }