Hi Dave,
When a percpu symbol is of type pointer, the 'struct' command does
not generate the expected output. For example:
Note: The correct value of 'exec_start' should be
0x15b070b28b0c27 not 0x0
crash> struct task_struct.se.exec_start softlockup_watchdog:0
[0]: ffff880214e55a00
se.exec_start = 0x0,
crash> px softlockup_watchdog:0-1
per_cpu(softlockup_watchdog, 0) = $1 = (struct task_struct *) 0xffff880fe97e2e00
crash> px ((struct task_struct *)0xffff880fe97e2e00)->se.exec_start
$2 = 0x15b070b28b0c27
Currently, the 'struct' and 'p' command simply calculates
'cpuaddr' as
follows -- where 'addr' is a percpu's value:
cpuaddr = addr + __per_cpu_offset
This is correct if the percpu symbol or offset
(i.e. [percpu symbol:cpu-specifier] or [percpu value:cpu-specifier])
is not of type pointer. If a given percpu symbol is of type pointer such as
in 'static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog)', we
need to dereference the pointer to obtain the above correct kernel virtual
address.
For instance, using the above example, we need to pass the following to gdb
to resolve appropriately:
kernel virtual addresses for the cpus specified.
crash> set gdb on
gdb: on
gdb> p *(struct task_struct **) 0xffff880fffc0ddc0
$1 = (struct task_struct *) 0xffff880fe97e2e00
gdb>
With this patch, we now obtain the expected output:
crash> struct task_struct.se.exec_start softlockup_watchdog:0
[0]: ffff880214e55a00
se.exec_start = 0x15b070b28b0c27,
In short, this patch does the following:
1. Fix the handling of a percpu symbol which are of type pointer
2. The 'struct' and 'p' command is now able to correctly handle a
percpu symbol's value for example:
Note: we show the address of the percpu object irrespective of data type
crash> p f738
PER-CPU DATA TYPE:
struct task_struct *softlockup_watchdog;
PER-CPU ADDRESSES:
[0]: ffff88021e20f738
[1]: ffff88021e24f738
[2]: ffff88021e28f738
[3]: ffff88021e2cf738
crash> p f738:a
per_cpu(softlockup_watchdog, 0) = $5 = (struct task_struct *) 0xffff880214e55a00
per_cpu(softlockup_watchdog, 1) = $6 = (struct task_struct *) 0xffff88021498da00
per_cpu(softlockup_watchdog, 2) = $7 = (struct task_struct *) 0xffff8802149c3c00
per_cpu(softlockup_watchdog, 3) = $8 = (struct task_struct *) 0xffff880214b39e00
crash> struct task_struct.se.cfs_rq f738:a
[0]: ffff880214e55a00
se.cfs_rq = 0xffff88021e216e78,
[1]: ffff88021498da00
se.cfs_rq = 0xffff88021e256e78,
[2]: ffff8802149c3c00
se.cfs_rq = 0xffff88021e296e78,
[3]: ffff880214b39e00
se.cfs_rq = 0xffff88021e2d6e78,
Signed-off-by: Aaron Tomlin <atomlin(a)redhat.com>
---
symbols.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 82 insertions(+), 14 deletions(-)
diff --git a/symbols.c b/symbols.c
index 51d41d8..0ded35d 100644
--- a/symbols.c
+++ b/symbols.c
@@ -76,8 +76,11 @@ static void cmd_datatype_common(ulong);
static void do_datatype_addr(struct datatype_member *, ulong, int,
ulong, char **, int);
static void process_gdb_output(char *, unsigned, const char *, int);
+static char *expr_type_name(const char *);
static int display_per_cpu_info(struct syment *, int, char *);
static struct load_module *get_module_percpu_sym_owner(struct syment *);
+static struct syment *__per_cpu_symbol_search(char *);
+static struct syment *per_cpu_symbol_value_search(ulong);
static int is_percpu_symbol(struct syment *);
static void dump_percpu_symbols(struct load_module *);
static void print_struct_with_dereference(ulong, struct datatype_member *, ulong);
@@ -5119,17 +5122,8 @@ symbol_exists(char *symbol)
return(FALSE);
}
-/*
- * Determine whether a per-cpu symbol exists.
-
- * The old-style per-cpu symbol names were pre-pended with
- * "per_cpu__", whereas the new-style ones (as of 2.6.34)
- * are not. This function allows the symbol argument to
- * use either the old- or new-sytle format, and find either
- * type.
- */
struct syment *
-per_cpu_symbol_search(char *symbol)
+__per_cpu_symbol_search(char *symbol)
{
struct syment *sp;
char old[BUFSIZE];
@@ -5164,6 +5158,37 @@ per_cpu_symbol_search(char *symbol)
}
/*
+ * Determine whether a per-cpu symbol exists.
+
+ * The old-style per-cpu symbol names were pre-pended with
+ * "per_cpu__", whereas the new-style ones (as of 2.6.34)
+ * are not. This function allows the symbol argument to
+ * use either the old- or new-sytle format, and find either
+ * type.
+ */
+struct syment *
+per_cpu_symbol_search(char *symbol)
+{
+ struct syment *sp;
+ return sp = __per_cpu_symbol_search(symbol);
+}
+
+struct syment *
+per_cpu_symbol_value_search(ulong value)
+{
+ struct syment *sp;
+
+ if ((sp = symval_hash_search(value)) == NULL)
+ sp = st->symtable;
+
+ for (; sp < st->symend; sp++)
+ if (value == sp->value)
+ return sp = __per_cpu_symbol_search(sp->name);
+
+ return NULL;
+}
+
+/*
* Determine whether a static kernel symbol exists.
*/
int
@@ -5987,12 +6012,17 @@ cmd_datatype_common(ulong flags)
ulong list_head_offset;
int count;
int argc_members;
+ int ptype;
int optind_save;
unsigned int radix, restore_radix;
struct datatype_member datatype_member, *dm;
- char *separator;
- char *structname, *members;
- char *memberlist[MAXARGS];
+ char *separator;
+ char *structname, *members;
+ char *memberlist[MAXARGS];
+ char *typename;
+ char buf[BUFSIZE];
+ char *argv[MAXARGS];
+ FILE *tmpfp;
dm = &datatype_member;
count = 0xdeadbeef;
@@ -6000,9 +6030,11 @@ cmd_datatype_common(ulong flags)
list_head_offset = 0;
argc_members = 0;
radix = restore_radix = 0;
+ ptype = 0;
separator = members = NULL;
cpuspec = NULL;
cpus = NULL;
+ typename = NULL;
while ((c = getopt(argcnt, args, "pxdhfuc:rvol:")) != EOF) {
switch (c)
@@ -6104,6 +6136,10 @@ cmd_datatype_common(ulong flags)
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
error(FATAL, "-f option cannot be used with percpu\n");
addr = htol(args[optind], FAULT_ON_ERROR, NULL);
+ if (!(sp = per_cpu_symbol_value_search(addr)))
+ error(FATAL,
+ "invalid percpu symbol value: %s\n",
+ args[optind]);
aflag++;
} else {
if (pc->curcmd_flags & MEMTYPE_FILEADDR)
@@ -6138,6 +6174,7 @@ cmd_datatype_common(ulong flags)
}
if (cpuspec) {
+ typename = expr_type_name(sp->name);
cpus = get_cpumask_buf();
if (STREQ(cpuspec, ""))
SET_BIT(cpus, CURRENT_CONTEXT()->processor);
@@ -6212,6 +6249,8 @@ cmd_datatype_common(ulong flags)
dm->name, memberlist[0]);
do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF));
} else if (cpus) {
+ if (typename && (ptype = (LASTCHAR(typename) == '*')))
+ open_tmpfile2();
for (c = 0; c < kt->cpus; c++) {
ulong cpuaddr;
@@ -6227,9 +6266,25 @@ cmd_datatype_common(ulong flags)
continue;
}
+ if (ptype) {
+ snprintf(buf, sizeof buf, "p *(%s*) 0x%lx",
+ typename, cpuaddr);
+ gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR);
+
+ rewind(pc->tmpfile2);
+ fgets(buf, BUFSIZE, pc->tmpfile2);
+ parse_line(buf, argv);
+ cpuaddr = htol(argv[3], FAULT_ON_ERROR, NULL);
+ rewind(pc->tmpfile2);
+ tmpfp = pc->tmpfile2;
+ pc->tmpfile2 = NULL;
+ }
fprintf(fp, "%lx\n", cpuaddr);
+
do_datatype_addr(dm, cpuaddr , count,
flags, memberlist, argc_members);
+ if (ptype)
+ set_tmpfile2(tmpfp);
}
} else
do_datatype_addr(dm, addr, count, flags,
@@ -6237,6 +6292,9 @@ cmd_datatype_common(ulong flags)
restore_current_radix(restore_radix);
+ if (ptype)
+ close_tmpfile2();
+
freebuf:
if (argc_members) {
FREEBUF(structname);
@@ -6245,6 +6303,9 @@ freebuf:
if (cpus)
FREEBUF(cpus);
+
+ if (typename)
+ FREEBUF(typename);
}
static void
@@ -6773,7 +6834,14 @@ cmd_p(void)
if (cpuspec)
*cpuspec++ = NULLCHAR;
- sp = NULL;
+ if (hexadecimal(args[optind], 0)) {
+ if ((sp = per_cpu_symbol_value_search(htol(args[optind],
+ FAULT_ON_ERROR, NULL)))) {
+ display_per_cpu_info(sp, radix, cpuspec);
+ return;
+ }
+ } else
+ 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, radix, cpuspec))
--
2.5.5