├── Makefile ├── README ├── classify.pl ├── classify.sh ├── pagemap.c └── pagemap2.c /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for pagemap 2 | 3 | CC = gcc 4 | CFLAGS = -std=c99 5 | 6 | .PHONY: all 7 | all: pagemap pagemap2 8 | 9 | pagemap: pagemap.c 10 | $(CC) $(CFLAGS) $^ -o $@ 11 | pagemap2: pagemap2.c 12 | $(CC) $(CFLAGS) $^ -o $@ 13 | 14 | .PHONY: clean 15 | clean: 16 | -rm pagemap pagemap2 17 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Userspace tool to map virtual page addresses to physical addresses. Based 2 | on the /proc/pid/pagemap interface, described in kernel docs (pagemap.txt): 3 | https://www.kernel.org/doc/Documentation/vm/pagemap.txt 4 | 5 | See also linux/tools/vm/page-types.c for a similar tool that generates page 6 | statistics (must be run as root, unlike this tool). 7 | 8 | This package includes the following tools: 9 | - pagemap: prints physical pages for a given virtual address range 10 | - pagemap2: parses /proc/pid/maps and shows all virtual->physical mappings 11 | - classify.sh: prints pages in common between multiple processes 12 | 13 | Examples follow. 14 | 15 | 16 | ### Example 1: print specific page ranges for a given process 17 | 18 | $ cat & 19 | [1] 11405 20 | $ ./pagemap 11405 0x400000 0x406000 21 | 0x400000 : pfn c1719 soft-dirty 0 file/shared 1 swapped 0 present 1 22 | 0x401000 : pfn c1718 soft-dirty 0 file/shared 1 swapped 0 present 1 23 | 0x402000 : pfn c1717 soft-dirty 0 file/shared 1 swapped 0 present 1 24 | 0x403000 : pfn c1716 soft-dirty 0 file/shared 1 swapped 0 present 1 25 | 0x404000 : pfn c171a soft-dirty 0 file/shared 1 swapped 0 present 1 26 | 0x405000 : pfn c16ef soft-dirty 0 file/shared 1 swapped 0 present 1 27 | 28 | 29 | ### Example 2: print all pages for given process (using bash variable $$ to find bash's pid) 30 | 31 | $ ./pagemap2 $$ | grep '^0x40' 32 | 0x400000 : pfn c1719 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 33 | 0x401000 : pfn c1718 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 34 | 0x402000 : pfn c1717 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 35 | 0x403000 : pfn c1716 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 36 | 0x404000 : pfn c171a soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 37 | 0x405000 : pfn c16ef soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 38 | 0x406000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/bash 39 | 0x407000 : pfn c171d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 40 | 0x408000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/bash 41 | 0x409000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/bash 42 | 0x40a000 : pfn c153d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 43 | 0x40b000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/bash 44 | 0x40c000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/bash 45 | 0x40d000 : pfn c16b5 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 46 | 0x40e000 : pfn c150a soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 47 | 0x40f000 : pfn c1d5a soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/bash 48 | 49 | 50 | ### Example 3: print all pages for multiple processes 51 | 52 | $ ./pagemap2 11437 11564 | grep -E '^0x40|===' 53 | === Maps for pid 11437 54 | 0x400000 : pfn c1d6d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 55 | 0x401000 : pfn c1d75 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 56 | 0x402000 : pfn c1d76 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 57 | 0x403000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 58 | 0x404000 : pfn c1d69 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 59 | 0x405000 : pfn c1d53 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 60 | 0x406000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 61 | 0x407000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 62 | 0x408000 : pfn c2332 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 63 | 0x409000 : pfn c2331 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 64 | 0x40a000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 65 | === Maps for pid 11564 66 | 0x400000 : pfn c1d6d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 67 | 0x401000 : pfn c1d75 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 68 | 0x402000 : pfn c1d76 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 69 | 0x403000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 70 | 0x404000 : pfn c1d69 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 71 | 0x405000 : pfn c1d53 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 72 | 0x406000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 73 | 0x407000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 74 | 0x408000 : pfn c2332 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 75 | 0x409000 : pfn c2331 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 76 | 0x40a000 : pfn 0 soft-dirty 0 file/shared 0 swapped 0 present 0 library /bin/cat 77 | 78 | 79 | ### Example 4: show pages in common between two processes 80 | 81 | $ ./classify.sh 11437 11564 | grep cat 82 | c1d53 => 0x405000 : pfn c1d53 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 83 | c1d53 => 0x405000 : pfn c1d53 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 84 | c1d69 => 0x404000 : pfn c1d69 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 85 | c1d69 => 0x404000 : pfn c1d69 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 86 | c1d6d => 0x400000 : pfn c1d6d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 87 | c1d6d => 0x400000 : pfn c1d6d soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 88 | c1d75 => 0x401000 : pfn c1d75 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 89 | c1d75 => 0x401000 : pfn c1d75 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 90 | c1d76 => 0x402000 : pfn c1d76 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 91 | c1d76 => 0x402000 : pfn c1d76 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 92 | c2331 => 0x409000 : pfn c2331 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 93 | c2331 => 0x409000 : pfn c2331 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 94 | c2332 => 0x408000 : pfn c2332 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 95 | c2332 => 0x408000 : pfn c2332 soft-dirty 0 file/shared 1 swapped 0 present 1 library /bin/cat 96 | $ ./classify.sh 11437 11564 | grep libc | head 97 | 14da => 0x7fcddf6a4000 : pfn 14da soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 98 | 14da => 0x7f6e9784a000 : pfn 14da soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 99 | 14db => 0x7fcddf6a5000 : pfn 14db soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 100 | 14db => 0x7f6e9784b000 : pfn 14db soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 101 | 15c1 => 0x7fcddf766000 : pfn 15c1 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 102 | 15c1 => 0x7f6e9790c000 : pfn 15c1 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 103 | 15c2 => 0x7fcddf767000 : pfn 15c2 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 104 | 15c2 => 0x7f6e9790d000 : pfn 15c2 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 105 | 15c3 => 0x7fcddf768000 : pfn 15c3 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 106 | 15c3 => 0x7f6e9790e000 : pfn 15c3 soft-dirty 0 file/shared 1 swapped 0 present 1 library /lib/x86_64-linux-gnu/libc-2.19.so 107 | 108 | Here, each pair of lines shows the same physical page mapped into two processes 109 | (the first line is from the first process, the second from the second process). 110 | Note that the virtual address may be the same, as for /bin/cat's code pages, or 111 | different, as for libc mapped to different base addresses. 112 | -------------------------------------------------------------------------------- /classify.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | while(<>) { 4 | chomp; 5 | if(/pfn (\S+)/) { 6 | push @{ $pfn{$1} }, $_; 7 | } 8 | } 9 | 10 | for my $i (sort keys %pfn) { 11 | next if(@{ $pfn{$i} } <= 1); # skip non-shared pages 12 | 13 | for my $line (@{ $pfn{$i} }) { 14 | printf "%-8s => %s\n", $i, $line; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /classify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./pagemap2 $@ | grep : | awk '$4 > 0' | ./classify.pl 3 | -------------------------------------------------------------------------------- /pagemap.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define PAGE_SIZE 0x1000 10 | 11 | static void print_page(uint64_t address, uint64_t data) { 12 | printf("0x%-16lx : pfn %-16lx soft-dirty %ld file/shared %ld " 13 | "swapped %ld present %ld\n", 14 | address, 15 | data & 0x7fffffffffffff, 16 | (data >> 55) & 1, 17 | (data >> 61) & 1, 18 | (data >> 62) & 1, 19 | (data >> 63) & 1); 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | char filename[BUFSIZ]; 24 | if(argc != 4) { 25 | printf("Usage: %s pid start_address end_address\n", 26 | argv[0]); 27 | return 1; 28 | } 29 | 30 | errno = 0; 31 | int pid = (int)strtol(argv[1], NULL, 0); 32 | if(errno) { 33 | perror("strtol"); 34 | return 1; 35 | } 36 | snprintf(filename, sizeof filename, "/proc/%d/pagemap", pid); 37 | 38 | int fd = open(filename, O_RDONLY); 39 | if(fd < 0) { 40 | perror("open"); 41 | return 1; 42 | } 43 | 44 | uint64_t start_address = strtoul(argv[2], NULL, 0); 45 | uint64_t end_address = strtoul(argv[3], NULL, 0); 46 | 47 | for(uint64_t i = start_address; i < end_address; i += 0x1000) { 48 | uint64_t data; 49 | uint64_t index = (i / PAGE_SIZE) * sizeof(data); 50 | if(pread(fd, &data, sizeof(data), index) != sizeof(data)) { 51 | perror("pread"); 52 | break; 53 | } 54 | 55 | print_page(i, data); 56 | } 57 | 58 | close(fd); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /pagemap2.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define PAGE_SIZE 0x1000 10 | 11 | #define FIND_LIB_NAME 12 | 13 | static void print_page(uint64_t address, uint64_t data, 14 | const char *lib_name) { 15 | 16 | printf("0x%-16lx : pfn %-16lx soft-dirty %ld file/shared %ld " 17 | "swapped %ld present %ld library %s\n", 18 | address, 19 | data & 0x7fffffffffffff, 20 | (data >> 55) & 1, 21 | (data >> 61) & 1, 22 | (data >> 62) & 1, 23 | (data >> 63) & 1, 24 | lib_name); 25 | } 26 | 27 | void handle_virtual_range(int pagemap, uint64_t start_address, 28 | uint64_t end_address, const char *lib_name) { 29 | 30 | for(uint64_t i = start_address; i < end_address; i += 0x1000) { 31 | uint64_t data; 32 | uint64_t index = (i / PAGE_SIZE) * sizeof(data); 33 | if(pread(pagemap, &data, sizeof(data), index) != sizeof(data)) { 34 | if(errno) perror("pread"); 35 | break; 36 | } 37 | 38 | print_page(i, data, lib_name); 39 | } 40 | } 41 | 42 | void parse_maps(const char *maps_file, const char *pagemap_file) { 43 | int maps = open(maps_file, O_RDONLY); 44 | if(maps < 0) return; 45 | 46 | int pagemap = open(pagemap_file, O_RDONLY); 47 | if(pagemap < 0) { 48 | close(maps); 49 | return; 50 | } 51 | 52 | char buffer[BUFSIZ]; 53 | int offset = 0; 54 | 55 | for(;;) { 56 | ssize_t length = read(maps, buffer + offset, sizeof buffer - offset); 57 | if(length <= 0) break; 58 | 59 | length += offset; 60 | 61 | for(size_t i = offset; i < (size_t)length; i ++) { 62 | uint64_t low = 0, high = 0; 63 | if(buffer[i] == '\n' && i) { 64 | size_t x = i - 1; 65 | while(x && buffer[x] != '\n') x --; 66 | if(buffer[x] == '\n') x ++; 67 | size_t beginning = x; 68 | 69 | while(buffer[x] != '-' && x+1 < sizeof buffer) { 70 | char c = buffer[x ++]; 71 | low *= 16; 72 | if(c >= '0' && c <= '9') { 73 | low += c - '0'; 74 | } 75 | else if(c >= 'a' && c <= 'f') { 76 | low += c - 'a' + 10; 77 | } 78 | else break; 79 | } 80 | 81 | while(buffer[x] != '-' && x+1 < sizeof buffer) x ++; 82 | if(buffer[x] == '-') x ++; 83 | 84 | while(buffer[x] != ' ' && x+1 < sizeof buffer) { 85 | char c = buffer[x ++]; 86 | high *= 16; 87 | if(c >= '0' && c <= '9') { 88 | high += c - '0'; 89 | } 90 | else if(c >= 'a' && c <= 'f') { 91 | high += c - 'a' + 10; 92 | } 93 | else break; 94 | } 95 | 96 | const char *lib_name = 0; 97 | #ifdef FIND_LIB_NAME 98 | for(int field = 0; field < 4; field ++) { 99 | x ++; // skip space 100 | while(buffer[x] != ' ' && x+1 < sizeof buffer) x ++; 101 | } 102 | while(buffer[x] == ' ' && x+1 < sizeof buffer) x ++; 103 | 104 | size_t y = x; 105 | while(buffer[y] != '\n' && y+1 < sizeof buffer) y ++; 106 | buffer[y] = 0; 107 | 108 | lib_name = buffer + x; 109 | #endif 110 | 111 | handle_virtual_range(pagemap, low, high, lib_name); 112 | 113 | #ifdef FIND_LIB_NAME 114 | buffer[y] = '\n'; 115 | #endif 116 | } 117 | } 118 | } 119 | 120 | close(maps); 121 | close(pagemap); 122 | } 123 | 124 | 125 | void process_pid(pid_t pid) { 126 | char maps_file[BUFSIZ]; 127 | char pagemap_file[BUFSIZ]; 128 | snprintf(maps_file, sizeof(maps_file), 129 | "/proc/%lu/maps", (uint64_t)pid); 130 | snprintf(pagemap_file, sizeof(pagemap_file), 131 | "/proc/%lu/pagemap", (uint64_t)pid); 132 | 133 | parse_maps(maps_file, pagemap_file); 134 | } 135 | 136 | int main(int argc, char *argv[]) { 137 | if(argc < 2) { 138 | printf("Usage: %s pid1 [pid2...]\n", argv[0]); 139 | return 1; 140 | } 141 | 142 | for(int i = 1; i < argc; i ++) { 143 | pid_t pid = (pid_t)strtoul(argv[i], NULL, 0); 144 | 145 | printf("=== Maps for pid %d\n", (int)pid); 146 | process_pid(pid); 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | --------------------------------------------------------------------------------