Dave,
This is the v3 of patch for memory mapping dump support in files cmd.
This version patch fixed following problems,
1. Rebased to latest crash upstream.
2. Cleanup unnecessary code in defs.h.
3. Add RADIX_TREE_DUMP_CB, make do_radix_tree API unchange for other flags.
4. Fixed line up problems on 32bit kernel
ADDR_SPACE got changed by MAPPING.
PAGE-COUNT got changed by PAGE-CNT.
5. New files CLI options
-m replaced -M, and -a replaced original -m
files -a use inode address instead of address space.
This change make files -a more separate with files -m.
I did see the debug scenario that use file -a separately.
6. Added basic helps information.
Will update exmaple info later, after review is done.
Below is the v3 patch, let me know your comments.
Added two options in files command,
1. -m option, which allows dump file mapping and
page count for each files
2. -a 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(a)gmail.com>
---
defs.h | 6 +++
filesys.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
help.c | 6 ++-
memory.c | 64 ++++++++++++++++++++++++++++
symbols.c | 2 +
task.c | 22 +++++++---
6 files changed, 218 insertions(+), 24 deletions(-)
diff --git a/defs.h b/defs.h
index b25b505..48dc59f 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_mappings(ulong);
+long get_page_tree_count(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..a83224f 100644
--- a/filesys.c
+++ b/filesys.c
@@ -49,6 +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)
@@ -2167,6 +2168,23 @@ show_hit_rates:
}
}
+static void
+dump_file_addr_space(ulong inode)
+{
+ ulong address_space;
+ char *inode_buf;
+
+ inode_buf = GETBUF(SIZE(inode));
+ readmem(inode, KVADDR, inode_buf, SIZE(inode), "inode buffer",
+ FAULT_ON_ERROR);
+
+ address_space = ULONG(inode_buf + OFFSET(inode_i_mapping));
+
+ dump_file_addr_mappings(address_space);
+
+ FREEBUF(inode_buf);
+}
+
/*
* This command displays information about the open files of a context.
* For each open file descriptor the file descriptor number, a pointer
@@ -2187,11 +2205,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:a:m")) != EOF) {
switch(c)
{
case 'R':
@@ -2210,6 +2229,24 @@ cmd_files(void)
display_dentry_info(value);
return;
+ case 'a':
+ 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('a');
+ }
+ 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 +2259,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 +2280,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 +2288,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 +2360,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 +2369,27 @@ 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%s%s%sTYPE%sPATH\n",
+ space(MINSPACE),
+ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+ space(MINSPACE),
+ mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "MAPPING"),
+ space(MINSPACE),
+ mkstring(buf3, LONG_PRLEN, CENTER|LJUST, "PAGE-CNT"),
+ 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);
@@ -2420,9 +2472,10 @@ open_files_dump(ulong task, int flags, struct reference *ref)
root_pwd_printed = TRUE;
ref->cmdflags |= FILES_REF_FOUND;
}
- } else
+ } else {
fprintf(fp, "ROOT: %s CWD: %s\n",
root_pathname, pwd_pathname);
+ }
FREEBUF(fs_struct_buf);
}
@@ -2523,6 +2576,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 +2596,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 +2614,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 +2809,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 count = 0;
file_buf = NULL;
@@ -2863,6 +2920,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));
+ count = get_page_tree_count(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, LONG_PRLEN,
+ CENTER|RJUST|LONG_DEC,
+ MKSTR(count)),
+ space(MINSPACE),
+ type,
+ space(MINSPACE),
+ pathname);
} else {
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
@@ -3870,6 +3949,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 +3959,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 +3973,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 +4078,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..1f7035d 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] | [-a 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.",
+" -a 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.",
diff --git a/memory.c b/memory.c
index 765732b..f3ebdcb 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,68 @@ translate_page_flags(char *buffer, ulong flags)
}
/*
+ * Page tree dump ops.
+ */
+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_mappings(ulong i_mapping)
+{
+ ulong root_rnode;
+ ulong count;
+ struct radix_tree_pair rtp;
+
+ root_rnode = i_mapping + OFFSET(address_space_page_tree);
+ count = get_page_tree_count(i_mapping);
+ fprintf(fp, "Address space %lx, %ld pages\n\n", i_mapping, count);
+
+ 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_page_tree_count(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..11679c8 100644
--- a/task.c
+++ b/task.c
@@ -5685,7 +5685,7 @@ cmd_foreach(void)
BZERO(&foreach_data, sizeof(struct foreach_data));
fd = &foreach_data;
- while ((c = getopt(argcnt, args, "R:vomlgersStTpukcfFxhdaG")) != EOF)
{
+ while ((c = getopt(argcnt, args, "R:vomMlgersStTpukcfFxhdaG")) != EOF)
{
switch(c)
{
case 'R':
@@ -5708,7 +5708,6 @@ cmd_foreach(void)
case 'm':
fd->flags |= FOREACH_m_FLAG;
break;
-
case 'l':
fd->flags |= FOREACH_l_FLAG;
break;
@@ -6234,6 +6233,13 @@ foreach(struct foreach_data *fd)
print_header = FALSE;
break;
+ case FOREACH_FILES:
+ if (fd->flags & FOREACH_a_FLAG)
+ error(FATAL,
+ "foreach files command does not "
+ "support -a option\n");
+ break;
+
case FOREACH_TEST:
break;
}
@@ -6460,9 +6466,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;
--
1.9.1