Add per-cpu specifications to the "p" command. This allows to specify CPUs for a per-cpu symbol after a colon. Keep in mind that a colon can be part of a valid C expression, e.g. ':' or the ternary operator, so if the part before the colon is not a symbol, the colon must be restored. Fortunately, a symbol name followed by a colon cannot start a valid C expression, so there is never any ambiguity. Signed-off-by: Petr Tesarik <ptesarik@xxxxxxx> --- help.c | 13 ++++++- symbols.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 113 insertions(+), 13 deletions(-)
[PATCH 3/11] Implement percpu variables for "p" Add per-cpu specifications to the "p" command. This allows to specify CPUs for a per-cpu symbol after a colon. Keep in mind that a colon can be part of a valid C expression, e.g. ':' or the ternary operator, so if the part before the colon is not a symbol, the colon must be restored. Fortunately, a symbol name followed by a colon cannot start a valid C expression, so there is never any ambiguity. Signed-off-by: Petr Tesarik <ptesarik@xxxxxxx> --- help.c | 13 ++++++- symbols.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 113 insertions(+), 13 deletions(-) --- a/symbols.c +++ b/symbols.c @@ -73,7 +73,7 @@ static void Elf32_Sym_to_common(Elf32_Sy static void Elf64_Sym_to_common(Elf64_Sym *, struct elf_common *); static void cmd_datatype_common(ulong); static void process_gdb_output(char *, unsigned, const char *, int); -static int display_per_cpu_info(struct syment *); +static int display_per_cpu_info(struct syment *, int, char *); static struct load_module *get_module_percpu_sym_owner(struct syment *); static int is_percpu_symbol(struct syment *); static void dump_percpu_symbols(struct load_module *); @@ -6392,6 +6392,7 @@ cmd_p(void) unsigned radix; int do_load_module_filter; char buf1[BUFSIZE]; + char *cpuspec; do_load_module_filter = radix = 0; @@ -6426,19 +6427,32 @@ cmd_p(void) if (argerrs || !args[optind]) cmd_usage(pc->curcmd, SYNOPSIS); + cpuspec = strrchr(args[optind], ':'); + if (cpuspec) + *cpuspec++ = NULLCHAR; + sp = NULL; if ((sp = symbol_search(args[optind])) && !args[optind+1]) { if ((percpu_sp = per_cpu_symbol_search(args[optind])) && - display_per_cpu_info(percpu_sp)) + display_per_cpu_info(percpu_sp, radix, cpuspec)) return; if (module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix)) do_load_module_filter = TRUE; } else if ((percpu_sp = per_cpu_symbol_search(args[optind])) && - display_per_cpu_info(percpu_sp)) + display_per_cpu_info(percpu_sp, radix, cpuspec)) return; else if (st->flags & LOAD_MODULE_SYMS) do_load_module_filter = TRUE; + if (cpuspec) { + if (sp) + error(WARNING, "%s is not percpu; cpuspec ignored.\n", + sp->name); + else + /* maybe a valid C expression (e.g. ':') */ + *(cpuspec-1) = ':'; + } + process_gdb_output(concat_args(buf1, 0, TRUE), radix, sp ? sp->name : NULL, do_load_module_filter); } @@ -6496,34 +6510,109 @@ process_gdb_output(char *gdb_request, un } /* + * Get the type of an expression using gdb's "whatis" command. + * The returned string is dynamically allocated, and it should + * be passed to FREEBUF() when no longer needed. + * Return NULL if the type cannot be determined. + */ +static char * +expr_type_name(const char *expr) +{ + char buf[BUFSIZE], *p; + + open_tmpfile(); + sprintf(buf, "whatis %s", expr); + if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { + close_tmpfile(); + return NULL; + } + + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile) && !STRNEQ(buf, "type = ")) + ; + p = feof(pc->tmpfile) ? NULL : buf + strlen("type = "); + close_tmpfile(); + + if (p) { + size_t len = strlen(clean_line(p)); + return strcpy(GETBUF(len + 1), p); + } + return NULL; +} + +/* * Display the datatype of the per_cpu__xxx symbol and * the addresses of each its per-cpu instances. */ static int -display_per_cpu_info(struct syment *sp) +display_per_cpu_info(struct syment *sp, int radix, char *cpuspec) { + ulong *cpus; int c; ulong addr; char buf[BUFSIZE]; + char leader[sizeof("&per_cpu(") + strlen(sp->name) + + sizeof(", " STR(UINT_MAX) ")")]; + char *typename; + int do_load_module_filter; if (((kt->flags & (SMP|PER_CPU_OFF)) != (SMP|PER_CPU_OFF)) || (!is_percpu_symbol(sp)) || !((sp->type == 'd') || (sp->type == 'D') || (sp->type == 'V'))) return FALSE; - fprintf(fp, "PER-CPU DATA TYPE:\n "); - sprintf(buf, "whatis %s", sp->name); - if (!gdb_pass_through(buf, pc->nullfp, GNU_RETURN_ON_ERROR)) - fprintf(fp, "[undetermined type] %s;\n", sp->name); - else - whatis_variable(sp); + if (cpuspec) { + cpus = get_cpumask_buf(); + if (STREQ(cpuspec, "")) { + SET_BIT(cpus, CURRENT_CONTEXT()->processor); + } else { + make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL); + } + } else + cpus = NULL; + + typename = expr_type_name(sp->name); + + if (!cpus) { + fprintf(fp, "PER-CPU DATA TYPE:\n "); + if (!typename) + fprintf(fp, "[undetermined type] %s;\n", sp->name); + else + whatis_variable(sp); + + fprintf(fp, "PER-CPU ADDRESSES:\n"); + } + + do_load_module_filter = + module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix); - fprintf(fp, "PER-CPU ADDRESSES:\n"); for (c = 0; c < kt->cpus; c++) { + if (cpus && !NUM_IN_BITMAP(cpus, c)) + continue; addr = sp->value + kt->__per_cpu_offset[c]; - fprintf(fp, " [%d]: %lx\n", c, addr); + if (!cpus) + fprintf(fp, " [%d]: %lx\n", c, addr); + else if (typename) { + snprintf(buf, sizeof buf, "p *(%s*) 0x%lx", + typename, addr); + sprintf(leader, "per_cpu(%s, %u)", + sp->name, c); + process_gdb_output(buf, radix, leader, + do_load_module_filter); + } else { + snprintf(buf, sizeof buf, "p (void*) 0x%lx", addr); + sprintf(leader, "&per_cpu(%s, %u)", + sp->name, c); + process_gdb_output(buf, radix, leader, + do_load_module_filter); + } } + if (typename) + FREEBUF(typename); + if (cpus) + FREEBUF(cpus); + return TRUE; } --- a/help.c +++ b/help.c @@ -1083,10 +1083,14 @@ NULL char *help_p[] = { "p", "print the value of an expression", -"[-x|-d][-u] expression", +"[-x|-d][-u] expression[:cpuspec]", " This command passes its arguments on to gdb \"print\" command for evaluation.", "", " expression The expression to be evaluated.", +" cpuspec CPU specification for per-cpu variables:", +" : CPU of the currently selected task.", +" :#[-#][,...] CPU list(s), e.g. \"1,3,5\", \"1-3\",", +" or \"1,3,5-7,10\".", " -x override default output format with hexadecimal format.", " -d override default output format with decimal format.", " -u the expression evaluates to a user address reference.", @@ -1144,6 +1148,13 @@ char *help_p[] = { " swap_address = 0x0, ", " segments = 0x0", " }", +"", +" Print the contents of the variable \"blk_cpu_iopoll\" on the current CPU:\n", +" %s> p blk_cpu_iopoll:", +" per_cpu(blk_cpu_iopoll, 2) = $1 = {", +" next = 0xffff88011e290100, ", +" prev = 0xffff88011e290100", +" }", NULL };
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility