blob: 87f57228f56e51cc674b5cb5e6c4b91174b8eaef [file] [log] [blame]
Wu Fengguang35efa5e2009-06-16 15:32:27 -07001/*
2 * page-types: Tool for querying page flags
3 *
4 * Copyright (C) 2009 Intel corporation
5 * Copyright (C) 2009 Wu Fengguang <fengguang.wu@intel.com>
6 */
7
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -07008#define _LARGEFILE64_SOURCE
Wu Fengguang35efa5e2009-06-16 15:32:27 -07009#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <stdint.h>
13#include <stdarg.h>
14#include <string.h>
15#include <getopt.h>
16#include <limits.h>
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -070017#include <assert.h>
Wu Fengguang35efa5e2009-06-16 15:32:27 -070018#include <sys/types.h>
19#include <sys/errno.h>
20#include <sys/fcntl.h>
21
22
23/*
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -070024 * pagemap kernel ABI bits
25 */
26
27#define PM_ENTRY_BYTES sizeof(uint64_t)
28#define PM_STATUS_BITS 3
29#define PM_STATUS_OFFSET (64 - PM_STATUS_BITS)
30#define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET)
31#define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK)
32#define PM_PSHIFT_BITS 6
33#define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS)
34#define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET)
35#define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK)
36#define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1)
37#define PM_PFRAME(x) ((x) & PM_PFRAME_MASK)
38
39#define PM_PRESENT PM_STATUS(4LL)
40#define PM_SWAP PM_STATUS(2LL)
41
42
43/*
Wu Fengguang35efa5e2009-06-16 15:32:27 -070044 * kernel page flags
45 */
46
47#define KPF_BYTES 8
48#define PROC_KPAGEFLAGS "/proc/kpageflags"
49
50/* copied from kpageflags_read() */
51#define KPF_LOCKED 0
52#define KPF_ERROR 1
53#define KPF_REFERENCED 2
54#define KPF_UPTODATE 3
55#define KPF_DIRTY 4
56#define KPF_LRU 5
57#define KPF_ACTIVE 6
58#define KPF_SLAB 7
59#define KPF_WRITEBACK 8
60#define KPF_RECLAIM 9
61#define KPF_BUDDY 10
62
63/* [11-20] new additions in 2.6.31 */
64#define KPF_MMAP 11
65#define KPF_ANON 12
66#define KPF_SWAPCACHE 13
67#define KPF_SWAPBACKED 14
68#define KPF_COMPOUND_HEAD 15
69#define KPF_COMPOUND_TAIL 16
70#define KPF_HUGE 17
71#define KPF_UNEVICTABLE 18
Wu Fengguang253fb022009-10-07 16:32:27 -070072#define KPF_HWPOISON 19
Wu Fengguang35efa5e2009-06-16 15:32:27 -070073#define KPF_NOPAGE 20
74
75/* [32-] kernel hacking assistances */
76#define KPF_RESERVED 32
77#define KPF_MLOCKED 33
78#define KPF_MAPPEDTODISK 34
79#define KPF_PRIVATE 35
80#define KPF_PRIVATE_2 36
81#define KPF_OWNER_PRIVATE 37
82#define KPF_ARCH 38
83#define KPF_UNCACHED 39
84
85/* [48-] take some arbitrary free slots for expanding overloaded flags
86 * not part of kernel API
87 */
88#define KPF_READAHEAD 48
89#define KPF_SLOB_FREE 49
90#define KPF_SLUB_FROZEN 50
91#define KPF_SLUB_DEBUG 51
92
93#define KPF_ALL_BITS ((uint64_t)~0ULL)
94#define KPF_HACKERS_BITS (0xffffULL << 32)
95#define KPF_OVERLOADED_BITS (0xffffULL << 48)
96#define BIT(name) (1ULL << KPF_##name)
97#define BITS_COMPOUND (BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL))
98
99static char *page_flag_names[] = {
100 [KPF_LOCKED] = "L:locked",
101 [KPF_ERROR] = "E:error",
102 [KPF_REFERENCED] = "R:referenced",
103 [KPF_UPTODATE] = "U:uptodate",
104 [KPF_DIRTY] = "D:dirty",
105 [KPF_LRU] = "l:lru",
106 [KPF_ACTIVE] = "A:active",
107 [KPF_SLAB] = "S:slab",
108 [KPF_WRITEBACK] = "W:writeback",
109 [KPF_RECLAIM] = "I:reclaim",
110 [KPF_BUDDY] = "B:buddy",
111
112 [KPF_MMAP] = "M:mmap",
113 [KPF_ANON] = "a:anonymous",
114 [KPF_SWAPCACHE] = "s:swapcache",
115 [KPF_SWAPBACKED] = "b:swapbacked",
116 [KPF_COMPOUND_HEAD] = "H:compound_head",
117 [KPF_COMPOUND_TAIL] = "T:compound_tail",
118 [KPF_HUGE] = "G:huge",
119 [KPF_UNEVICTABLE] = "u:unevictable",
Wu Fengguang253fb022009-10-07 16:32:27 -0700120 [KPF_HWPOISON] = "X:hwpoison",
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700121 [KPF_NOPAGE] = "n:nopage",
122
123 [KPF_RESERVED] = "r:reserved",
124 [KPF_MLOCKED] = "m:mlocked",
125 [KPF_MAPPEDTODISK] = "d:mappedtodisk",
126 [KPF_PRIVATE] = "P:private",
127 [KPF_PRIVATE_2] = "p:private_2",
128 [KPF_OWNER_PRIVATE] = "O:owner_private",
129 [KPF_ARCH] = "h:arch",
130 [KPF_UNCACHED] = "c:uncached",
131
132 [KPF_READAHEAD] = "I:readahead",
133 [KPF_SLOB_FREE] = "P:slob_free",
134 [KPF_SLUB_FROZEN] = "A:slub_frozen",
135 [KPF_SLUB_DEBUG] = "E:slub_debug",
136};
137
138
139/*
140 * data structures
141 */
142
143static int opt_raw; /* for kernel developers */
144static int opt_list; /* list pages (in ranges) */
145static int opt_no_summary; /* don't show summary */
146static pid_t opt_pid; /* process to walk */
147
148#define MAX_ADDR_RANGES 1024
149static int nr_addr_ranges;
150static unsigned long opt_offset[MAX_ADDR_RANGES];
151static unsigned long opt_size[MAX_ADDR_RANGES];
152
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700153#define MAX_VMAS 10240
154static int nr_vmas;
155static unsigned long pg_start[MAX_VMAS];
156static unsigned long pg_end[MAX_VMAS];
157static unsigned long voffset;
158
159static int pagemap_fd;
160
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700161#define MAX_BIT_FILTERS 64
162static int nr_bit_filters;
163static uint64_t opt_mask[MAX_BIT_FILTERS];
164static uint64_t opt_bits[MAX_BIT_FILTERS];
165
166static int page_size;
167
168#define PAGES_BATCH (64 << 10) /* 64k pages */
169static int kpageflags_fd;
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700170
171#define HASH_SHIFT 13
172#define HASH_SIZE (1 << HASH_SHIFT)
173#define HASH_MASK (HASH_SIZE - 1)
174#define HASH_KEY(flags) (flags & HASH_MASK)
175
176static unsigned long total_pages;
177static unsigned long nr_pages[HASH_SIZE];
178static uint64_t page_flags[HASH_SIZE];
179
180
181/*
182 * helper functions
183 */
184
185#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
186
187#define min_t(type, x, y) ({ \
188 type __min1 = (x); \
189 type __min2 = (y); \
190 __min1 < __min2 ? __min1 : __min2; })
191
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700192#define max_t(type, x, y) ({ \
193 type __max1 = (x); \
194 type __max2 = (y); \
195 __max1 > __max2 ? __max1 : __max2; })
196
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700197static unsigned long pages2mb(unsigned long pages)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700198{
199 return (pages * page_size) >> 20;
200}
201
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700202static void fatal(const char *x, ...)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700203{
204 va_list ap;
205
206 va_start(ap, x);
207 vfprintf(stderr, x, ap);
208 va_end(ap);
209 exit(EXIT_FAILURE);
210}
211
212
213/*
214 * page flag names
215 */
216
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700217static char *page_flag_name(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700218{
219 static char buf[65];
220 int present;
221 int i, j;
222
223 for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {
224 present = (flags >> i) & 1;
225 if (!page_flag_names[i]) {
226 if (present)
227 fatal("unkown flag bit %d\n", i);
228 continue;
229 }
230 buf[j++] = present ? page_flag_names[i][0] : '_';
231 }
232
233 return buf;
234}
235
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700236static char *page_flag_longname(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700237{
238 static char buf[1024];
239 int i, n;
240
241 for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) {
242 if (!page_flag_names[i])
243 continue;
244 if ((flags >> i) & 1)
245 n += snprintf(buf + n, sizeof(buf) - n, "%s,",
246 page_flag_names[i] + 2);
247 }
248 if (n)
249 n--;
250 buf[n] = '\0';
251
252 return buf;
253}
254
255
256/*
257 * page list and summary
258 */
259
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700260static void show_page_range(unsigned long offset, uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700261{
262 static uint64_t flags0;
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700263 static unsigned long voff;
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700264 static unsigned long index;
265 static unsigned long count;
266
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700267 if (flags == flags0 && offset == index + count &&
268 (!opt_pid || voffset == voff + count)) {
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700269 count++;
270 return;
271 }
272
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700273 if (count) {
274 if (opt_pid)
275 printf("%lx\t", voff);
276 printf("%lx\t%lx\t%s\n",
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700277 index, count, page_flag_name(flags0));
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700278 }
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700279
280 flags0 = flags;
281 index = offset;
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700282 voff = voffset;
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700283 count = 1;
284}
285
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700286static void show_page(unsigned long offset, uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700287{
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700288 if (opt_pid)
289 printf("%lx\t", voffset);
290 printf("%lx\t%s\n", offset, page_flag_name(flags));
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700291}
292
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700293static void show_summary(void)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700294{
295 int i;
296
297 printf(" flags\tpage-count MB"
298 " symbolic-flags\t\t\tlong-symbolic-flags\n");
299
300 for (i = 0; i < ARRAY_SIZE(nr_pages); i++) {
301 if (nr_pages[i])
302 printf("0x%016llx\t%10lu %8lu %s\t%s\n",
303 (unsigned long long)page_flags[i],
304 nr_pages[i],
305 pages2mb(nr_pages[i]),
306 page_flag_name(page_flags[i]),
307 page_flag_longname(page_flags[i]));
308 }
309
310 printf(" total\t%10lu %8lu\n",
311 total_pages, pages2mb(total_pages));
312}
313
314
315/*
316 * page flag filters
317 */
318
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700319static int bit_mask_ok(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700320{
321 int i;
322
323 for (i = 0; i < nr_bit_filters; i++) {
324 if (opt_bits[i] == KPF_ALL_BITS) {
325 if ((flags & opt_mask[i]) == 0)
326 return 0;
327 } else {
328 if ((flags & opt_mask[i]) != opt_bits[i])
329 return 0;
330 }
331 }
332
333 return 1;
334}
335
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700336static uint64_t expand_overloaded_flags(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700337{
338 /* SLOB/SLUB overload several page flags */
339 if (flags & BIT(SLAB)) {
340 if (flags & BIT(PRIVATE))
341 flags ^= BIT(PRIVATE) | BIT(SLOB_FREE);
342 if (flags & BIT(ACTIVE))
343 flags ^= BIT(ACTIVE) | BIT(SLUB_FROZEN);
344 if (flags & BIT(ERROR))
345 flags ^= BIT(ERROR) | BIT(SLUB_DEBUG);
346 }
347
348 /* PG_reclaim is overloaded as PG_readahead in the read path */
349 if ((flags & (BIT(RECLAIM) | BIT(WRITEBACK))) == BIT(RECLAIM))
350 flags ^= BIT(RECLAIM) | BIT(READAHEAD);
351
352 return flags;
353}
354
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700355static uint64_t well_known_flags(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700356{
357 /* hide flags intended only for kernel hacker */
358 flags &= ~KPF_HACKERS_BITS;
359
360 /* hide non-hugeTLB compound pages */
361 if ((flags & BITS_COMPOUND) && !(flags & BIT(HUGE)))
362 flags &= ~BITS_COMPOUND;
363
364 return flags;
365}
366
367
368/*
369 * page frame walker
370 */
371
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700372static int hash_slot(uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700373{
374 int k = HASH_KEY(flags);
375 int i;
376
377 /* Explicitly reserve slot 0 for flags 0: the following logic
378 * cannot distinguish an unoccupied slot from slot (flags==0).
379 */
380 if (flags == 0)
381 return 0;
382
383 /* search through the remaining (HASH_SIZE-1) slots */
384 for (i = 1; i < ARRAY_SIZE(page_flags); i++, k++) {
385 if (!k || k >= ARRAY_SIZE(page_flags))
386 k = 1;
387 if (page_flags[k] == 0) {
388 page_flags[k] = flags;
389 return k;
390 }
391 if (page_flags[k] == flags)
392 return k;
393 }
394
395 fatal("hash table full: bump up HASH_SHIFT?\n");
396 exit(EXIT_FAILURE);
397}
398
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700399static void add_page(unsigned long offset, uint64_t flags)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700400{
401 flags = expand_overloaded_flags(flags);
402
403 if (!opt_raw)
404 flags = well_known_flags(flags);
405
406 if (!bit_mask_ok(flags))
407 return;
408
409 if (opt_list == 1)
410 show_page_range(offset, flags);
411 else if (opt_list == 2)
412 show_page(offset, flags);
413
414 nr_pages[hash_slot(flags)]++;
415 total_pages++;
416}
417
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700418static void walk_pfn(unsigned long index, unsigned long count)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700419{
420 unsigned long batch;
421 unsigned long n;
422 unsigned long i;
423
424 if (index > ULONG_MAX / KPF_BYTES)
425 fatal("index overflow: %lu\n", index);
426
427 lseek(kpageflags_fd, index * KPF_BYTES, SEEK_SET);
428
429 while (count) {
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700430 uint64_t kpageflags_buf[KPF_BYTES * PAGES_BATCH];
431
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700432 batch = min_t(unsigned long, count, PAGES_BATCH);
433 n = read(kpageflags_fd, kpageflags_buf, batch * KPF_BYTES);
434 if (n == 0)
435 break;
436 if (n < 0) {
437 perror(PROC_KPAGEFLAGS);
438 exit(EXIT_FAILURE);
439 }
440
441 if (n % KPF_BYTES != 0)
442 fatal("partial read: %lu bytes\n", n);
443 n = n / KPF_BYTES;
444
445 for (i = 0; i < n; i++)
446 add_page(index + i, kpageflags_buf[i]);
447
448 index += batch;
449 count -= batch;
450 }
451}
452
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700453
454#define PAGEMAP_BATCH 4096
455static unsigned long task_pfn(unsigned long pgoff)
456{
457 static uint64_t buf[PAGEMAP_BATCH];
458 static unsigned long start;
459 static long count;
460 uint64_t pfn;
461
462 if (pgoff < start || pgoff >= start + count) {
463 if (lseek64(pagemap_fd,
464 (uint64_t)pgoff * PM_ENTRY_BYTES,
465 SEEK_SET) < 0) {
466 perror("pagemap seek");
467 exit(EXIT_FAILURE);
468 }
469 count = read(pagemap_fd, buf, sizeof(buf));
470 if (count == 0)
471 return 0;
472 if (count < 0) {
473 perror("pagemap read");
474 exit(EXIT_FAILURE);
475 }
476 if (count % PM_ENTRY_BYTES) {
477 fatal("pagemap read not aligned.\n");
478 exit(EXIT_FAILURE);
479 }
480 count /= PM_ENTRY_BYTES;
481 start = pgoff;
482 }
483
484 pfn = buf[pgoff - start];
485 if (pfn & PM_PRESENT)
486 pfn = PM_PFRAME(pfn);
487 else
488 pfn = 0;
489
490 return pfn;
491}
492
493static void walk_task(unsigned long index, unsigned long count)
494{
495 int i = 0;
496 const unsigned long end = index + count;
497
498 while (index < end) {
499
500 while (pg_end[i] <= index)
501 if (++i >= nr_vmas)
502 return;
503 if (pg_start[i] >= end)
504 return;
505
506 voffset = max_t(unsigned long, pg_start[i], index);
507 index = min_t(unsigned long, pg_end[i], end);
508
509 assert(voffset < index);
510 for (; voffset < index; voffset++) {
511 unsigned long pfn = task_pfn(voffset);
512 if (pfn)
513 walk_pfn(pfn, 1);
514 }
515 }
516}
517
518static void add_addr_range(unsigned long offset, unsigned long size)
519{
520 if (nr_addr_ranges >= MAX_ADDR_RANGES)
521 fatal("too many addr ranges\n");
522
523 opt_offset[nr_addr_ranges] = offset;
524 opt_size[nr_addr_ranges] = min_t(unsigned long, size, ULONG_MAX-offset);
525 nr_addr_ranges++;
526}
527
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700528static void walk_addr_ranges(void)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700529{
530 int i;
531
532 kpageflags_fd = open(PROC_KPAGEFLAGS, O_RDONLY);
533 if (kpageflags_fd < 0) {
534 perror(PROC_KPAGEFLAGS);
535 exit(EXIT_FAILURE);
536 }
537
538 if (!nr_addr_ranges)
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700539 add_addr_range(0, ULONG_MAX);
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700540
541 for (i = 0; i < nr_addr_ranges; i++)
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700542 if (!opt_pid)
543 walk_pfn(opt_offset[i], opt_size[i]);
544 else
545 walk_task(opt_offset[i], opt_size[i]);
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700546
547 close(kpageflags_fd);
548}
549
550
551/*
552 * user interface
553 */
554
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700555static const char *page_flag_type(uint64_t flag)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700556{
557 if (flag & KPF_HACKERS_BITS)
558 return "(r)";
559 if (flag & KPF_OVERLOADED_BITS)
560 return "(o)";
561 return " ";
562}
563
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700564static void usage(void)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700565{
566 int i, j;
567
568 printf(
569"page-types [options]\n"
570" -r|--raw Raw mode, for kernel developers\n"
571" -a|--addr addr-spec Walk a range of pages\n"
572" -b|--bits bits-spec Walk pages with specified bits\n"
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700573" -p|--pid pid Walk process address space\n"
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700574#if 0 /* planned features */
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700575" -f|--file filename Walk file address space\n"
576#endif
577" -l|--list Show page details in ranges\n"
578" -L|--list-each Show page details one by one\n"
579" -N|--no-summary Don't show summay info\n"
580" -h|--help Show this usage message\n"
581"addr-spec:\n"
582" N one page at offset N (unit: pages)\n"
583" N+M pages range from N to N+M-1\n"
584" N,M pages range from N to M-1\n"
585" N, pages range from N to end\n"
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700586" ,M pages range from 0 to M-1\n"
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700587"bits-spec:\n"
588" bit1,bit2 (flags & (bit1|bit2)) != 0\n"
589" bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n"
590" bit1,~bit2 (flags & (bit1|bit2)) == bit1\n"
591" =bit1,bit2 flags == (bit1|bit2)\n"
592"bit-names:\n"
593 );
594
595 for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {
596 if (!page_flag_names[i])
597 continue;
598 printf("%16s%s", page_flag_names[i] + 2,
599 page_flag_type(1ULL << i));
600 if (++j > 3) {
601 j = 0;
602 putchar('\n');
603 }
604 }
605 printf("\n "
606 "(r) raw mode bits (o) overloaded bits\n");
607}
608
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700609static unsigned long long parse_number(const char *str)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700610{
611 unsigned long long n;
612
613 n = strtoll(str, NULL, 0);
614
615 if (n == 0 && str[0] != '0')
616 fatal("invalid name or number: %s\n", str);
617
618 return n;
619}
620
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700621static void parse_pid(const char *str)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700622{
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700623 FILE *file;
624 char buf[5000];
625
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700626 opt_pid = parse_number(str);
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700627
628 sprintf(buf, "/proc/%d/pagemap", opt_pid);
629 pagemap_fd = open(buf, O_RDONLY);
630 if (pagemap_fd < 0) {
631 perror(buf);
632 exit(EXIT_FAILURE);
633 }
634
635 sprintf(buf, "/proc/%d/maps", opt_pid);
636 file = fopen(buf, "r");
637 if (!file) {
638 perror(buf);
639 exit(EXIT_FAILURE);
640 }
641
642 while (fgets(buf, sizeof(buf), file) != NULL) {
643 unsigned long vm_start;
644 unsigned long vm_end;
645 unsigned long long pgoff;
646 int major, minor;
647 char r, w, x, s;
648 unsigned long ino;
649 int n;
650
651 n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu",
652 &vm_start,
653 &vm_end,
654 &r, &w, &x, &s,
655 &pgoff,
656 &major, &minor,
657 &ino);
658 if (n < 10) {
659 fprintf(stderr, "unexpected line: %s\n", buf);
660 continue;
661 }
662 pg_start[nr_vmas] = vm_start / page_size;
663 pg_end[nr_vmas] = vm_end / page_size;
664 if (++nr_vmas >= MAX_VMAS) {
665 fprintf(stderr, "too many VMAs\n");
666 break;
667 }
668 }
669 fclose(file);
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700670}
671
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700672static void parse_file(const char *name)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700673{
674}
675
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700676static void parse_addr_range(const char *optarg)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700677{
678 unsigned long offset;
679 unsigned long size;
680 char *p;
681
682 p = strchr(optarg, ',');
683 if (!p)
684 p = strchr(optarg, '+');
685
686 if (p == optarg) {
687 offset = 0;
688 size = parse_number(p + 1);
689 } else if (p) {
690 offset = parse_number(optarg);
691 if (p[1] == '\0')
692 size = ULONG_MAX;
693 else {
694 size = parse_number(p + 1);
695 if (*p == ',') {
696 if (size < offset)
697 fatal("invalid range: %lu,%lu\n",
698 offset, size);
699 size -= offset;
700 }
701 }
702 } else {
703 offset = parse_number(optarg);
704 size = 1;
705 }
706
707 add_addr_range(offset, size);
708}
709
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700710static void add_bits_filter(uint64_t mask, uint64_t bits)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700711{
712 if (nr_bit_filters >= MAX_BIT_FILTERS)
713 fatal("too much bit filters\n");
714
715 opt_mask[nr_bit_filters] = mask;
716 opt_bits[nr_bit_filters] = bits;
717 nr_bit_filters++;
718}
719
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700720static uint64_t parse_flag_name(const char *str, int len)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700721{
722 int i;
723
724 if (!*str || !len)
725 return 0;
726
727 if (len <= 8 && !strncmp(str, "compound", len))
728 return BITS_COMPOUND;
729
730 for (i = 0; i < ARRAY_SIZE(page_flag_names); i++) {
731 if (!page_flag_names[i])
732 continue;
733 if (!strncmp(str, page_flag_names[i] + 2, len))
734 return 1ULL << i;
735 }
736
737 return parse_number(str);
738}
739
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700740static uint64_t parse_flag_names(const char *str, int all)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700741{
742 const char *p = str;
743 uint64_t flags = 0;
744
745 while (1) {
746 if (*p == ',' || *p == '=' || *p == '\0') {
747 if ((*str != '~') || (*str == '~' && all && *++str))
748 flags |= parse_flag_name(str, p - str);
749 if (*p != ',')
750 break;
751 str = p + 1;
752 }
753 p++;
754 }
755
756 return flags;
757}
758
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700759static void parse_bits_mask(const char *optarg)
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700760{
761 uint64_t mask;
762 uint64_t bits;
763 const char *p;
764
765 p = strchr(optarg, '=');
766 if (p == optarg) {
767 mask = KPF_ALL_BITS;
768 bits = parse_flag_names(p + 1, 0);
769 } else if (p) {
770 mask = parse_flag_names(optarg, 0);
771 bits = parse_flag_names(p + 1, 0);
772 } else if (strchr(optarg, '~')) {
773 mask = parse_flag_names(optarg, 1);
774 bits = parse_flag_names(optarg, 0);
775 } else {
776 mask = parse_flag_names(optarg, 0);
777 bits = KPF_ALL_BITS;
778 }
779
780 add_bits_filter(mask, bits);
781}
782
783
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700784static struct option opts[] = {
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700785 { "raw" , 0, NULL, 'r' },
786 { "pid" , 1, NULL, 'p' },
787 { "file" , 1, NULL, 'f' },
788 { "addr" , 1, NULL, 'a' },
789 { "bits" , 1, NULL, 'b' },
790 { "list" , 0, NULL, 'l' },
791 { "list-each" , 0, NULL, 'L' },
792 { "no-summary", 0, NULL, 'N' },
793 { "help" , 0, NULL, 'h' },
794 { NULL , 0, NULL, 0 }
795};
796
797int main(int argc, char *argv[])
798{
799 int c;
800
801 page_size = getpagesize();
802
803 while ((c = getopt_long(argc, argv,
804 "rp:f:a:b:lLNh", opts, NULL)) != -1) {
805 switch (c) {
806 case 'r':
807 opt_raw = 1;
808 break;
809 case 'p':
810 parse_pid(optarg);
811 break;
812 case 'f':
813 parse_file(optarg);
814 break;
815 case 'a':
816 parse_addr_range(optarg);
817 break;
818 case 'b':
819 parse_bits_mask(optarg);
820 break;
821 case 'l':
822 opt_list = 1;
823 break;
824 case 'L':
825 opt_list = 2;
826 break;
827 case 'N':
828 opt_no_summary = 1;
829 break;
830 case 'h':
831 usage();
832 exit(0);
833 default:
834 usage();
835 exit(1);
836 }
837 }
838
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700839 if (opt_list && opt_pid)
840 printf("voffset\t");
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700841 if (opt_list == 1)
Wu Fengguang0b4b2ad2009-09-23 15:56:16 -0700842 printf("offset\tlen\tflags\n");
Wu Fengguang35efa5e2009-06-16 15:32:27 -0700843 if (opt_list == 2)
844 printf("offset\tflags\n");
845
846 walk_addr_ranges();
847
848 if (opt_list == 1)
849 show_page_range(0, 0); /* drain the buffer */
850
851 if (opt_no_summary)
852 return 0;
853
854 if (opt_list)
855 printf("\n\n");
856
857 show_summary();
858
859 return 0;
860}