ksminfo.c revision 266dde27811b78f466702295ba0173b8d8be5ada
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <stdbool.h> 19#include <stdlib.h> 20#include <sys/types.h> 21#include <unistd.h> 22#include <string.h> 23#include <fcntl.h> 24#include <stdint.h> 25#include <getopt.h> 26 27#include <pagemap/pagemap.h> 28 29#define MAX_FILENAME 64 30 31#define GROWTH_FACTOR 10 32 33#define NO_PATTERN 0x100 34 35static void usage(char *myname); 36static int getprocname(pid_t pid, char *buf, int len); 37static void print_ksm_pages(pm_map_t **maps, size_t num_maps, bool verbose); 38static bool is_pattern(uint8_t *data, size_t len); 39extern uint32_t hashword(const uint32_t *, size_t, int32_t); 40 41struct ksm_page { 42 uint32_t hash; 43 unsigned long *vaddr; 44 size_t vaddr_len, vaddr_size; 45 uint16_t pattern; 46}; 47 48int main(int argc, char *argv[]) { 49 pm_kernel_t *ker; 50 pm_process_t *proc; 51 pid_t pid; 52 pm_map_t **maps; 53 size_t num_maps; 54 char cmdline[256]; // this must be within the range of int 55 int error; 56 int rc = EXIT_SUCCESS; 57 bool verbose = false; 58 59 opterr = 0; 60 do { 61 int c = getopt(argc, argv, "hv"); 62 if (c == -1) 63 break; 64 65 switch (c) { 66 case 'v': 67 verbose = true; 68 break; 69 case 'h': 70 usage(argv[0]); 71 exit(EXIT_SUCCESS); 72 case '?': 73 fprintf(stderr, "unknown option: %c\n", optopt); 74 usage(argv[0]); 75 exit(EXIT_FAILURE); 76 } 77 } while (1); 78 79 if (optind != argc - 1) { 80 usage(argv[0]); 81 exit(EXIT_FAILURE); 82 } 83 84 pid = strtoul(argv[optind], NULL, 10); 85 if (pid == 0) { 86 fprintf(stderr, "Invalid PID\n"); 87 exit(EXIT_FAILURE); 88 } 89 90 error = pm_kernel_create(&ker); 91 if (error) { 92 fprintf(stderr, "Error creating kernel interface -- " 93 "does this kernel have pagemap?\n"); 94 exit(EXIT_FAILURE); 95 } 96 97 error = pm_process_create(ker, pid, &proc); 98 if (error) { 99 fprintf(stderr, "warning: could not create process interface for %d\n", pid); 100 exit(EXIT_FAILURE); 101 } 102 103 error = pm_process_maps(proc, &maps, &num_maps); 104 if (error) { 105 fprintf(stderr, "warning: could not read process map for %d\n", pid); 106 rc = EXIT_FAILURE; 107 goto destroy_proc; 108 } 109 110 if (getprocname(pid, cmdline, sizeof(cmdline)) < 0) { 111 cmdline[0] = '\0'; 112 } 113 printf("%s (%u):\n", cmdline, pid); 114 printf("Warning: this tool only compares the KSM CRCs of pages, there is a chance of " 115 "collisions\n"); 116 print_ksm_pages(maps, num_maps, verbose); 117 118 free(maps); 119destroy_proc: 120 pm_process_destroy(proc); 121 return rc; 122} 123 124static void print_ksm_pages(pm_map_t **maps, size_t num_maps, bool verbose) { 125 size_t i, j, k; 126 size_t len; 127 uint64_t *pagemap; 128 size_t map_len; 129 uint64_t flags; 130 pm_kernel_t *ker; 131 int error; 132 unsigned long vaddr; 133 int fd; 134 off_t off; 135 char filename[MAX_FILENAME]; 136 uint32_t *data; 137 uint32_t hash; 138 struct ksm_page *pages; 139 size_t pages_len, pages_size; 140 141 if (num_maps <= 0) 142 return; 143 144 ker = maps[0]->proc->ker; 145 error = snprintf(filename, MAX_FILENAME, "/proc/%d/mem", pm_process_pid(maps[0]->proc)); 146 if (error < 0 || error >= MAX_FILENAME) { 147 return; 148 } 149 150 data = malloc(pm_kernel_pagesize(ker)); 151 if (data == NULL) { 152 fprintf(stderr, "warning: not enough memory to malloc data buffer\n"); 153 return; 154 } 155 156 fd = open(filename, O_RDONLY); 157 if (fd < 0) { 158 fprintf(stderr, "warning: could not open %s\n", filename); 159 goto err_open; 160 } 161 162 pages = NULL; 163 pages_size = 0; 164 pages_len = 0; 165 166 for (i = 0; i < num_maps; i++) { 167 error = pm_map_pagemap(maps[i], &pagemap, &map_len); 168 if (error) { 169 fprintf(stderr, "warning: could not read the pagemap of %d\n", 170 pm_process_pid(maps[i]->proc)); 171 } 172 for (j = 0; j < map_len; j++) { 173 error = pm_kernel_flags(ker, pagemap[j], &flags); 174 if (error) { 175 fprintf(stderr, "warning: could not read flags for pfn at address 0x%016llx\n", 176 pagemap[i]); 177 continue; 178 } 179 if (!(flags & PM_PAGE_KSM)) { 180 continue; 181 } 182 vaddr = pm_map_start(maps[i]) + j * pm_kernel_pagesize(ker); 183 off = lseek(fd, vaddr, SEEK_SET); 184 if (off == (off_t)-1) { 185 fprintf(stderr, "warning: could not lseek to 0x%08lx\n", vaddr); 186 continue; 187 } 188 len = read(fd, data, pm_kernel_pagesize(ker)); 189 if (len != pm_kernel_pagesize(ker)) { 190 fprintf(stderr, "warning: could not read page at 0x%08lx\n", vaddr); 191 continue; 192 } 193 194 hash = hashword(data, pm_kernel_pagesize(ker) / sizeof(*data), 17); 195 196 for (k = 0; k < pages_len; k++) { 197 if (pages[k].hash == hash) break; 198 } 199 200 if (k == pages_len) { 201 if (pages_len == pages_size) { 202 struct ksm_page *tmp = realloc(pages, 203 (pages_size + GROWTH_FACTOR) * sizeof(*pages)); 204 if (tmp == NULL) { 205 fprintf(stderr, "warning: not enough memory to realloc pages struct\n"); 206 free(pagemap); 207 goto err_realloc; 208 } 209 memset(&tmp[k], 0, sizeof(tmp[k]) * GROWTH_FACTOR); 210 pages = tmp; 211 pages_size += GROWTH_FACTOR; 212 } 213 pages[pages_len].hash = hash; 214 pages[pages_len].pattern = is_pattern((uint8_t *)data, pm_kernel_pagesize(ker)) ? 215 (data[0] & 0xFF) : NO_PATTERN; 216 pages_len++; 217 } 218 219 if (verbose) { 220 if (pages[k].vaddr_len == pages[k].vaddr_size) { 221 unsigned long *tmp = realloc(pages[k].vaddr, 222 (pages[k].vaddr_size + GROWTH_FACTOR) * sizeof(*(pages[k].vaddr))); 223 if (tmp == NULL) { 224 fprintf(stderr, "warning: not enough memory to realloc vaddr array\n"); 225 free(pagemap); 226 goto err_realloc; 227 } 228 memset(&tmp[pages[k].vaddr_len], 0, sizeof(tmp[pages[k].vaddr_len]) * GROWTH_FACTOR); 229 pages[k].vaddr = tmp; 230 pages[k].vaddr_size += GROWTH_FACTOR; 231 } 232 pages[k].vaddr[pages[k].vaddr_len] = vaddr; 233 } 234 pages[k].vaddr_len++; 235 } 236 free(pagemap); 237 } 238 239 for (i = 0; i < pages_len; i++) { 240 if (pages[i].pattern != NO_PATTERN) { 241 printf("0x%02x byte pattern: ", pages[i].pattern); 242 } else { 243 printf("KSM CRC 0x%08x:", pages[i].hash); 244 } 245 printf(" %4d page", pages[i].vaddr_len); 246 if (pages[i].vaddr_len > 1) { 247 printf("s"); 248 } 249 printf("\n"); 250 251 if (verbose) { 252 j = 0; 253 while (j < pages[i].vaddr_len) { 254 printf(" "); 255 for (k = 0; k < 8 && j < pages[i].vaddr_len; k++, j++) { 256 printf(" 0x%08lx", pages[i].vaddr[j]); 257 } 258 printf("\n"); 259 } 260 } 261 } 262 263err_realloc: 264 if (verbose) { 265 for (i = 0; i < pages_len; i++) { 266 free(pages[i].vaddr); 267 } 268 } 269 free(pages); 270err_pages: 271 close(fd); 272err_open: 273 free(data); 274} 275 276static void usage(char *myname) { 277 fprintf(stderr, "Usage: %s [ -v | -h ] <pid>\n" 278 " -v Verbose: print virtual addresses.\n" 279 " -h Display this help screen.\n", 280 myname); 281} 282 283static bool is_pattern(uint8_t *data, size_t len) { 284 size_t i; 285 uint8_t first_byte = data[0]; 286 287 for (i = 1; i < len; i++) { 288 if (first_byte != data[i]) return false; 289 } 290 291 return true; 292} 293 294/* 295 * Get the process name for a given PID. Inserts the process name into buffer 296 * buf of length len. The size of the buffer must be greater than zero to get 297 * any useful output. 298 * 299 * Note that fgets(3) only declares length as an int, so our buffer size is 300 * also declared as an int. 301 * 302 * Returns 0 on success, a positive value on partial success, and -1 on 303 * failure. Other interesting values: 304 * 1 on failure to create string to examine proc cmdline entry 305 * 2 on failure to open proc cmdline entry 306 * 3 on failure to read proc cmdline entry 307 */ 308static int getprocname(pid_t pid, char *buf, int len) { 309 char *filename; 310 FILE *f; 311 int rc = 0; 312 static const char* unknown_cmdline = "<unknown>"; 313 314 if (len <= 0) { 315 return -1; 316 } 317 318 if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { 319 rc = 1; 320 goto exit; 321 } 322 323 f = fopen(filename, "r"); 324 if (f == NULL) { 325 rc = 2; 326 goto releasefilename; 327 } 328 329 if (fgets(buf, len, f) == NULL) { 330 rc = 3; 331 goto closefile; 332 } 333 334closefile: 335 (void) fclose(f); 336releasefilename: 337 free(filename); 338exit: 339 if (rc != 0) { 340 /* 341 * The process went away before we could read its process name. Try 342 * to give the user "<unknown>" here, but otherwise they get to look 343 * at a blank. 344 */ 345 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { 346 rc = 4; 347 } 348 } 349 350 return rc; 351} 352 353