[Crash-utility] [RFC PATCH] struct: Fix handing of percpu symbols

Aaron Tomlin atomlin at redhat.com
Thu Apr 14 15:32:51 UTC 2016


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 at 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




More information about the Crash-utility mailing list