diff --git a/src/cgutil.c b/src/cgutil.c index b71bdbf1..d334ef00 100644 --- a/src/cgutil.c +++ b/src/cgutil.c @@ -32,6 +32,7 @@ #include #include /* 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 diff --git a/src/cgutil.h b/src/cgutil.h index 614809fa..661830f1 100644 --- a/src/cgutil.h +++ b/src/cgutil.h @@ -27,16 +27,45 @@ #include #include +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_ */ diff --git a/src/initctl.c b/src/initctl.c index f6d200bd..0433a85c 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -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 [: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 }, diff --git a/src/util.c b/src/util.c index 745e6d2a..67a240f5 100644 --- a/src/util.c +++ b/src/util.c @@ -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; }