[V3] Add the proccgroup extension

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Dave,

So here is (hopefully) a final version of the proccgroup module. I've
fixed the null-terminated subsys array issue as well as an inverted
check for the ss member. Everything works as expected.

Many thanks for providing the coredump files.

Regards,
Nikolay
/*
 * 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 bool have_ss_member;

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")))
    {
        error(FATAL, "Unrecognised or disabled cgroup support\n");
    }

    if (!MEMBER_OFFSET("cgroup_subsys_state", "ss")) {
        have_ss_member = false;
        error(WARNING, "pre-3.12 kernel detected, no support for getting subsys name\n");
    } else
        have_ss_member = true;

    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);
    }

    /* pre-3.12 cgroup_subsys_state doesn't contain 'ss' member */
    if (have_ss_member) {
        get_subsys_name(subsys, subsys_name, BUFSIZE);
        fprintf(fp, "subsys: %-20s cgroup: %s\n", subsys_name, cgroup_path);
    } else {
        fprintf(fp, "cgroup: %s\n", 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;
		
		/* Generally the subsys_array is not NULL-terminated, however 
		 * a particular fedora kernel was NULL-terminated
		 */
		if (!cgroup_subsys_arr[i])
			continue;

        /* 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
};


--
Crash-utility mailing list
Crash-utility@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/crash-utility

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux