Commit dad94ca7 authored by Wu Fengguang's avatar Wu Fengguang Committed by James Toy

Introduce "-p|--pid <pid>" for walking the process address space. The

default action is to walk raw memory PFNs.

Both the virtual address and physhcal address of each present pages will be listed:

	# ./tools/vm/page-types -lp $$ | head -3
	voffset offset  len     flags
	400     11bebe  1       __RU_lA____M______________________
	402     11bebc  1       __RU_lA____M______________________

Note that voffset/offset/len are now showed as hex numbers.

Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: default avatarWu Fengguang <fengguang.wu@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent e124b201
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright (C) 2009 Wu Fengguang <fengguang.wu@intel.com> * Copyright (C) 2009 Wu Fengguang <fengguang.wu@intel.com>
*/ */
#define _LARGEFILE64_SOURCE
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
...@@ -13,11 +14,32 @@ ...@@ -13,11 +14,32 @@
#include <string.h> #include <string.h>
#include <getopt.h> #include <getopt.h>
#include <limits.h> #include <limits.h>
#include <assert.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/errno.h> #include <sys/errno.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
/*
* pagemap kernel ABI bits
*/
#define PM_ENTRY_BYTES sizeof(uint64_t)
#define PM_STATUS_BITS 3
#define PM_STATUS_OFFSET (64 - PM_STATUS_BITS)
#define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET)
#define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK)
#define PM_PSHIFT_BITS 6
#define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS)
#define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET)
#define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK)
#define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1)
#define PM_PFRAME(x) ((x) & PM_PFRAME_MASK)
#define PM_PRESENT PM_STATUS(4LL)
#define PM_SWAP PM_STATUS(2LL)
/* /*
* kernel page flags * kernel page flags
*/ */
...@@ -126,6 +148,14 @@ static int nr_addr_ranges; ...@@ -126,6 +148,14 @@ static int nr_addr_ranges;
static unsigned long opt_offset[MAX_ADDR_RANGES]; static unsigned long opt_offset[MAX_ADDR_RANGES];
static unsigned long opt_size[MAX_ADDR_RANGES]; static unsigned long opt_size[MAX_ADDR_RANGES];
#define MAX_VMAS 10240
static int nr_vmas;
static unsigned long pg_start[MAX_VMAS];
static unsigned long pg_end[MAX_VMAS];
static unsigned long voffset;
static int pagemap_fd;
#define MAX_BIT_FILTERS 64 #define MAX_BIT_FILTERS 64
static int nr_bit_filters; static int nr_bit_filters;
static uint64_t opt_mask[MAX_BIT_FILTERS]; static uint64_t opt_mask[MAX_BIT_FILTERS];
...@@ -135,7 +165,6 @@ static int page_size; ...@@ -135,7 +165,6 @@ static int page_size;
#define PAGES_BATCH (64 << 10) /* 64k pages */ #define PAGES_BATCH (64 << 10) /* 64k pages */
static int kpageflags_fd; static int kpageflags_fd;
static uint64_t kpageflags_buf[KPF_BYTES * PAGES_BATCH];
#define HASH_SHIFT 13 #define HASH_SHIFT 13
#define HASH_SIZE (1 << HASH_SHIFT) #define HASH_SIZE (1 << HASH_SHIFT)
...@@ -158,6 +187,11 @@ static uint64_t page_flags[HASH_SIZE]; ...@@ -158,6 +187,11 @@ static uint64_t page_flags[HASH_SIZE];
type __min2 = (y); \ type __min2 = (y); \
__min1 < __min2 ? __min1 : __min2; }) __min1 < __min2 ? __min1 : __min2; })
#define max_t(type, x, y) ({ \
type __max1 = (x); \
type __max2 = (y); \
__max1 > __max2 ? __max1: __max2; })
static unsigned long pages2mb(unsigned long pages) static unsigned long pages2mb(unsigned long pages)
{ {
return (pages * page_size) >> 20; return (pages * page_size) >> 20;
...@@ -224,26 +258,34 @@ static char *page_flag_longname(uint64_t flags) ...@@ -224,26 +258,34 @@ static char *page_flag_longname(uint64_t flags)
static void show_page_range(unsigned long offset, uint64_t flags) static void show_page_range(unsigned long offset, uint64_t flags)
{ {
static uint64_t flags0; static uint64_t flags0;
static unsigned long voff;
static unsigned long index; static unsigned long index;
static unsigned long count; static unsigned long count;
if (flags == flags0 && offset == index + count) { if (flags == flags0 && offset == index + count &&
(!opt_pid || voffset == voff + count)) {
count++; count++;
return; return;
} }
if (count) if (count) {
printf("%lu\t%lu\t%s\n", if (opt_pid)
printf("%lx\t", voff);
printf("%lx\t%lx\t%s\n",
index, count, page_flag_name(flags0)); index, count, page_flag_name(flags0));
}
flags0 = flags; flags0 = flags;
index = offset; index = offset;
voff = voffset;
count = 1; count = 1;
} }
static void show_page(unsigned long offset, uint64_t flags) static void show_page(unsigned long offset, uint64_t flags)
{ {
printf("%lu\t%s\n", offset, page_flag_name(flags)); if (opt_pid)
printf("%lx\t", voffset);
printf("%lx\t%s\n", offset, page_flag_name(flags));
} }
static void show_summary(void) static void show_summary(void)
...@@ -383,6 +425,8 @@ static void walk_pfn(unsigned long index, unsigned long count) ...@@ -383,6 +425,8 @@ static void walk_pfn(unsigned long index, unsigned long count)
lseek(kpageflags_fd, index * KPF_BYTES, SEEK_SET); lseek(kpageflags_fd, index * KPF_BYTES, SEEK_SET);
while (count) { while (count) {
uint64_t kpageflags_buf[KPF_BYTES * PAGES_BATCH];
batch = min_t(unsigned long, count, PAGES_BATCH); batch = min_t(unsigned long, count, PAGES_BATCH);
n = read(kpageflags_fd, kpageflags_buf, batch * KPF_BYTES); n = read(kpageflags_fd, kpageflags_buf, batch * KPF_BYTES);
if (n == 0) if (n == 0)
...@@ -404,6 +448,81 @@ static void walk_pfn(unsigned long index, unsigned long count) ...@@ -404,6 +448,81 @@ static void walk_pfn(unsigned long index, unsigned long count)
} }
} }
#define PAGEMAP_BATCH 4096
static unsigned long task_pfn(unsigned long pgoff)
{
static uint64_t buf[PAGEMAP_BATCH];
static unsigned long start;
static long count;
uint64_t pfn;
if (pgoff < start || pgoff >= start + count) {
if (lseek64(pagemap_fd,
(uint64_t)pgoff * PM_ENTRY_BYTES,
SEEK_SET) < 0) {
perror("pagemap seek");
exit(EXIT_FAILURE);
}
count = read(pagemap_fd, buf, sizeof(buf));
if (count == 0)
return 0;
if (count < 0) {
perror("pagemap read");
exit(EXIT_FAILURE);
}
if (count % PM_ENTRY_BYTES) {
fatal("pagemap read not aligned.\n");
exit(EXIT_FAILURE);
}
count /= PM_ENTRY_BYTES;
start = pgoff;
}
pfn = buf[pgoff - start];
if (pfn & PM_PRESENT)
pfn = PM_PFRAME(pfn);
else
pfn = 0;
return pfn;
}
static void walk_task(unsigned long index, unsigned long count)
{
int i = 0;
const unsigned long end = index + count;
while (index < end) {
while (pg_end[i] <= index)
if (++i >= nr_vmas)
return;
if (pg_start[i] >= end)
return;
voffset = max_t(unsigned long, pg_start[i], index);
index = min_t(unsigned long, pg_end[i], end);
assert(voffset < index);
for (; voffset < index; voffset++) {
unsigned long pfn = task_pfn(voffset);
if (pfn)
walk_pfn(pfn, 1);
}
}
}
static void add_addr_range(unsigned long offset, unsigned long size)
{
if (nr_addr_ranges >= MAX_ADDR_RANGES)
fatal("too many addr ranges\n");
opt_offset[nr_addr_ranges] = offset;
opt_size[nr_addr_ranges] = min_t(unsigned long, size, ULONG_MAX-offset);
nr_addr_ranges++;
}
static void walk_addr_ranges(void) static void walk_addr_ranges(void)
{ {
int i; int i;
...@@ -415,10 +534,13 @@ static void walk_addr_ranges(void) ...@@ -415,10 +534,13 @@ static void walk_addr_ranges(void)
} }
if (!nr_addr_ranges) if (!nr_addr_ranges)
walk_pfn(0, ULONG_MAX); add_addr_range(0, ULONG_MAX);
for (i = 0; i < nr_addr_ranges; i++) for (i = 0; i < nr_addr_ranges; i++)
if (!opt_pid)
walk_pfn(opt_offset[i], opt_size[i]); walk_pfn(opt_offset[i], opt_size[i]);
else
walk_task(opt_offset[i], opt_size[i]);
close(kpageflags_fd); close(kpageflags_fd);
} }
...@@ -446,8 +568,8 @@ static void usage(void) ...@@ -446,8 +568,8 @@ static void usage(void)
" -r|--raw Raw mode, for kernel developers\n" " -r|--raw Raw mode, for kernel developers\n"
" -a|--addr addr-spec Walk a range of pages\n" " -a|--addr addr-spec Walk a range of pages\n"
" -b|--bits bits-spec Walk pages with specified bits\n" " -b|--bits bits-spec Walk pages with specified bits\n"
#if 0 /* planned features */
" -p|--pid pid Walk process address space\n" " -p|--pid pid Walk process address space\n"
#if 0 /* planned features */
" -f|--file filename Walk file address space\n" " -f|--file filename Walk file address space\n"
#endif #endif
" -l|--list Show page details in ranges\n" " -l|--list Show page details in ranges\n"
...@@ -459,7 +581,7 @@ static void usage(void) ...@@ -459,7 +581,7 @@ static void usage(void)
" N+M pages range from N to N+M-1\n" " N+M pages range from N to N+M-1\n"
" N,M pages range from N to M-1\n" " N,M pages range from N to M-1\n"
" N, pages range from N to end\n" " N, pages range from N to end\n"
" ,M pages range from 0 to M\n" " ,M pages range from 0 to M-1\n"
"bits-spec:\n" "bits-spec:\n"
" bit1,bit2 (flags & (bit1|bit2)) != 0\n" " bit1,bit2 (flags & (bit1|bit2)) != 0\n"
" bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n" " bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n"
...@@ -496,21 +618,57 @@ static unsigned long long parse_number(const char *str) ...@@ -496,21 +618,57 @@ static unsigned long long parse_number(const char *str)
static void parse_pid(const char *str) static void parse_pid(const char *str)
{ {
FILE *file;
char buf[5000];
opt_pid = parse_number(str); opt_pid = parse_number(str);
}
static void parse_file(const char *name) sprintf(buf, "/proc/%d/pagemap", opt_pid);
{ pagemap_fd = open(buf, O_RDONLY);
if (pagemap_fd < 0) {
perror(buf);
exit(EXIT_FAILURE);
}
sprintf(buf, "/proc/%d/maps", opt_pid);
file = fopen(buf, "r");
if (!file) {
perror(buf);
exit(EXIT_FAILURE);
}
while (fgets(buf, sizeof(buf), file) != NULL) {
unsigned long vm_start;
unsigned long vm_end;
unsigned long long pgoff;
int major, minor;
char r, w, x, s;
unsigned long ino;
int n;
n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu",
&vm_start,
&vm_end,
&r, &w, &x, &s,
&pgoff,
&major, &minor,
&ino);
if (n < 10) {
fprintf(stderr, "unexpected line: %s\n", buf);
continue;
}
pg_start[nr_vmas] = vm_start / page_size;
pg_end[nr_vmas] = vm_end / page_size;
if (++nr_vmas >= MAX_VMAS) {
fprintf(stderr, "too many VMAs\n");
break;
}
}
fclose(file);
} }
static void add_addr_range(unsigned long offset, unsigned long size) static void parse_file(const char *name)
{ {
if (nr_addr_ranges >= MAX_ADDR_RANGES)
fatal("too much addr ranges\n");
opt_offset[nr_addr_ranges] = offset;
opt_size[nr_addr_ranges] = size;
nr_addr_ranges++;
} }
static void parse_addr_range(const char *optarg) static void parse_addr_range(const char *optarg)
...@@ -676,8 +834,10 @@ int main(int argc, char *argv[]) ...@@ -676,8 +834,10 @@ int main(int argc, char *argv[])
} }
} }
if (opt_list && opt_pid)
printf("voffset\t");
if (opt_list == 1) if (opt_list == 1)
printf("offset\tcount\tflags\n"); printf("offset\tlen\tflags\n");
if (opt_list == 2) if (opt_list == 2)
printf("offset\tflags\n"); printf("offset\tflags\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment