[Crash-utility] [PATCH 2/5] memory: struct page.flags based filter

Yu Zhao yuzhao at google.com
Thu Feb 5 21:06:45 UTC 2015


Command search walks through all pages in identity mapping (i.e. virtual
address space starting at PAGE_OFFSET and ending at high_memory) when
it's used to search kernel memory. The new option -f allows the command
to skip some uninteresting pages (e.g. LRU pages) to speed up the search.
---
 help.c   |  11 +++++-
 memory.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 107 insertions(+), 27 deletions(-)

diff --git a/help.c b/help.c
index b2f4d21..8d45667 100644
--- a/help.c
+++ b/help.c
@@ -2814,7 +2814,8 @@ char *help_search[] = {
 "search",
 "search memory",
 "[-s start] [ -[kKV] | -u | -p | -t ] [-e end | -l length] [-m mask]\n"
-"         [-x count] -[cwh] [value | (expression) | symbol | string] ...",
+"         [-f struct-page.flags-mask] [-x count] -[cwh] "
+"         [value | (expression) | symbol | string] ...",
 "  This command searches for a given value within a range of user virtual, kernel",
 "  virtual, or physical memory space.  If no end nor length value is entered, ",
 "  then the search stops at the end of user virtual, kernel virtual, or physical",
@@ -2849,6 +2850,11 @@ char *help_search[] = {
 "              must be appropriate for the memory type specified.",
 "   -l length  Length in bytes of address range to search.",
 "     -m mask  Ignore the bits that are set in the hexadecimal mask value.",
+"     -f struct-page.flags-mask",
+"              Only search pages when page.flags & mask is true, or not true when",
+"              the mask is preceded by a letter n. When used multiple times, only",
+"              pages that meet all conditions will be searched. Only works for",
+"              virtual address space (either user or kernel).",
 "          -c  Search for character string values instead of unsigned longs.  If",
 "              the string contains any space(s), it must be encompassed by double",
 "              quotes.",
@@ -2986,6 +2992,9 @@ char *help_search[] = {
 "    ffff8100399565a8: ffffffff80493d60 (anon_inode_inode)",
 "    ffff81003a278cd0: ffffffff800649d6 (__down_interruptible+191)",
 "    ffff81003cc23e08: ffffffff800649d6 (__down_interruptible+191)",
+"\n  Search kernel virtual address space for all instances of 0xdeadbeef",
+"  within compound (PG_head|tail) pages that are not LRU pages:\n",
+"    %s> search deadbeef -f c000 -f n20",
 NULL               
 };
 
diff --git a/memory.c b/memory.c
index 2208553..70649a2 100644
--- a/memory.c
+++ b/memory.c
@@ -77,6 +77,16 @@ struct meminfo {           /* general purpose memory information structure */
 };
 
 /*
+ * Memory cache for struct page within range
+ * of kernel virtual address start and end.
+ */
+struct mem_map_cache {
+	ulong start;
+	ulong end;
+	char *cache;
+};
+
+/*
  * Search modes
  */
 
@@ -100,6 +110,9 @@ struct searchinfo {
 	ulong vaddr_end;
 	ulonglong paddr_start;
 	ulonglong paddr_end;
+	int pg_nmasks;
+	char pg_mask_neg[MAXARGS];
+	ulong pg_mask[MAXARGS];
 	union {
 		/* default ulong search */
 		struct {
@@ -136,7 +149,7 @@ static char *memtype_string(int, int);
 static char *error_handle_string(ulong);
 static void dump_mem_map(struct meminfo *);
 static void dump_mem_map_SPARSEMEM(struct meminfo *);
-static void fill_mem_map_cache(ulong, ulong, char *);
+static void fill_mem_map_cache(struct mem_map_cache *);
 static void page_flags_init(void);
 static int page_flags_init_from_pageflag_names(void);
 static int page_flags_init_from_pageflags_enum(void);
@@ -4956,7 +4969,7 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 	ulong i;
 	long total_pages;
 	int others, page_not_mapped, phys_not_mapped, page_mapping;
-	ulong pp, ppend;
+	ulong pp;
 	physaddr_t phys, physend;
 	ulong tmp, reserved, shared, slabs;
         ulong PG_reserved_flag;
@@ -4970,12 +4983,12 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 	char buf2[BUFSIZE];
 	char buf3[BUFSIZE];
 	char buf4[BUFSIZE];
-	char *page_cache;
 	char *pcache;
 	ulong section, section_nr, nr_mem_sections, section_size;
 	long buffersize;
 	char *outputbuffer;
 	int bufferindex;
+	struct mem_map_cache mmc;
 
 	buffersize = 1024 * 1024;
 	outputbuffer = GETBUF(buffersize + 512);
@@ -5101,7 +5114,7 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 		break;
 	}
 
-	page_cache = GETBUF(SIZE(page) * PGMM_CACHED);
+	mmc.cache = GETBUF(SIZE(page) * PGMM_CACHED);
 	done = FALSE;
 	total_pages = 0;
 
@@ -5166,23 +5179,23 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 		     i++, pp += SIZE(page), phys += PAGESIZE()) {
 
 			if ((i % PGMM_CACHED) == 0) {
-
-				ppend = pp + ((PGMM_CACHED-1) * SIZE(page));
+				mmc.start = pp;
+				mmc.end = pp + ((PGMM_CACHED-1) * SIZE(page));
 				physend = phys + ((PGMM_CACHED-1) * PAGESIZE());
 
-				if ((pg_spec && (mi->spec_addr > ppend)) ||
+				if ((pg_spec && (mi->spec_addr > mmc.end)) ||
 			            (phys_spec && 
 				    (PHYSPAGEBASE(mi->spec_addr) > physend))) {
 					i += (PGMM_CACHED-1);
-					pp = ppend;
+					pp = mmc.end;
 					phys = physend;
 					continue;
 				}  
 
-				fill_mem_map_cache(pp, ppend, page_cache);
+				fill_mem_map_cache(&mmc);
 			}
 
-			pcache = page_cache + ((i%PGMM_CACHED) * SIZE(page));
+			pcache = mmc.cache + ((i%PGMM_CACHED) * SIZE(page));
 
 			if (received_SIGINT())
 				restart(0);
@@ -5417,7 +5430,7 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
 	}
 
 	FREEBUF(outputbuffer);
-	FREEBUF(page_cache);
+	FREEBUF(mmc.cache);
 }
 
 static void
@@ -5426,7 +5439,7 @@ dump_mem_map(struct meminfo *mi)
 	long i, n;
 	long total_pages;
 	int others, page_not_mapped, phys_not_mapped, page_mapping;
-	ulong pp, ppend;
+	ulong pp;
 	physaddr_t phys, physend;
 	ulong tmp, reserved, shared, slabs;
         ulong PG_reserved_flag;
@@ -5442,11 +5455,11 @@ dump_mem_map(struct meminfo *mi)
 	char buf2[BUFSIZE];
 	char buf3[BUFSIZE];
 	char buf4[BUFSIZE];
-	char *page_cache;
 	char *pcache;
 	long buffersize;
 	char *outputbuffer;
 	int bufferindex;
+	struct mem_map_cache mmc;
 
 	buffersize = 1024 * 1024;
 	outputbuffer = GETBUF(buffersize + 512);
@@ -5577,7 +5590,7 @@ dump_mem_map(struct meminfo *mi)
 		break;
 	}
 
-	page_cache = GETBUF(SIZE(page) * PGMM_CACHED);
+	mmc.cache = GETBUF(SIZE(page) * PGMM_CACHED);
 	done = FALSE;
 	total_pages = 0;
 
@@ -5604,22 +5617,23 @@ dump_mem_map(struct meminfo *mi)
 		     i++, pp += SIZE(page), phys += PAGESIZE()) {
 
 			if ((i % PGMM_CACHED) == 0) {
-				ppend = pp + ((PGMM_CACHED-1) * SIZE(page));
+				mmc.start = pp;
+				mmc.end = pp + ((PGMM_CACHED-1) * SIZE(page));
 				physend = phys + ((PGMM_CACHED-1) * PAGESIZE());
 
-				if ((pg_spec && (mi->spec_addr > ppend)) ||
+				if ((pg_spec && (mi->spec_addr > mmc.end)) ||
 			            (phys_spec && 
 				    (PHYSPAGEBASE(mi->spec_addr) > physend))) {
 					i += (PGMM_CACHED-1);
-					pp = ppend;
+					pp = mmc.end;
 					phys = physend;
 					continue;
 				}  
 
-				fill_mem_map_cache(pp, ppend, page_cache);
+				fill_mem_map_cache(&mmc);
 			}
 
-			pcache = page_cache + ((i%PGMM_CACHED) * SIZE(page));
+			pcache = mmc.cache + ((i%PGMM_CACHED) * SIZE(page));
 
 			if (received_SIGINT())
 				restart(0);
@@ -5855,7 +5869,7 @@ dump_mem_map(struct meminfo *mi)
 	}
 
 	FREEBUF(outputbuffer);
-	FREEBUF(page_cache);
+	FREEBUF(mmc.cache);
 }
 
 /*
@@ -5866,7 +5880,7 @@ dump_mem_map(struct meminfo *mi)
  *  that are currently mapped, leaving the unmapped ones just zeroed out.
  */
 static void
-fill_mem_map_cache(ulong pp, ulong ppend, char *page_cache)
+fill_mem_map_cache(struct mem_map_cache *mmc)
 {
 	long size, cnt;
 	ulong addr;
@@ -5875,7 +5889,7 @@ fill_mem_map_cache(ulong pp, ulong ppend, char *page_cache)
 	/*
 	 *  Try to read it in one fell swoop.
  	 */
-	if (readmem(pp, KVADDR, page_cache, SIZE(page) * PGMM_CACHED,
+	if (readmem(mmc->start, KVADDR, mmc->cache, SIZE(page) * PGMM_CACHED,
       	    "page struct cache", RETURN_ON_ERROR|QUIET))
 		return;
 
@@ -5884,8 +5898,8 @@ fill_mem_map_cache(ulong pp, ulong ppend, char *page_cache)
 	 *  not a virtual mem_map.
 	 */
         size = SIZE(page) * PGMM_CACHED;
-        addr = pp;
-        bufptr = page_cache;
+	addr = mmc->start;
+	bufptr = mmc->cache;
 
         while (size > 0) {
 		/* 
@@ -5899,7 +5913,7 @@ fill_mem_map_cache(ulong pp, ulong ppend, char *page_cache)
 		if (!readmem(addr, KVADDR, bufptr, size,
                     "virtual page struct cache", RETURN_ON_ERROR|QUIET)) {
 			BZERO(bufptr, size);
-			if (!(vt->flags & V_MEM_MAP) && ((addr+size) < ppend)) 
+			if (!(vt->flags & V_MEM_MAP) && ((addr+size) < mmc->end))
 				error(WARNING, 
 		                   "mem_map[] from %lx to %lx not accessible\n",
 					addr, addr+size);
@@ -13500,7 +13514,7 @@ cmd_search(void)
 
 	searchinfo.mode = SEARCH_ULONG;	/* default search */
 
-        while ((c = getopt(argcnt, args, "tl:ukKVps:e:v:m:hwcx:")) != EOF) {
+	while ((c = getopt(argcnt, args, "tl:ukKVps:e:v:m:f:hwcx:")) != EOF) {
                 switch(c)
                 {
 		case 'u':
@@ -13572,6 +13586,17 @@ cmd_search(void)
 				error(FATAL, "invalid ending address: 0\n");
                         break;
 
+		case 'f':
+			if (searchinfo.pg_nmasks == MAXARGS)
+				break;
+
+			c = *optarg == 'n';
+			searchinfo.pg_mask_neg[searchinfo.pg_nmasks] = c;
+			searchinfo.pg_mask[searchinfo.pg_nmasks] =
+				htol(optarg + c, FAULT_ON_ERROR, NULL);
+			searchinfo.pg_nmasks++;
+			break;
+
 		case 'l':
 			len = stol(optarg, FAULT_ON_ERROR, NULL);
 			break;
@@ -14409,6 +14434,42 @@ search_chars_p(ulong *bufptr, ulonglong addr_p, int longcnt, struct searchinfo *
 	return addr_p;
 }
 
+static int
+skip_pages(struct searchinfo *si, struct mem_map_cache *mmc, ulong page, ulong *pp)
+{
+	int i;
+	ulong flags;
+
+	if (page < mmc->start || page > mmc->end) {
+		mmc->start = page;
+		mmc->end = page + SIZE(page) * (PGMM_CACHED - 1);
+		fill_mem_map_cache(mmc);
+	}
+
+	flags = ULONG(mmc->cache + page - mmc->start + OFFSET(page_flags));
+
+	for (i = 0; i < si->pg_nmasks; i++) {
+		if ((si->pg_mask_neg[i] && si->pg_mask[i] & flags) ||
+		    (!si->pg_mask_neg[i] && !(si->pg_mask[i] & flags)))
+			goto skip;
+	}
+
+	if (CRASHDEBUG(4))
+		fprintf(fp, "search page: struct %lx, vaddr %lx, flags %lx\n",
+		        page, *pp, flags);
+
+	return 0;
+
+skip:
+	if (CRASHDEBUG(5))
+		fprintf(fp, "ignore page: struct %lx, vaddr %lx, flags %lx\n",
+		        page, *pp, flags);
+
+	*pp += PAGESIZE();
+
+	return 1;
+}
+
 static void
 search_virtual(struct searchinfo *si)
 {
@@ -14420,6 +14481,7 @@ search_virtual(struct searchinfo *si)
 	char *pagebuf;
 	ulong pct, pages_read, pages_checked;
 	time_t begin, finish;
+	struct mem_map_cache mmc;
 
 	start = si->vaddr_start;
 	end = si->vaddr_end;
@@ -14427,6 +14489,10 @@ search_virtual(struct searchinfo *si)
 	begin = finish = 0;
 
 	pagebuf = GETBUF(PAGESIZE());
+	if (si->pg_nmasks) {
+		mmc.start = mmc.end = 0;
+		mmc.cache = GETBUF(SIZE(page) * PGMM_CACHED);
+	}
 
 	if (start & (sizeof(long)-1)) {
 		start &= ~(sizeof(long)-1);
@@ -14482,6 +14548,9 @@ search_virtual(struct searchinfo *si)
                         break;
                 }
 
+		if (si->pg_nmasks && skip_pages(si, &mmc, page, &pp))
+			continue;
+
                 if (!readmem(paddr, PHYSADDR, pagebuf, PAGESIZE(),
                     "search page", RETURN_ON_ERROR|QUIET)) {
 			pp += PAGESIZE();
@@ -14536,6 +14605,8 @@ done:
 	}
 
 	FREEBUF(pagebuf);
+	if (si->pg_nmasks)
+		FREEBUF(mmc.cache);
 }
 
 
-- 
2.2.0.rc0.207.ga3a616c




More information about the Crash-utility mailing list