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(a)gmail.com>
---
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(a)gmail.com>
+ */
+
+#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