Implementation of two new "files" command options. The "files -c"

option is context-sensitive, similar to the the regular "files"
command when used without an argument, but replaces the FILE and
DENTRY columns with I_MAPPING and NRPAGES columns that reflect
each open file's inode.i_mapping address_space structure address,
and the address_space.nrpages count within it; this shows how
many of each open file's pages are currently in the system's
page cache.  The "files -p <inode>" option takes the address
of an inode, and dumps all of its pages that are currently in the
system's page cache, borrowing the "kmem -p" page structure output.
(yangoliver@gmail.com)
This commit is contained in:
Dave Anderson 2015-07-02 15:16:53 -04:00
parent 7a2ff137fe
commit 3106fee2be
6 changed files with 256 additions and 34 deletions

14
defs.h
View File

@ -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_NRPAGES (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);
int dump_inode_page(ulong);
/*
* filesys.c
@ -4743,16 +4747,18 @@ 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;
};
ulong do_radix_tree(ulong, int, struct radix_tree_pair *);
int file_dump(ulong, ulong, ulong, int, int);
#define DUMP_FULL_NAME 1
#define DUMP_INODE_ONLY 2
#define DUMP_DENTRY_ONLY 4
#define DUMP_EMPTY_FILE 8
#define DUMP_FULL_NAME 0x1
#define DUMP_INODE_ONLY 0x2
#define DUMP_DENTRY_ONLY 0x4
#define DUMP_EMPTY_FILE 0x8
#define DUMP_FILE_NRPAGES 0x10
#endif /* !GDB_COMMON */
int same_file(char *, char *);
#ifndef GDB_COMMON

197
filesys.c
View File

@ -49,7 +49,8 @@ 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 long get_inode_nrpages(ulong);
static void dump_inode_page_cache_info(ulong);
#define DENTRY_CACHE (20)
#define INODE_CACHE (20)
@ -2167,6 +2168,70 @@ show_hit_rates:
}
}
/*
* Get the page count for the specific mapping
*/
static long
get_inode_nrpages(ulong i_mapping)
{
char *address_space_buf;
ulong nrpages;
address_space_buf = GETBUF(SIZE(address_space));
readmem(i_mapping, 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;
}
static void
dump_inode_page_cache_info(ulong inode)
{
char *inode_buf;
ulong i_mapping, nrpages, root_rnode, count;
struct radix_tree_pair rtp;
char header[BUFSIZE];
char buf1[BUFSIZE];
char buf2[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_inode_nrpages(i_mapping);
sprintf(header, "%s NRPAGES\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"));
fprintf(fp, "%s", header);
fprintf(fp, "%s %s\n\n",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
mkstring(buf2, strlen("NRPAGES"),
RJUST|LONG_DEC,
MKSTR(nrpages)));
root_rnode = i_mapping + OFFSET(address_space_page_tree);
rtp.index = 0;
rtp.value = (void *)&dump_inode_page;
count = do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &rtp);
if (count != nrpages)
error(INFO, "page_tree count: %ld nrpages: %ld\n",
count, nrpages);
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 +2252,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:c")) != EOF) {
switch(c)
{
case 'R':
@ -2210,6 +2276,23 @@ 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_inode_page_cache_info(value);
} else
option_not_supported('p');
return;
case 'c':
if (VALID_MEMBER(address_space_page_tree) &&
VALID_MEMBER(inode_i_mapping))
open_flags |= PRINT_NRPAGES;
else
option_not_supported('c');
break;
default:
argerrs++;
break;
@ -2222,7 +2305,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 +2326,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 +2334,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 +2406,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 +2415,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_NRPAGES) {
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, MAX(VADDR_PRLEN, strlen("I_MAPPING")),
BITS32() ? (CENTER|RJUST) : (CENTER|LJUST), "I_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 +2621,10 @@ open_files_dump(ulong task, int flags, struct reference *ref)
return;
}
file_dump_flags = DUMP_FULL_NAME | DUMP_EMPTY_FILE;
if (flags & PRINT_NRPAGES)
file_dump_flags |= DUMP_FILE_NRPAGES;
j = 0;
for (;;) {
unsigned long set;
@ -2539,8 +2641,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 +2659,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 +2854,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 +2965,28 @@ file_dump(ulong file, ulong dentry, ulong inode, int fd, int flags)
type,
space(MINSPACE),
pathname+1);
} else if (flags & DUMP_FILE_NRPAGES) {
i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
nrpages = get_inode_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, MAX(VADDR_PRLEN, strlen("I_MAPPING")),
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 +3994,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 +4004,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 must be initialized as a
* callback function. The callback prototype must be: int (*)(ulong);
*/
ulong
do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
@ -3889,6 +4018,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;
@ -3932,14 +4062,13 @@ do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
"radix_tree_root", FAULT_ON_ERROR);
height = UINT(radix_tree_root_buf + OFFSET(radix_tree_root_height));
if (height > ilen) {
fprintf(fp, "radix_tree_root at %lx:\n", root);
if ((height < 0) || (height > ilen)) {
error(INFO, "height_to_maxindex[] index: %ld\n", ilen);
fprintf(fp, "invalid height in radix_tree_root at %lx:\n", root);
dump_struct("radix_tree_root", (ulong)root, RADIX(16));
error(FATAL,
"height %d is greater than height_to_maxindex[] index %ld\n",
height, ilen);
return 0;
}
maxindex = height_to_maxindex[height];
FREEBUF(height_to_maxindex);
FREEBUF(radix_tree_root_buf);
@ -3993,6 +4122,26 @@ 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)) {
error(FATAL, "do_radix_tree: callback "
"operation failed: entry: %ld item: %lx\n",
count, (ulong)ret);
}
count++;
}
}
break;
default:
error(FATAL, "do_radix_tree: invalid flag: %lx\n", flag);
}

41
help.c
View File

@ -829,7 +829,7 @@ char *help_foreach[] = {
" -o -s -x -d)",
" vm run the \"vm\" command (optional flags: -p -v -m -R -d -x)",
" task run the \"task\" command (optional flags: -R -d -x)",
" files run the \"files\" command (optional flag: -R)",
" files run the \"files\" command (optional flag: -c -R)",
" net run the \"net\" command (optional flags: -s -S -R -d -x)",
" set run the \"set\" command",
" ps run the \"ps\" command (optional flags: -G -s -p -c -t -l -a",
@ -6488,7 +6488,7 @@ NULL
char *help_files[] = {
"files",
"open files",
"[-d dentry] | [-R reference] [pid | taskp] ... ",
"[-d dentry] | [-p inode] | [-c] [-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,8 +6501,15 @@ 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 of its pages",
" that are in the page cache.",
" -c for each open file descriptor, prints a pointer to its",
" inode, a pointer to the inode's i_mapping address_space",
" structure, the number of pages of the inode that are in",
" the page cache, the file type, and the pathname.",
" -R reference search for references to this file descriptor number,",
" filename, or dentry, inode, or file structure address.",
" filename, dentry, inode, address_space, or file structure",
" address.",
" pid a process PID.",
" taskp a hexadecimal task_struct pointer.",
"\nEXAMPLES",
@ -6578,6 +6585,34 @@ char *help_files[] = {
" %s> files -d f745fd60",
" DENTRY INODE SUPERBLK TYPE PATH",
" f745fd60 f7284640 f73a3e00 REG /var/spool/lpd/lpd.lock",
" ",
" For each open file, display the number of pages that are in the page cache:\n",
" %s> files -c 1954",
" PID: 1954 TASK: f7a28000 CPU: 1 COMMAND: \"syslogd\"",
" ROOT: / CWD: /",
" FD INODE I_MAPPING NRPAGES TYPE PATH",
" 0 cb3ae868 cb3ae910 0 SOCK socket:/[4690]",
" 2 f2721c5c f2721d04 461 REG /var/log/messages",
" 3 cbda4884 cbda492c 47 REG /var/log/secure",
" 4 e48092c0 e4809368 58 REG /var/log/maillog",
" 5 f65192c0 f6519368 48 REG /var/log/cron",
" 6 e4809e48 e4809ef0 0 REG /var/log/spooler",
" 7 d9c43884 d9c4392c 0 REG /var/log/boot.log",
" ",
" For the inode at address f59b90fc, display all of its pages that are in",
" the page cache:\n",
" %s> files -p f59b90fc",
" INODE NRPAGES",
" f59b90fc 6",
" ",
" PAGE PHYSICAL MAPPING INDEX CNT FLAGS",
" ca3353e0 39a9f000 f59b91ac 0 2 82c referenced,uptodate,lru,private",
" ca22cb20 31659000 f59b91ac 1 2 82c referenced,uptodate,lru,private",
" ca220160 3100b000 f59b91ac 2 2 82c referenced,uptodate,lru,private",
" ca1ddde0 2eeef000 f59b91ac 3 2 82c referenced,uptodate,lru,private",
" ca36b300 3b598000 f59b91ac 4 2 82c referenced,uptodate,lru,private",
" ca202680 30134000 f59b91ac 5 2 82c referenced,uptodate,lru,private",
" ",
NULL
};

View File

@ -476,6 +476,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");
@ -6464,6 +6465,23 @@ translate_page_flags(char *buffer, ulong flags)
return(strlen(buf));
}
/*
* Display the mem_map data for a single page.
*/
int
dump_inode_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 meminfo.retval;
}
/*
* dump_page_hash_table() displays the entries in each page_hash_table.
*/

View File

@ -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",

18
task.c
View File

@ -6234,6 +6234,12 @@ foreach(struct foreach_data *fd)
print_header = FALSE;
break;
case FOREACH_FILES:
if (fd->flags & FOREACH_p_FLAG)
error(FATAL,
"files command does not support -p 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_c_FLAG)
cmdflags |= PRINT_NRPAGES;
open_files_dump(tc->task,
cmdflags,
fd->reference ? ref : NULL);
break;