Initial version of a crash module which can be used to show which cgroups is a process member of. Signed-off-by: Nikolay Borisov <n.borisov.lkml@xxxxxxxxx> --- So here is the second version of the proccgroup module. Changes since v1: * Now show the full path to the cgroup (limited to 4k long paths). * Added support for passing either pid or hex address of task struct, so that cgroup info can be acquired for an arbitrary task * Added support for pre-3.15 kernels * Removed leftovers from the echo module extensions/proccgroup.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 extensions/proccgroup.c diff --git a/extensions/proccgroup.c b/extensions/proccgroup.c new file mode 100644 index 0000000..aee735b --- /dev/null +++ b/extensions/proccgroup.c @@ -0,0 +1,278 @@ +/* + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Nikolay Borisov <n.borisov.lkml@xxxxxxxxx> + */ + +#include <stdbool.h> +#include "defs.h" + +#define MAX_CGROUP_PATH 4096 + +static void showcgrp(void); +char *help_proc_cgroups[]; + +static struct command_table_entry command_table[] = { + { "showcg", showcgrp, help_proc_cgroups, 0}, + { NULL }, +}; + + +void __attribute__((constructor)) +proccgroup_init(void) +{ + + if (!MEMBER_EXISTS("task_struct", "cgroups") || + (!MEMBER_EXISTS("cgroup", "kn") && !MEMBER_EXISTS("cgroup", "name"))) + { + fprintf(fp, "Unrecognised or disabled cgroup support\n"); + return; + } + + register_extension(command_table); +} + +void __attribute__((destructor)) +proccgroup_finish(void) { } + +/* Prepends contents of cgroup_name to buf, using start as a pointer + * index into buf + */ +static void prepend_string(char *buf, char **start, char *cgroup_name) { + + int len = strlen(cgroup_name); + *start -= len; + + if (*start < buf) { + error(FATAL, "Cgroup too long to parse\n"); + } + + memcpy(*start, cgroup_name, len); + + if (--*start < buf) { + error(FATAL, "Cgroup too long to parse\n"); + } + + **start = '/'; +} + +/* For post-3.15 kernels */ +static void get_cgroup_name_kn(ulong cgroup, char *buf, int buflen) +{ + ulong kernfs_node; + ulong cgroup_name_ptr; + ulong kernfs_parent; + bool slash_prepended = false; + char cgroup_name[BUFSIZE]; + char *start = buf + buflen - 1; + *start = '\0'; //null terminate the end + + /* Get cgroup->kn */ + readmem(cgroup + MEMBER_OFFSET("cgroup", "kn"), KVADDR, &kernfs_node, sizeof(void *), + "cgroup->kn", FAULT_ON_ERROR); + + do { + /* Get kn->name */ + readmem(kernfs_node + MEMBER_OFFSET("kernfs_node", "name"), KVADDR, &cgroup_name_ptr, sizeof(void *), + "kernfs_node->name", FAULT_ON_ERROR); + /* Get kn->parent */ + readmem(kernfs_node + MEMBER_OFFSET("kernfs_node", "parent"), KVADDR, &kernfs_parent, sizeof(void *), + "kernfs_node->parent", FAULT_ON_ERROR); + + if (kernfs_parent != 0) { + read_string(cgroup_name_ptr, cgroup_name, BUFSIZE-1); + prepend_string(buf, &start, cgroup_name); + slash_prepended = true; + } else if (!slash_prepended) { + if (--start < buf) { + error(FATAL, "Cgroup too long to parse\n"); + } + *start = '/'; + } + + kernfs_node = kernfs_parent; + + } while(kernfs_parent); + + memmove(buf, start, buf + buflen - start); +} + +/* For pre-3.15 kernels */ +static void get_cgroup_name_old(ulong cgroup, char *buf, size_t buflen) +{ + ulong cgroup_name_ptr; + ulong cgroup_parent_ptr; + char cgroup_name[BUFSIZE]; + char *start = buf + buflen - 1; + *start = '\0'; //null terminate the end + bool slash_prepended = false; + + do { + /* Get cgroup->name */ + readmem(cgroup + MEMBER_OFFSET("cgroup", "name"), KVADDR, &cgroup_name_ptr, sizeof(void *), + "cgroup->name", FAULT_ON_ERROR); + /* Get cgroup->parent */ + readmem(cgroup + MEMBER_OFFSET("cgroup", "parent"), KVADDR, &cgroup_parent_ptr, sizeof(void *), + "cgroup->parent", FAULT_ON_ERROR); + + read_string(cgroup_name_ptr + MEMBER_OFFSET("cgroup_name", "name"), cgroup_name, BUFSIZE-1); + + if (cgroup_parent_ptr) { + prepend_string(buf, &start, cgroup_name); + slash_prepended = true; + } else if (!slash_prepended) { + if (--start < buf) + break; + *start = '/'; + } + + cgroup = cgroup_parent_ptr; + + } while(cgroup_parent_ptr); + + memmove(buf, start, buf + buflen - start); +} + +static void get_subsys_name(ulong subsys, char *buf, size_t buflen) +{ + ulong subsys_name_ptr; + ulong cgroup_subsys_ptr; + + /* Get cgroup->kn */ + readmem(subsys + MEMBER_OFFSET("cgroup_subsys_state", "ss"), KVADDR, &cgroup_subsys_ptr, sizeof(void *), + "cgroup_subsys_state->ss", FAULT_ON_ERROR); + + readmem(cgroup_subsys_ptr + MEMBER_OFFSET("cgroup_subsys", "name"), KVADDR, &subsys_name_ptr, sizeof(void *), + "cgroup_subsys->name", FAULT_ON_ERROR); + read_string(subsys_name_ptr, buf, buflen-1); +} + +static void get_cgroup_name(ulong cgroup, ulong subsys) +{ + char *cgroup_path = GETBUF(MAX_CGROUP_PATH); + char subsys_name[BUFSIZE]; + + /* Handle the 2 cases of cgroup_name and the kernfs one */ + if (MEMBER_EXISTS("cgroup", "kn")) { + get_cgroup_name_kn(cgroup, cgroup_path, MAX_CGROUP_PATH); + } else if (MEMBER_EXISTS("cgroup", "name")) { + get_cgroup_name_old(cgroup, cgroup_path, MAX_CGROUP_PATH); + } + + get_subsys_name(subsys, subsys_name, BUFSIZE); + + fprintf(fp, "subsys: %-20s cgroup: %s\n", subsys_name, cgroup_path); + + FREEBUF(cgroup_path); +} + + +void show_proc_cgroups(ulong task_ctx) { + int en_subsys_cnt; + int i; + ulong *cgroup_subsys_arr; + ulong subsys_base_ptr; + ulong cgroups_subsys_ptr = 0; + + + /* Get address of task_struct->cgroups */ + readmem(task_ctx + MEMBER_OFFSET("task_struct", "cgroups"), + KVADDR, &cgroups_subsys_ptr, sizeof(void *), + "task_struct->cgroups", FAULT_ON_ERROR); + + subsys_base_ptr = cgroups_subsys_ptr + MEMBER_OFFSET("css_set", "subsys"); + en_subsys_cnt = MEMBER_SIZE("css_set", "subsys") / sizeof(void *); + cgroup_subsys_arr = (ulong *)GETBUF(en_subsys_cnt * sizeof(ulong)); + + /* Get the contents of the css_set->subsys array */ + readmem(subsys_base_ptr, KVADDR, cgroup_subsys_arr, sizeof(ulong) * en_subsys_cnt, + "css_set->subsys", FAULT_ON_ERROR); + + for (i = 0; i < en_subsys_cnt; i++) { + ulong cgroup; + + /* Get cgroup_subsys_state -> cgroup */ + readmem(cgroup_subsys_arr[i] + MEMBER_OFFSET("cgroup_subsys_state", "cgroup"), + KVADDR, &cgroup, sizeof(void *), "cgroup_subsys_state->cgroup", FAULT_ON_ERROR); + + get_cgroup_name(cgroup, cgroup_subsys_arr[i]); + } + + FREEBUF(cgroup_subsys_arr); +} + + +static void showcgrp(void) { + + ulong value; + struct task_context *tc; + ulong task_struct_ptr = 0; + + while (args[++optind]) { + if (IS_A_NUMBER(args[optind])) { + switch (str_to_context(args[optind], &value, &tc)) + { + case STR_PID: + task_struct_ptr = tc->task; + ++optind; + break; + + case STR_TASK: + task_struct_ptr = value; + ++optind; + break; + + case STR_INVALID: + error(FATAL, "invalid task or pid value: %s\n\n", + args[optind]); + break; + } + } else { + if (argcnt > 1) + error(FATAL, "invalid task or pid value: %s\n",args[optind]); + else + break; + } + } + + if (!task_struct_ptr) { + task_struct_ptr = CURRENT_TASK(); + } + + show_proc_cgroups(task_struct_ptr); +} + +char *help_proc_cgroups[] = { + "showcg", + "Show which cgroups is a process member of", + " [task | pid]", + + " This command prints the cgroup for each subsys that a process is a member of", + "\nExample", + " Show the cgroup for the currently active process:\n", + " crash> showcg", + " subsys: cpuset cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: cpu cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: cpuacct cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: blkio cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: memory cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: devices cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: freezer cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: net_cls cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: perf_event cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: net_prio cgroup: /user.slice/user-1000.slice/session-c1.scope", + " subsys: hugetlb cgroup: /user.slice/user-1000.slice/session-c1.scope", + "\n Alternatively you can pass either a pid or a task pointer to show the cgroup the", + " respective process is a member of e.g:\n", + " crash> showcg 1064\n OR", + " crash> showcg ffff880405711b80", + + + + NULL +}; + + -- 2.5.0 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility