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
2021-04-06 12:03:20 +02:00
parent 02936de993
commit d59f55b90c
4 changed files with 231 additions and 77 deletions
+152 -53
View File
@@ -32,6 +32,7 @@
#include <lite/lite.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include "cgutil.h"
#include "initctl.h"
#define CDIM plain ? "" : "\e[2m"
@@ -44,18 +45,6 @@
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 *list;
@@ -129,28 +118,49 @@ char *pid_cgroup(int pid, char *buf, size_t len)
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 buf[42];
char *val = NULL;
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (!fp)
return val;
if (fp) {
if (fgets(buf, len, fp)) {
val = chomp(buf);
len = strcspn(val, " \t");
val[len] = 0;
}
if (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
val = strtoull(buf, NULL, 10);
fclose(fp);
}
fclose(fp);
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)
@@ -160,22 +170,49 @@ static uint64_t cgroup_memuse(struct cg *cg)
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) {
cg->cg_rss = 0;
cg->cg_vmlib = 0;
while (fgets(buf, sizeof(buf), fp)) {
chomp(buf);
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;
}
if (!strncmp(buf, "file", 4)) {
cg->cg_vmlib = strtoull(&buf[5], NULL, 10);
break; /* done */
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
if (!strncmp(buf, "file_mapped", 11)) {
cg->cg_vmlib += strtoull(&buf[5], NULL, 10);
continue;
}
}
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");
}
@@ -185,7 +222,6 @@ uint64_t cgroup_memory(char *group)
char path[256];
paste(path, sizeof(path), FINIT_CGPATH, group);
printf("DBG: %s (%s)\n", path, group);
return cgroup_uint64(path, "memory.current");
}
@@ -267,7 +303,8 @@ static struct cg *find(char *path)
return append(path);
}
static struct cg *cg_update(char *path)
/* update stats */
struct cg *cg_stats(char *path)
{
struct cg *cg;
@@ -281,6 +318,21 @@ static struct cg *cg_update(char *path)
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)
{
/* Skip current dir ".", and prev dir "..", from list of files */
@@ -297,7 +349,7 @@ static int cgroup_filter(const struct dirent *entry)
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;
char s[32], r[32], l[32];
@@ -312,7 +364,7 @@ int cgroup_tree(char *path, char *pfx, int top)
int i, n;
int num;
if (top >= screen_rows)
if (pos >= screen_rows)
return 0;
if (-1 == lstat(path, &st))
@@ -332,15 +384,26 @@ int cgroup_tree(char *path, char *pfx, int top)
if (!pfx) {
pfx = "";
if (top) {
cg = cg_update(path);
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
switch (mode) {
case 1:
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_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load, path);
} else
cg->cg_memshare, cg->cg_load, path);
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);
break;
}
puts(row);
}
@@ -349,38 +412,47 @@ int cgroup_tree(char *path, char *pfx, int top)
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80], tmp[42];
char prefix[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top) {
cg = cg_update(buf);
switch (mode) {
case 1:
cg = cg_stats(buf);
snprintf(row, rplen,
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load);
} else
cg->cg_memshare, cg->cg_load);
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;
break;
}
strlcat(row, pfx, rplen);
if (i + 1 == n) {
strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
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, nm, rplen);
strlcat(row, "/ ", rplen);
snprintf(tmp, sizeof(tmp), "[cpu.weight: %d]", cgroup_weight(buf));
strlcat(row, tmp, rplen);
puts(row);
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
rc += cgroup_tree(buf, prefix, mode, pos + i);
free(namelist[i]);
}
@@ -406,10 +478,17 @@ int cgroup_tree(char *path, char *pfx, int top)
char proc[screen_cols];
int len;
if (top)
switch (mode) {
case 1:
snprintf(row, rplen, "%37s", " ");
else
break;
case 2:
snprintf(row, rplen, " --.-- [ ] [ ] ");
break;
default:
row[0] = 0;
break;
}
strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen);
@@ -432,9 +511,9 @@ int cgroup_tree(char *path, char *pfx, int top)
puts(row);
}
if (top) {
top += i;
if (top >= screen_rows)
if (mode == 1) {
pos += i;
if (pos >= screen_rows)
break;
}
}
@@ -458,13 +537,19 @@ int show_cgroup(char *arg)
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)
{
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1);
cgroup_tree(arg, NULL, 1, 0);
}
int show_cgtop(char *arg)
@@ -493,6 +578,20 @@ int show_cgtop(char *arg)
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:
* indent-tabs-mode: t
+30 -1
View File
@@ -27,16 +27,45 @@
#include <stdint.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_comm (int pid, char *buf, size_t len);
char *pid_cgroup (int pid, char *buf, size_t len);
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_cgtop (char *arg);
int show_cgps (char *arg);
#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;
}
static char *status(svc_t *svc)
static char *status(svc_t *svc, int pretty)
{
static char buf[25];
int bold = !plain;
const char *color;
char *s;
s = svc_status(svc);
if (svc->state != SVC_HALTED_STATE)
bold = 0;
switch (svc->state) {
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",
bold ? "\e[1m" : "", s, bold ? "\e[0m" : "");
color ? color : "", s, color ? "\e[0m" : "");
return buf;
}
@@ -640,7 +654,7 @@ static void show_cgroup_tree(char *group, char *pfx)
strlcpy(path, FINIT_CGPATH, sizeof(path));
strlcat(path, group, sizeof(path));
cgroup_tree(path, pfx, 0);
cgroup_tree(path, pfx, 0, 0);
}
static int show_status(char *arg)
@@ -654,6 +668,9 @@ static int show_status(char *arg)
if (arg && arg[0]) {
long now = jiffies();
char uptm[42] = "N/A";
char grbuf[128];
char path[256];
struct cg *cg;
char *group;
svc = client_svc_find(arg);
@@ -663,23 +680,30 @@ static int show_status(char *arg)
if (quiet)
return svc->state != SVC_RUNNING_STATE;
printf("Status : %s\n", status(svc));
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf));
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("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("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf("PID file : %s\n", svc->pidfile);
printf("PID : %d\n", svc->pid);
printf("User : %s\n", svc->username);
printf("Group : %s\n", svc->group);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
group = pid_cgroup(svc->pid, buf, sizeof(buf));
printf("Memory; : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
printf("CGroup : %s\n", group);
show_cgroup_tree(group, " ");
printf(" Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf(" PID file : %s\n", svc->pidfile);
printf(" PID : %d\n", svc->pid);
printf(" User : %s\n", svc->username);
printf(" Group : %s\n", svc->group);
printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
if (svc->pid > 1) {
printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
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");
return do_log(svc->cmd);
@@ -705,7 +729,7 @@ static int show_status(char *arg)
printf("%-*d ", pw, svc->pid);
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);
if (strchr(lvls, '\e'))
@@ -821,6 +845,7 @@ static int usage(int rc)
" status <NAME>[:ID] Show service status, by name\n"
" status Show status of services, default command\n"
"\n"
" cgroup List cgroup config overview\n"
" ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n"
"\n"
@@ -938,7 +963,8 @@ int main(int argc, char *argv[])
{ "stop", NULL, do_stop },
{ "restart", NULL, do_restart },
{ "ps", NULL, show_cgroup },
{ "cgroup", NULL, show_cgroup },
{ "ps", NULL, show_cgps },
{ "top", NULL, show_cgtop },
{ "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;
if (!sz) {
strlcpy(buf, "-.-", len);
strlcpy(buf, "--.--", len);
return buf;
}