This piece allows to dump a percpu struct or union at a given address on a given CPU. Signed-off-by: Petr Tesarik <ptesarik@xxxxxxx> --- defs.h | 2 global_data.c | 1 help.c | 65 ++++++++++++++++++++++++ symbols.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+)
Implement basic percpu functionality This piece allows to dump a percpu struct or union at a given address on a given CPU. Signed-off-by: Petr Tesarik <ptesarik@xxxxxxx> --- defs.h | 2 global_data.c | 1 help.c | 65 ++++++++++++++++++++++++ symbols.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+) --- a/defs.h +++ b/defs.h @@ -4124,6 +4124,7 @@ void cmd_sym(void); /* symbols. void cmd_struct(void); /* symbols.c */ void cmd_union(void); /* symbols.c */ void cmd_pointer(void); /* symbols.c */ +void cmd_percpu(void); /* symbols.c */ void cmd_whatis(void); /* symbols.c */ void cmd_p(void); /* symbols.c */ void cmd_mount(void); /* filesys.c */ @@ -4608,6 +4609,7 @@ extern char *help_mod[]; extern char *help_mount[]; extern char *help_net[]; extern char *help_p[]; +extern char *help_percpu[]; extern char *help_ps[]; extern char *help_pte[]; extern char *help_ptob[]; --- a/global_data.c +++ b/global_data.c @@ -95,6 +95,7 @@ struct command_table_entry linux_command {"mount", cmd_mount, help_mount, 0}, {"net", cmd_net, help_net, REFRESH_TASK_TABLE}, {"p", cmd_p, help_p, 0}, + {"percpu", cmd_percpu, help_percpu, 0}, {"ps", cmd_ps, help_ps, REFRESH_TASK_TABLE}, {"pte", cmd_pte, help_pte, 0}, {"ptob", cmd_ptob, help_ptob, 0}, --- a/help.c +++ b/help.c @@ -1147,6 +1147,71 @@ char *help_p[] = { NULL }; +char *help_percpu[] = { +"percpu", +"percpu variables", +"[-a] [-c cpu] [cpu]... [struct|union|*] struct_name [address|symbol]", +" This command displays a formatted display of the contents of a per-cpu", +" variable for a given set of CPUs.", +" ", +" The set of CPUs can be specified in the following ways:\n", +" -a show all CPUs.", +" -c cpu specify cpu set, e.g. \"1,3,5\", \"1-3\", or \"1,3,5-7,10\".", +" cpu show for a specific CPU (numeric only, can be repeated).", +" ", +" If no CPU specifiers are present, percpu will use the CPU of the", +" currently selected task.", +"\nEXAMPLES", +" Show the value of a per-cpu variable on processor 2:\n", +" %s> percpu 2 list_head blk_cpu_iopoll", +" [2]: ffff88011e290100", +" struct list_head {", +" next = 0xffff88011e290100, ", +" prev = 0xffff88011e290100", +" }", + +" ", +" Show a per-cpu variable on all processors:\n", +" %s> percpu -a disk_stats 0x1a468", +" [0]: ffff88011e21a468", +" struct disk_stats {", +" sectors = {11197190, 7550896}, ", +" ios = {360193, 159113}, ", +" merges = {11723, 22075}, ", +" ticks = {6180943, 11137498}, ", +" io_ticks = 1781827, ", +" time_in_queue = 16785949", +" }", +" [1]: ffff88011e25a468", +" struct disk_stats {", +" sectors = {1250132, 173032}, ", +" ios = {20393, 7573}, ", +" merges = {26161, 9947}, ", +" ticks = {415390, 758619}, ", +" io_ticks = 130165, ", +" time_in_queue = 1426936", +" }", +" [2]: ffff88011e29a468", +" struct disk_stats {", +" sectors = {88514, 10136}, ", +" ios = {1381, 382}, ", +" merges = {5515, 24870}, ", +" ticks = {29334, 69079}, ", +" io_ticks = 18280, ", +" time_in_queue = 264582", +" }", +" [3]: ffff88011e2da468", +" struct disk_stats {", +" sectors = {66608, 14128}, ", +" ios = {2731, 602}, ", +" merges = {4697, 5173}, ", +" ticks = {27411, 67344}, ", +" io_ticks = 13246, ", +" time_in_queue = 211221", +" }", +NULL +}; + char *help_ps[] = { "ps", "display process status information", --- a/symbols.c +++ b/symbols.c @@ -5987,6 +5987,162 @@ freebuf: } } +/* + * This command displays a data type after adjusting the address with + * a per-cpu offset. + */ +void +cmd_percpu(void) +{ + int c, i; + int cpulen; + ulong *cpus; + int cflag; + ulong flags; + char *structname, *typename; + struct datatype_member datatype_member, *dm = &datatype_member; + struct syment *sp; + ulong addr; + char buf[BUFSIZE]; + + cflag = 0; + + if ((cpulen = STRUCT_SIZE("cpumask_t")) < 0) + cpulen = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong); + cpus = (ulong *)GETBUF(cpulen); + + while ((c = getopt(argcnt, args, "ac:")) != EOF) { + switch (c) + { + case 'a': + for (i = 0; i < kt->cpus; i++) + SET_BIT(cpus, i); + cflag = 1; + break; + + case 'c': + make_cpumask(optarg, cpus, FAULT_ON_ERROR, NULL); + cflag = 1; + break; + + default: + argerrs++; + break; + } + } + + while (args[optind] && isdigit(args[optind][0])) { + int iserr = FALSE; + int cpu = dtoi(args[optind], RETURN_ON_ERROR, &iserr); + if (iserr) { + FREEBUF(cpus); + RESTART(); + } + if (cpu >= kt->cpus) { + FREEBUF(cpus); + error(FATAL, "CPU %d is out of range\n", cpu); + } + SET_BIT(cpus, cpu); + cflag = 1; + ++optind; + } + + if (!cflag) + SET_BIT(cpus, CURRENT_CONTEXT()->processor); + + flags = 0UL; + structname = NULL; + + if (STREQ(args[optind], "struct")) { + flags |= STRUCT_REQUEST; + structname = args[++optind]; + } else if (STREQ(args[optind], "union")) { + flags |= UNION_REQUEST; + structname = args[++optind]; + } else if (args[optind][0] == '*') { + structname = args[optind][1] + ? args[optind] + 1 + : args[++optind]; + } else { + structname = args[optind]; + } + ++optind; + + if (argerrs || !structname || !args[optind] || args[optind+1]) { + FREEBUF(cpus); + cmd_usage(pc->curcmd, SYNOPSIS); + } + + if (arg_to_datatype(structname, dm, + DATATYPE_QUERY|ANON_MEMBER_QUERY|RETURN_ON_ERROR) < 1) { + FREEBUF(cpus); + error(FATAL, "invalid data structure reference: %s\n", structname); + } + + if (dm->flags & TYPEDEF) { + flags |= dm->type; + typename = strdup(dm->name); + } else { + if (! (flags & (STRUCT_REQUEST|UNION_REQUEST)) ) + flags |= dm->type; + typename = malloc(strlen(dm->name) + 8); + sprintf(typename, "%s %s", + dm->flags & UNION_REQUEST ? "union" : "struct", + dm->name); + } + + if ((flags & (STRUCT_REQUEST|UNION_REQUEST)) != + dm->type & (STRUCT_REQUEST|UNION_REQUEST)) { + fprintf(fp, "data type mismatch: %s is not a %s\n", + structname, + flags & UNION_REQUEST ? "union" : "struct"); + goto freebuf; + } + + if (clean_arg() && IS_A_NUMBER(args[optind])) { + addr = htol(args[optind], FAULT_ON_ERROR, NULL); + } else if ((sp = per_cpu_symbol_search(args[optind]))) { + addr = sp->value; + } else { + if ((sp = symbol_search(args[optind]))) { + fprintf(fp, "not a per-cpu symbol: %s\n", + args[optind]); + } else { + fprintf(fp, "symbol not found: %s\n", args[optind]); + fprintf(fp, "possible alternatives:\n"); + if (!symbol_query(args[optind], " ", NULL)) + fprintf(fp, " (none found)\n"); + } + goto freebuf; + } + + for (i = 0; i < kt->cpus; i++) { + ulong cpuaddr; + + if (!NUM_IN_BITMAP(cpus, i)) + continue; + + cpuaddr = addr + kt->__per_cpu_offset[i]; + fprintf(fp, " [%d]: %lx\n", i, cpuaddr); + if (!IS_KVADDR(cpuaddr)) { + error(WARNING, + "invalid kernel virtual address: 0x%lx\n", + cpuaddr); + continue; + } + + fprintf(fp, "%s ", typename); + snprintf(buf, sizeof buf, "output *(%s*) 0x%lx", + typename, cpuaddr); + gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR); + fprintf(fp, "\n"); + } + +freebuf: + free(typename); + FREEBUF(cpus); +} + /* * Generic function for dumping data structure declarations, with a small
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility