[Crash-utility] [PATCH v2] kmem: Introduce -m option

Aaron Tomlin atomlin at redhat.com
Tue Apr 28 13:31:19 UTC 2015


Hi Dave,

Hopefully this is satisfactory:

The dump_mem_map() function displays basic data about each entry in the
mem_map[] array, or if an address is specified, just the mem_map[] entry
for that address.

This patch introduces the -m option to be used with 'kmem -p' exclusively.
When used with -p, a comma-separated list of one or more struct page
members may be specified to generate a custom, formatted display. For
example:

  crash> kmem -p -m mapping,index,_mapcount.counter,_count.counter
  ffffea0000000000      0x0     0       0       0
  ffffea0000000040      0x0     0       -1      1
  ffffea0000000080      0x0     0       -1      1
  ffffea00000000c0      0x0     0       -1      1
  ffffea0000000100      0x0     0       -1      1
  ffffea0000000140      0x0     0       -1      1
  ffffea0000000180      0x0     0       -1      1
  ffffea00000001c0      0x0     0       -1      1
  ffffea0000000200      0x0     0       -1      1
  ffffea0000000240      0x0     0       -1      1
  ...

Signed-off-by: Aaron Tomlin <atomlin at redhat.com>
---
 help.c   |  19 +++-
 memory.c | 357 ++++++++++++++++++++++++++++++++++++++++++---------------------
 2 files changed, 257 insertions(+), 119 deletions(-)

diff --git a/help.c b/help.c
index b2f4d21..76b640a 100644
--- a/help.c
+++ b/help.c
@@ -5530,7 +5530,7 @@ char *help_kmem[] = {
 "kmem",
 "kernel memory",
 "[-f|-F|-p|-c|-C|-i|-s|-S|-v|-V|-n|-z|-o|-h] [slab] [[-P] address]\n"
-"       [-g [flags]] [-I slab[,slab]]",
+"       [-g [flags]] [-I slab[,slab]] [-m member[,member]]",
 "  This command displays information about the use of kernel memory.\n",
 "        -f  displays the contents of the system free memory headers.",
 "            also verifies that the page count equals nr_free_pages.",
@@ -5565,6 +5565,9 @@ char *help_kmem[] = {
 "            all slab cache names and addresses are listed.",
 "   -I slab  when used with -s or -S, one or more slab cache names in a",
 "            comma-separated list may be specified as slab caches to ignore.",
+" -m member  when used with -p, a comma-separated list of one or more",
+"            struct page members may be specified to generate a custom",
+"            formatted display.",
 "        -P  declares that the following address argument is a physical address.",
 "   address  when used without any flag, the address can be a kernel virtual,",
 "            or physical address; a search is made through the symbol table,",
@@ -5717,6 +5720,20 @@ char *help_kmem[] = {
 "    f5c51440     22000         0         0  1 80 slab",
 "    ...",
 "    ",
+"  Dump the mem_map[] array but select desired fields:\n",
+"    %s> kmem -p -m mapping,index,_mapcount.counter",
+"    ffffea0000000000	0x0	0	0",
+"    ffffea0000000040	0x0	0	-1",
+"    ffffea0000000080	0x0	0	-1",
+"    ffffea00000000c0	0x0	0	-1",
+"    ffffea0000000100	0x0	0	-1",
+"    ffffea0000000140	0x0	0	-1",
+"    ffffea0000000180	0x0	0	-1",
+"    ffffea00000001c0	0x0	0	-1",
+"    ffffea0000000200	0x0	0	-1",
+"    ffffea0000000240	0x0	0	-1",
+"    ...",
+"    ",
 "  Use the commands above with a page pointer or a physical address argument:\n",
 "    %s> kmem -f c40425b0",
 "    NODE ",
diff --git a/memory.c b/memory.c
index aacf929..0ec5f5a 100644
--- a/memory.c
+++ b/memory.c
@@ -54,6 +54,8 @@ struct meminfo {           /* general purpose memory information structure */
 	int current_cache_index;
 	ulong found;
 	ulong retval;
+	ulong nr_members;
+	struct struct_member_data *page_member_cache;
 	char *ignore;
 	int errors;
 	int calls;
@@ -4214,6 +4216,101 @@ tgid_quick_search(ulong tgid)
 	return NULL;
 }
 
+static void
+collect_page_member_data(char *optlist, struct meminfo *mi)
+{
+	int i;
+	int members;
+	char *opt_string;
+	char *memberlist[MAXARGS];
+	struct struct_member_data *page_member_cache, *pmd;
+
+	if ((count_chars(optlist, ',')+1) > MAXARGS)
+		error(FATAL, "too many members in comma-separated list\n");
+
+	if ((LASTCHAR(optlist) == ',') || (LASTCHAR(optlist) == '.'))
+		error(FATAL, "invalid format: %s\n", optlist);
+
+	opt_string = STRDUPBUF(optlist);
+	replace_string(opt_string, ",", ' ');
+
+	if (!(members = parse_line(opt_string, memberlist)))
+		error(FATAL, "invalid page struct member list format: %s\n",
+			optlist);
+
+        page_member_cache = (struct struct_member_data *)
+                GETBUF(sizeof(struct struct_member_data) * members);
+
+	for (i = 0, pmd = page_member_cache; i < members; i++, pmd++) {
+		pmd->structure = "page";
+		pmd->member = memberlist[i];
+
+		if (!fill_struct_member_data(pmd))
+			error(FATAL, "invalid %s struct member: %s\n",
+				pmd->structure, pmd->member);
+
+		if (CRASHDEBUG(1)) {
+			fprintf(fp, "      structure: %s\n", pmd->structure);
+			fprintf(fp, "         member: %s\n", pmd->member);
+			fprintf(fp, "           type: %ld\n", pmd->type);
+			fprintf(fp, "  unsigned_type: %ld\n", pmd->unsigned_type);
+			fprintf(fp, "         length: %ld\n", pmd->length);
+			fprintf(fp, "         offset: %ld\n", pmd->offset);
+			fprintf(fp, "         bitpos: %ld\n", pmd->bitpos);
+			fprintf(fp, "        bitsize: %ld\n", pmd->bitsize);
+		}
+	}
+
+	mi->nr_members = members;
+	mi->page_member_cache = page_member_cache;
+
+	FREEBUF(page_member_cache);
+}
+
+static int
+show_page_member_data(ulong *pp, struct meminfo *mi, char *outputbuffer)
+{
+	int bufferindex, i;
+	ulong buf;
+	struct struct_member_data *pmd;
+
+	bufferindex = 0;
+	pmd = mi->page_member_cache;
+
+	bufferindex += sprintf(outputbuffer + bufferindex,
+					"%lx\t", *pp);
+
+	for (i = 0; i < mi->nr_members; pmd++, i++) {
+
+		switch (pmd->type)
+		{
+		case TYPE_CODE_PTR:
+			readmem(*pp + pmd->offset, KVADDR, &buf,
+				pmd->length, "page_member_cache offset", FAULT_ON_ERROR);
+			bufferindex += sprintf(outputbuffer + bufferindex, "0x%lx\t",
+				buf);
+			break;
+		case TYPE_CODE_INT:
+			readmem(*pp + pmd->offset, KVADDR, &buf,
+				pmd->length, "page_member_cache offset", FAULT_ON_ERROR);
+			if (pmd->unsigned_type ||
+				pmd->length == sizeof(ulonglong))
+				bufferindex += sprintf(outputbuffer + bufferindex,
+					"%lu\t", buf);
+			else
+				bufferindex += sprintf(outputbuffer + bufferindex,
+					"%d\t",	(int)buf);
+			break;
+		default:
+			error(FATAL, "invalid data structure reference: %s.%s\n",
+				pmd->structure, pmd->member);
+			break;
+		}
+	}
+
+	return bufferindex += sprintf(outputbuffer+bufferindex, "\n");
+}
+
 /*
  *  Fill in the task_mem_usage structure with the RSS, virtual memory size,
  *  percent of physical memory being used, and the mm_struct address.
@@ -4425,7 +4522,7 @@ cmd_kmem(void)
 	BZERO(&meminfo, sizeof(struct meminfo));
 	BZERO(&value[0], sizeof(ulonglong)*MAXARGS);
 
-        while ((c = getopt(argcnt, args, "gI:sSFfpvczCinl:L:PVoh")) != EOF) {
+        while ((c = getopt(argcnt, args, "gI:sSFfm:pvczCinl:L:PVoh")) != EOF) {
                 switch(c)
 		{
 		case 'V':
@@ -4480,6 +4577,10 @@ cmd_kmem(void)
 			pflag = 1;
 			break;
 
+		case 'm':
+			collect_page_member_data(optarg, &meminfo);
+			break;
+
 		case 'I':
 			meminfo.ignore = optarg;
 			break;	
@@ -4980,62 +5081,64 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 	char style3[100];
 	char style4[100];
 
-	sprintf((char *)&style1, "%%lx%s%%%dllx%s%%%dlx%s%%8lx %%2d%s",
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			VADDR_PRLEN,
-			space(MINSPACE),
-			space(MINSPACE));
-	sprintf((char *)&style2, "%%-%dlx%s%%%dllx%s%s%s%s %2s ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, " "),
-			space(MINSPACE),
-			mkstring(buf4, 8, CENTER|RJUST, " "),
-			" ");
-	sprintf((char *)&style3, "%%-%dlx%s%%%dllx%s%s%s%s %%2d ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "-------"),
-			space(MINSPACE),
-			mkstring(buf4, 8, CENTER|RJUST, "-----"));
-	sprintf((char *)&style4, "%%-%dlx%s%%%dllx%s%%%dlx%s%%8lx %%2d ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			VADDR_PRLEN,
-			space(MINSPACE));
-
 	v22 = VALID_MEMBER(page_inode);  /* page.inode vs. page.mapping */
 
-        if (v22) {
-		sprintf(hdr, "%s%s%s%s%s%s%s%sCNT FLAGS\n",
-		    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
-		    space(MINSPACE),               
-                    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			RJUST, "PHYSICAL"),		    
-		    space(MINSPACE),               
-		    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "INODE"), 
-		    space(MINSPACE),               
-		    mkstring(buf4, 8, CENTER|LJUST, "OFFSET"),
-		    space(MINSPACE-1));
-        } else {
-		sprintf(hdr, "%s%s%s%s%s%s%sCNT FLAGS\n",
-		    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
-		    space(MINSPACE),             
-                    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-                        RJUST, "PHYSICAL"),
-		    space(MINSPACE),             
-		    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "MAPPING"),
-		    space(MINSPACE),               
-		    mkstring(buf4, 8, CENTER|RJUST, "INDEX"));
-        }
+	if (!mi->nr_members) {
+		sprintf((char *)&style1, "%%lx%s%%%dllx%s%%%dlx%s%%8lx %%2d%s",
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				VADDR_PRLEN,
+				space(MINSPACE),
+				space(MINSPACE));
+		sprintf((char *)&style2, "%%-%dlx%s%%%dllx%s%s%s%s %2s ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, " "),
+				space(MINSPACE),
+				mkstring(buf4, 8, CENTER|RJUST, " "),
+				" ");
+		sprintf((char *)&style3, "%%-%dlx%s%%%dllx%s%s%s%s %%2d ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "-------"),
+				space(MINSPACE),
+				mkstring(buf4, 8, CENTER|RJUST, "-----"));
+		sprintf((char *)&style4, "%%-%dlx%s%%%dllx%s%%%dlx%s%%8lx %%2d ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				VADDR_PRLEN,
+				space(MINSPACE));
+
+		if (v22) {
+			sprintf(hdr, "%s%s%s%s%s%s%s%sCNT FLAGS\n",
+			    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
+			    space(MINSPACE),               
+			    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				RJUST, "PHYSICAL"),		    
+			    space(MINSPACE),               
+			    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "INODE"), 
+			    space(MINSPACE),               
+			    mkstring(buf4, 8, CENTER|LJUST, "OFFSET"),
+			    space(MINSPACE-1));
+		} else {
+			sprintf(hdr, "%s%s%s%s%s%s%sCNT FLAGS\n",
+			    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
+			    space(MINSPACE),             
+			    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				RJUST, "PHYSICAL"),
+			    space(MINSPACE),             
+			    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "MAPPING"),
+			    space(MINSPACE),               
+			    mkstring(buf4, 8, CENTER|RJUST, "INDEX"));
+		}
+	}
 
 	mapping = index = 0;
 	reserved = shared = slabs = buffers = inode = offset = 0;
@@ -5065,7 +5168,8 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 			error(FATAL, "dump_mem_map: no memtype specified\n");
 			break;
 		}
-		print_hdr = TRUE;
+		if (!mi->nr_members)
+			print_hdr = TRUE;
 		break;
 
 	case GET_ALL:
@@ -5092,7 +5196,8 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 		break;
 
 	default:
-		print_hdr = TRUE;
+		if (!mi->nr_members)
+			print_hdr = TRUE;
 		break;
 	}
 
@@ -5188,6 +5293,11 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 
 			if (!done && (pg_spec || phys_spec))
 				continue;
+
+			if (mi->nr_members) {
+				bufferindex += show_page_member_data(&pp, mi, outputbuffer+bufferindex);
+				goto display_members;
+			}
 			
 			flags = ULONG(pcache + OFFSET(page_flags));
 			if (SIZE(page_flags) == 4)
@@ -5364,6 +5474,7 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 				bufferindex += sprintf(outputbuffer+bufferindex, "\n");
 			}
 
+display_members:
 			if (bufferindex > buffersize) {
 				fprintf(fp, "%s", outputbuffer);
 				bufferindex = 0;
@@ -5443,6 +5554,11 @@ dump_mem_map(struct meminfo *mi)
 	char *outputbuffer;
 	int bufferindex;
 
+	if (IS_SPARSEMEM()) {
+		dump_mem_map_SPARSEMEM(mi);
+		return;
+	}
+
 	buffersize = 1024 * 1024;
 	outputbuffer = GETBUF(buffersize + 512);
 
@@ -5451,67 +5567,64 @@ dump_mem_map(struct meminfo *mi)
 	char style3[100];
 	char style4[100];
 
-	if (IS_SPARSEMEM()) {
-		dump_mem_map_SPARSEMEM(mi);
-		return;
-	}
-
-	sprintf((char *)&style1, "%%lx%s%%%dllx%s%%%dlx%s%%8lx %%2d%s",
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			VADDR_PRLEN,
-			space(MINSPACE),
-			space(MINSPACE));
-	sprintf((char *)&style2, "%%-%dlx%s%%%dllx%s%s%s%s %2s ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, " "),
-			space(MINSPACE),
-			mkstring(buf4, 8, CENTER|RJUST, " "),
-			" ");
-	sprintf((char *)&style3, "%%-%dlx%s%%%dllx%s%s%s%s %%2d ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "-------"),
-			space(MINSPACE),
-			mkstring(buf4, 8, CENTER|RJUST, "-----"));
-	sprintf((char *)&style4, "%%-%dlx%s%%%dllx%s%%%dlx%s%%8lx %%2d ",
-			VADDR_PRLEN,
-			space(MINSPACE),
-			(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			space(MINSPACE),
-			VADDR_PRLEN,
-			space(MINSPACE));
-
 	v22 = VALID_MEMBER(page_inode);  /* page.inode vs. page.mapping */
 
-        if (v22) {
-		sprintf(hdr, "%s%s%s%s%s%s%s%sCNT FLAGS\n",
-		    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
-		    space(MINSPACE),               
-                    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-			RJUST, "PHYSICAL"),		    
-		    space(MINSPACE),               
-		    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "INODE"), 
-		    space(MINSPACE),               
-		    mkstring(buf4, 8, CENTER|LJUST, "OFFSET"),
-		    space(MINSPACE-1));
-        } else {
-		sprintf(hdr, "%s%s%s%s%s%s%sCNT FLAGS\n",
-		    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
-		    space(MINSPACE),             
-                    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
-                        RJUST, "PHYSICAL"),
-		    space(MINSPACE),             
-		    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "MAPPING"),
-		    space(MINSPACE),               
-		    mkstring(buf4, 8, CENTER|RJUST, "INDEX"));
-        }
+	if (!mi->nr_members) {
+		sprintf((char *)&style1, "%%lx%s%%%dllx%s%%%dlx%s%%8lx %%2d%s",
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				VADDR_PRLEN,
+				space(MINSPACE),
+				space(MINSPACE));
+		sprintf((char *)&style2, "%%-%dlx%s%%%dllx%s%s%s%s %2s ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, " "),
+				space(MINSPACE),
+				mkstring(buf4, 8, CENTER|RJUST, " "),
+				" ");
+		sprintf((char *)&style3, "%%-%dlx%s%%%dllx%s%s%s%s %%2d ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "-------"),
+				space(MINSPACE),
+				mkstring(buf4, 8, CENTER|RJUST, "-----"));
+		sprintf((char *)&style4, "%%-%dlx%s%%%dllx%s%%%dlx%s%%8lx %%2d ",
+				VADDR_PRLEN,
+				space(MINSPACE),
+				(int)MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				space(MINSPACE),
+				VADDR_PRLEN,
+				space(MINSPACE));
+
+		if (v22) {
+			sprintf(hdr, "%s%s%s%s%s%s%s%sCNT FLAGS\n",
+			    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
+			    space(MINSPACE),               
+			    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				RJUST, "PHYSICAL"),		    
+			    space(MINSPACE),               
+			    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "INODE"), 
+			    space(MINSPACE),               
+			    mkstring(buf4, 8, CENTER|LJUST, "OFFSET"),
+			    space(MINSPACE-1));
+		} else {
+			sprintf(hdr, "%s%s%s%s%s%s%sCNT FLAGS\n",
+			    mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), 
+			    space(MINSPACE),             
+			    mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")),
+				RJUST, "PHYSICAL"),
+			    space(MINSPACE),             
+			    mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "MAPPING"),
+			    space(MINSPACE),               
+			    mkstring(buf4, 8, CENTER|RJUST, "INDEX"));
+		}
+	}
 
 	mapping = index = 0;
 	reserved = shared = slabs = buffers = inode = offset = 0;
@@ -5541,7 +5654,8 @@ dump_mem_map(struct meminfo *mi)
 			error(FATAL, "dump_mem_map: no memtype specified\n");
 			break;
 		}
-		print_hdr = TRUE;
+		if (!mi->nr_members)
+			print_hdr = TRUE;
 		break;
 
 	case GET_ALL:
@@ -5568,7 +5682,8 @@ dump_mem_map(struct meminfo *mi)
 		break;
 
 	default:
-		print_hdr = TRUE;
+		if (!mi->nr_members)
+			print_hdr = TRUE;
 		break;
 	}
 
@@ -5626,6 +5741,11 @@ dump_mem_map(struct meminfo *mi)
 			if (!done && (pg_spec || phys_spec))
 				continue;
 			
+			if (mi->nr_members) {
+				bufferindex += show_page_member_data(&pp, mi, outputbuffer+bufferindex);
+				goto display_members;
+			}
+
 			flags = ULONG(pcache + OFFSET(page_flags));
 			if (SIZE(page_flags) == 4)
 				flags &= 0xffffffff;
@@ -5802,6 +5922,7 @@ dump_mem_map(struct meminfo *mi)
 				bufferindex += sprintf(outputbuffer+bufferindex, "\n");
 			}
 	
+display_members:
 			if (bufferindex > buffersize) {
 				fprintf(fp, "%s", outputbuffer);
 				bufferindex = 0;
-- 
1.9.3




More information about the Crash-utility mailing list