[Crash-utility] [PATCH v4] files: support dump file memory mapping

yangoliver yang_oliver at hotmail.com
Fri Jun 26 13:55:30 UTC 2015


Hi Dave,

This is v4 patch for files memory mapping dump support.

The major changes in this version are,

1. Your alignment patch for NRPAGES

2. Changed files -a to files -p

   Changed output and displayed INODE, ADDRESS_SPACE, NRPAGES
   at beginning.

3. Updated help.c and added exmaple outputs for new options.

4. Some minor code cleanup, for function name defined in defs.h

Here is my patch,

Added two options in files command,

1. -m option, which allows dump file mapping and
   page count for each files
2. -p option, which could dump each pages within
   the mapping for given inode address

The foreach command also could work with -m, so
that we can easily find which processes/files hold
biggest page cache within the system.

Signed-off-by: Yong Yang <yangoliver at gmail.com>
---
 defs.h    |   6 +++
 filesys.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 help.c    |  39 ++++++++++++++-
 memory.c  |  61 +++++++++++++++++++++++
 symbols.c |   2 +
 task.c    |  19 +++++--
 6 files changed, 271 insertions(+), 22 deletions(-)

diff --git a/defs.h b/defs.h
index b25b505..ba4e0d8 100644
--- a/defs.h
+++ b/defs.h
@@ -1940,6 +1940,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long task_struct_thread_reg31;
 	long pt_regs_regs;
 	long pt_regs_cp0_badvaddr;
+	long address_space_page_tree;
 };
 
 struct size_table {         /* stash of commonly-used sizes */
@@ -2598,6 +2599,7 @@ struct load_module {
 #define PRINT_SINGLE_VMA  (0x80)
 #define PRINT_RADIX_10   (0x100)
 #define PRINT_RADIX_16   (0x200)
+#define PRINT_PAGES      (0x400)
 
 #define MIN_PAGE_SIZE  (4096)
 
@@ -4707,6 +4709,8 @@ void alter_stackbuf(struct bt_info *);
 int vaddr_type(ulong, struct task_context *);
 char *format_stack_entry(struct bt_info *bt, char *, ulong, ulong);
 int in_user_stack(ulong, ulong);
+void dump_file_addr_mapping(ulong);
+long get_file_mapping_nrpages(ulong);
 
 /*
  *  filesys.c 
@@ -4743,6 +4747,7 @@ int is_readable(char *);
 #define RADIX_TREE_SEARCH  (2)
 #define RADIX_TREE_DUMP    (3)
 #define RADIX_TREE_GATHER  (4)
+#define RADIX_TREE_DUMP_CB (5)
 struct radix_tree_pair {
 	ulong index;
 	void *value;
@@ -4753,6 +4758,7 @@ int file_dump(ulong, ulong, ulong, int, int);
 #define DUMP_INODE_ONLY  2
 #define DUMP_DENTRY_ONLY 4
 #define DUMP_EMPTY_FILE  8
+#define DUMP_FILE_PAGE   16
 #endif  /* !GDB_COMMON */
 int same_file(char *, char *);
 #ifndef GDB_COMMON
diff --git a/filesys.c b/filesys.c
index 0573fe6..a54576f 100644
--- a/filesys.c
+++ b/filesys.c
@@ -49,7 +49,7 @@ static void *radix_tree_lookup(ulong, ulong, int);
 static int match_file_string(char *, char *, char *);
 static ulong get_root_vfsmount(char *);
 static void check_live_arch_mismatch(void);
-
+static void dump_file_addr_space(ulong);
 
 #define DENTRY_CACHE (20)
 #define INODE_CACHE  (20)
@@ -2167,6 +2167,50 @@ show_hit_rates:
 	}
 }
 
+static void
+dump_file_addr_space(ulong inode)
+{
+	char *inode_buf;
+	ulong i_mapping;
+	ulong nrpages;
+	char header[BUFSIZE];
+	char buf1[BUFSIZE];
+	char buf2[BUFSIZE];
+	char buf3[BUFSIZE];
+
+	inode_buf = GETBUF(SIZE(inode));
+	readmem(inode, KVADDR, inode_buf, SIZE(inode), "inode buffer",
+	    FAULT_ON_ERROR);
+
+	i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
+	nrpages = get_file_mapping_nrpages(i_mapping);
+
+	sprintf(header, "%s%s%s%sNRPAGES\n",
+		mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+		space(MINSPACE),
+		mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "MAPPING"),
+		space(MINSPACE));
+	fprintf(fp, "%s", header);
+
+	fprintf(fp, "%s%s%s%s%s\n\n",
+		mkstring(buf1, VADDR_PRLEN,
+		CENTER|RJUST|LONG_HEX,
+		MKSTR(inode)),
+		space(MINSPACE),
+		mkstring(buf2, VADDR_PRLEN,
+		CENTER|RJUST|LONG_HEX,
+		MKSTR(i_mapping)),
+		space(MINSPACE),
+		mkstring(buf3, strlen("NRPAGES"),
+		RJUST|LONG_DEC,
+		MKSTR(nrpages)));
+
+	dump_file_addr_mapping(i_mapping);
+
+	FREEBUF(inode_buf);
+	return;
+}
+
 /*
  *  This command displays information about the open files of a context.
  *  For each open file descriptor the file descriptor number, a pointer
@@ -2187,11 +2231,12 @@ cmd_files(void)
 	int subsequent;
 	struct reference reference, *ref;
 	char *refarg;
+	int open_flags = 0;
 
         ref = NULL;
         refarg = NULL;
 
-        while ((c = getopt(argcnt, args, "d:R:")) != EOF) {
+        while ((c = getopt(argcnt, args, "d:R:p:m")) != EOF) {
                 switch(c)
 		{
 		case 'R':
@@ -2210,6 +2255,24 @@ cmd_files(void)
 			display_dentry_info(value);
 			return;
 
+		case 'p':
+			if (VALID_MEMBER(address_space_page_tree) &&
+			    VALID_MEMBER(inode_i_mapping)) {
+				value = htol(optarg, FAULT_ON_ERROR, NULL);
+				dump_file_addr_space(value);
+			} else {
+				option_not_supported('p');
+			}
+			return;
+
+		case 'm':
+			if (VALID_MEMBER(address_space_page_tree) &&
+			    VALID_MEMBER(inode_i_mapping))
+				open_flags |= PRINT_PAGES;
+			else
+				option_not_supported('m');
+			break;
+
 		default:
 			argerrs++;
 			break;
@@ -2222,7 +2285,9 @@ cmd_files(void)
 	if (!args[optind]) {
 		if (!ref)
 			print_task_header(fp, CURRENT_CONTEXT(), 0);
-		open_files_dump(CURRENT_TASK(), 0, ref);
+
+		open_files_dump(CURRENT_TASK(), open_flags, ref);
+
 		return;
 	}
 
@@ -2241,7 +2306,7 @@ cmd_files(void)
                         for (tc = pid_to_context(value); tc; tc = tc->tc_next) {
                                 if (!ref)
                                         print_task_header(fp, tc, subsequent);
-                                open_files_dump(tc->task, 0, ref);
+                                open_files_dump(tc->task, open_flags, ref);
                                 fprintf(fp, "\n");
                         }
                         break;
@@ -2249,7 +2314,7 @@ cmd_files(void)
                 case STR_TASK:
                         if (!ref)
                                 print_task_header(fp, tc, subsequent);
-                        open_files_dump(tc->task, 0, ref);
+                        open_files_dump(tc->task, open_flags, ref);
                         break;
 
                 case STR_INVALID:
@@ -2321,6 +2386,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
 	char buf4[BUFSIZE];
 	char root_pwd[BUFSIZE];
 	int root_pwd_printed = 0;
+	int file_dump_flags = 0;
 
 	BZERO(root_pathname, BUFSIZE);
 	BZERO(pwd_pathname, BUFSIZE);
@@ -2329,15 +2395,26 @@ open_files_dump(ulong task, int flags, struct reference *ref)
 		fdtable_buf = GETBUF(SIZE(fdtable));
 	fill_task_struct(task);
 
-	sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
-		space(MINSPACE),
-		mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "FILE"),
-		space(MINSPACE),
-		mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
-		space(MINSPACE),
-		mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
-		space(MINSPACE),
-		space(MINSPACE));
+	if (flags & PRINT_PAGES) {
+		sprintf(files_header, " FD%s%s%s%s%sNRPAGES%sTYPE%sPATH\n",
+			space(MINSPACE),
+			mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+			space(MINSPACE),
+			mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "MAPPING"),
+			space(MINSPACE),
+			space(MINSPACE),
+			space(MINSPACE));
+	} else {
+		sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
+			space(MINSPACE),
+			mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "FILE"),
+			space(MINSPACE),
+			mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
+			space(MINSPACE),
+			mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+			space(MINSPACE),
+			space(MINSPACE));
+	}
 
 	tc = task_to_context(task);
 
@@ -2523,6 +2600,10 @@ open_files_dump(ulong task, int flags, struct reference *ref)
 		return;
 	}
 
+	file_dump_flags = DUMP_FULL_NAME | DUMP_EMPTY_FILE;
+	if (flags & PRINT_PAGES)
+		file_dump_flags |= DUMP_FILE_PAGE;
+
 	j = 0;
 	for (;;) {
 		unsigned long set;
@@ -2539,8 +2620,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
 
 				if (ref && file) {
 					open_tmpfile();
-                                        if (file_dump(file, 0, 0, i,
-                                            DUMP_FULL_NAME|DUMP_EMPTY_FILE)) {
+                                        if (file_dump(file, 0, 0, i, file_dump_flags)) {
 						BZERO(buf4, BUFSIZE);
 						rewind(pc->tmpfile);
 						ret = fgets(buf4, BUFSIZE, 
@@ -2558,8 +2638,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
 						fprintf(fp, "%s", files_header);
 						header_printed = 1;
 					}
-					file_dump(file, 0, 0, i, 
-						DUMP_FULL_NAME|DUMP_EMPTY_FILE);
+					file_dump(file, 0, 0, i, file_dump_flags);
 				}
 			}
 			i++;
@@ -2754,6 +2833,8 @@ file_dump(ulong file, ulong dentry, ulong inode, int fd, int flags)
 	char buf1[BUFSIZE];
 	char buf2[BUFSIZE];
 	char buf3[BUFSIZE];
+	ulong i_mapping = 0;
+	ulong nrpages = 0;
 
 	file_buf = NULL;
 
@@ -2863,6 +2944,28 @@ file_dump(ulong file, ulong dentry, ulong inode, int fd, int flags)
 				type, 
 				space(MINSPACE),
 				pathname+1);
+		} else if (flags & DUMP_FILE_PAGE) {
+			i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
+			nrpages = get_file_mapping_nrpages(i_mapping);
+
+			fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
+				fd,
+				space(MINSPACE),
+				mkstring(buf1, VADDR_PRLEN,
+				CENTER|RJUST|LONG_HEX,
+				MKSTR(inode)),
+				space(MINSPACE),
+				mkstring(buf2, VADDR_PRLEN,
+				CENTER|RJUST|LONG_HEX,
+				MKSTR(i_mapping)),
+				space(MINSPACE),
+				mkstring(buf3, strlen("NRPAGES"),
+				RJUST|LONG_DEC,
+				MKSTR(nrpages)),
+				space(MINSPACE),
+				type,
+				space(MINSPACE),
+				pathname);
 		} else {
                         fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
                                 fd,
@@ -3870,6 +3973,9 @@ ulong RADIX_TREE_MAP_MASK = UNINITIALIZED;
  *            limit the number of returned entries by putting the array size
  *            (max count) in the rtp->index field of the first structure 
  *            in the passed-in array.
+ *          RADIX_TREE_DUMP_CB - Similar with RADIX_TREE_DUMP, but for each
+ *            radix tree entry, a user defined callback at rtp->value will
+ *            be invoked.
  *
  *     rtp: Unused by RADIX_TREE_COUNT and RADIX_TREE_DUMP. 
  *          A pointer to a radix_tree_pair structure for RADIX_TREE_SEARCH.
@@ -3877,6 +3983,8 @@ ulong RADIX_TREE_MAP_MASK = UNINITIALIZED;
  *          RADIX_TREE_GATHER; the dimension (max count) of the array may
  *          be stored in the index field of the first structure to avoid
  *          any chance of an overrun.
+ *          For RADIX_TREE_DUMP_CB, the rtp->value need to be initialized as
+ *          callback function. The callback prototype must be int (*)(ulong);
  */
 ulong
 do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
@@ -3889,6 +3997,7 @@ do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
 	struct radix_tree_pair *r;
 	ulong root_rnode;
 	void *ret;
+	int (*cb)(ulong) = NULL;
 
 	count = 0;
 
@@ -3993,6 +4102,27 @@ do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
                 }
 		break;
 
+	case RADIX_TREE_DUMP_CB:
+		if (rtp->value == NULL) {
+			error(FATAL, "do_radix_tree: need set callback function");
+			return -EINVAL;
+		}
+		cb = (int (*)(ulong))rtp->value;
+		for (index = count = 0; index <= maxindex; index++) {
+			if ((ret =
+			    radix_tree_lookup(root_rnode, index, height))) {
+					/* Caller defined operation */
+				if (cb((ulong)ret) != 0) {
+					error(FATAL, "do_radix_tree: dump "
+					    "operation failed, count: %ld\n",
+					    count);
+					return -EIO;
+				}
+				count++;
+			}
+		}
+		break;
+
 	default:
 		error(FATAL, "do_radix_tree: invalid flag: %lx\n", flag);
 	}
diff --git a/help.c b/help.c
index f36316f..25df6e5 100644
--- a/help.c
+++ b/help.c
@@ -6488,7 +6488,7 @@ NULL
 char *help_files[] = {
 "files",
 "open files",
-"[-d dentry] | [-R reference] [pid | taskp] ... ",
+"[-d dentry] | [-p inode] | [-m] [-R reference] [pid | taskp] ... ",
 "  This command displays information about open files of a context.",
 "  It prints the context's current root directory and current working", 
 "  directory, and then for each open file descriptor it prints a pointer",
@@ -6501,6 +6501,10 @@ char *help_files[] = {
 "  specific, and only shows the data requested.\n",
 "     -d dentry  given a hexadecimal dentry address, display its inode,",
 "                super block, file type, and full pathname.",
+"     -p inode   given a hexadecimal inode address, dump all memory pages in",
+"                its address space.",
+"     -m         show inode memory mapping information, including mapping",
+"                address, page counts within the mapping.",
 "  -R reference  search for references to this file descriptor number,",
 "                filename, or dentry, inode, or file structure address.",
 "           pid  a process PID.",
@@ -6578,6 +6582,39 @@ char *help_files[] = {
 "    %s> files -d f745fd60",
 "     DENTRY    INODE    SUPERBLK  TYPE  PATH",
 "     f745fd60  f7284640  f73a3e00  REG   /var/spool/lpd/lpd.lock",
+" ",
+"  Show all tasks file mappings for REG file type:\n",
+"    %s> foreach files -m -R REG",
+"    PID: 1      TASK: f5c94000  CPU: 0   COMMAND: \"systemd\"",
+"    ROOT: /    CWD: /",
+"     FD   INODE    MAPPING   NRPAGES  TYPE  PATH",
+"     29  f5b7f338  f5b7f404        0  REG   /proc/1/mountinfo",
+"     32  f5b728f0  f5b729bc        0  REG   /proc/swaps",
+" ",
+"    PID: 241    TASK: f5fcb020  CPU: 0   COMMAND: \"systemd-journal\"",
+"    ROOT: /    CWD: /",
+"     FD   INODE    MAPPING   NRPAGES  TYPE  PATH",
+"     16  f560a820  f560a8ec     1359  REG   /var/log/journal/1f05.../system.journal",
+"     32  f3e42fb8  f3e43084        3  REG   /var/log/journal/1f05.../user-42.journal",
+"     38  f577efb8  f577f084      438  REG   /var/log/journal/1f05.../user-1000.journal",
+"     <...snipped...>",
+" ",
+"    PID: 280    TASK: f5d17020  CPU: 0   COMMAND: \"systemd-udevd\"",
+"    ROOT: /    CWD: /",
+"     FD   INODE    MAPPING   NRPAGES  TYPE  PATH",
+"      6  ea5adc0c  ea5adcd8        1  REG   /run/udev/queue.bin",
+"     11  f554efb8  f554f084        0  REG   /etc/udev/hwdb.bin",
+" ",
+"  Display file mapping and pages information about the inode at address f3e42fb8:\n",
+"    %s> files -p f3e42fb8",
+"    INODE    MAPPING   NRPAGES",
+"    f3e42fb8  f3e43084       3",
+" ",
+"      PAGE    PHYSICAL   MAPPING  INDEX CNT FLAGS",
+"    f71d4e60  1ebf3000  f3e43084      0  3 4002002c referenced,uptodate,lru,mappedtodisk",
+"    f6eabf80   577c000  f3e43084    394  2 4002006c referenced,uptodate,lru,active,mappedtodisk",
+"    f6e6fd60   396b000  f3e43084    396  2 4002006c referenced,uptodate,lru,active,mappedtodisk",
+" ",
 NULL               
 };
 
diff --git a/memory.c b/memory.c
index 765732b..973d4eb 100644
--- a/memory.c
+++ b/memory.c
@@ -292,6 +292,7 @@ static void dump_per_cpu_offsets(void);
 static void dump_page_flags(ulonglong);
 static ulong kmem_cache_nodelists(ulong);
 static void dump_hstates(void);
+static int dump_file_page(ulong);
 
 /*
  *  Memory display modes specific to this file.
@@ -476,6 +477,7 @@ vm_init(void)
 	MEMBER_OFFSET_INIT(block_device_bd_list, "block_device", "bd_list");
 	MEMBER_OFFSET_INIT(block_device_bd_disk, "block_device", "bd_disk");
 	MEMBER_OFFSET_INIT(inode_i_mapping, "inode", "i_mapping");
+	MEMBER_OFFSET_INIT(address_space_page_tree, "address_space", "page_tree");
 	MEMBER_OFFSET_INIT(address_space_nrpages, "address_space", "nrpages");
 	if (INVALID_MEMBER(address_space_nrpages))
 		MEMBER_OFFSET_INIT(address_space_nrpages, "address_space", "__nrpages");
@@ -6465,6 +6467,65 @@ translate_page_flags(char *buffer, ulong flags)
 }
 
 /*
+ * Radix page tree dump callback.
+ */
+static int
+dump_file_page(ulong page)
+{
+	struct meminfo meminfo;
+
+	BZERO(&meminfo, sizeof(struct meminfo));
+	meminfo.spec_addr = page;
+	meminfo.memtype = KVADDR;
+	meminfo.flags = ADDRESS_SPECIFIED;
+	dump_mem_map(&meminfo);
+
+	return 0;
+}
+
+/*
+ * The address space file mapping radix tree walker.
+ */
+void
+dump_file_addr_mapping(ulong i_mapping)
+{
+	ulong root_rnode;
+	struct radix_tree_pair rtp;
+
+	root_rnode = i_mapping + OFFSET(address_space_page_tree);
+
+	rtp.index = 0;
+	rtp.value = (void *)&dump_file_page;
+
+	/* Dump each pages in radix tree */
+	(void) do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &rtp);
+
+	return;
+}
+
+/*
+ * Get the page count for the specific mapping
+ */
+long
+get_file_mapping_nrpages(ulong i_mapping)
+{
+	ulong address_space = i_mapping;
+	char *address_space_buf;
+	ulong nrpages = 0;
+
+	address_space_buf = GETBUF(SIZE(address_space));
+
+	readmem(address_space, KVADDR, address_space_buf,
+	    SIZE(address_space), "address_space buffer",
+	    FAULT_ON_ERROR);
+	nrpages = ULONG(address_space_buf + OFFSET(address_space_nrpages));
+
+	FREEBUF(address_space_buf);
+
+	return nrpages;
+}
+
+/*
  *  dump_page_hash_table() displays the entries in each page_hash_table.
  */
 
diff --git a/symbols.c b/symbols.c
index 6acfcae..984cb55 100644
--- a/symbols.c
+++ b/symbols.c
@@ -8634,6 +8634,8 @@ dump_offset_table(char *spec, ulong makestruct)
 		OFFSET(block_device_bd_disk));
 	fprintf(fp, "         address_space_nrpages: %ld\n",
 		OFFSET(address_space_nrpages));
+	fprintf(fp, "         address_space_page_tree: %ld\n",
+		OFFSET(address_space_page_tree));
 	fprintf(fp, "                 gendisk_major: %ld\n",
 		OFFSET(gendisk_major));
 	fprintf(fp, "                  gendisk_fops: %ld\n",
diff --git a/task.c b/task.c
index 3a88d68..5fe650b 100644
--- a/task.c
+++ b/task.c
@@ -6234,6 +6234,13 @@ foreach(struct foreach_data *fd)
 			print_header = FALSE;
 			break;
 
+		case FOREACH_FILES:
+			if (fd->flags & FOREACH_p_FLAG)
+				error(FATAL,
+				    "foreach files command does not "
+				    "support -p option\n");
+			break;
+
 		case FOREACH_TEST:
 			break;
 		}
@@ -6460,9 +6467,15 @@ foreach(struct foreach_data *fd)
 
 			case FOREACH_FILES:
 				pc->curcmd = "files";
-				open_files_dump(tc->task, 
-					fd->flags & FOREACH_i_FLAG ?
-					PRINT_INODES : 0, 
+				cmdflags = 0;
+
+				if (fd->flags & FOREACH_i_FLAG)
+					cmdflags |= PRINT_INODES;
+				if (fd->flags & FOREACH_m_FLAG)
+					cmdflags |= PRINT_PAGES;
+
+				open_files_dump(tc->task,
+					cmdflags,
 					fd->reference ? ref : NULL);
 				break;
 
-- 
2.4.0




More information about the Crash-utility mailing list