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 35 #define PR_SORTED 1 36 #define PR_VERBOSE 2 37 #define PR_ALL 4 38 39 struct vaddr { 40 unsigned long addr; 41 size_t num_pages; 42 pid_t pid; 43 }; 44 45 struct ksm_page { 46 uint64_t count; 47 uint32_t hash; 48 struct vaddr *vaddr; 49 size_t vaddr_len, vaddr_size; 50 size_t vaddr_count; 51 uint16_t pattern; 52 }; 53 54 struct ksm_pages { 55 struct ksm_page *pages; 56 size_t len, size; 57 }; 58 59 static void usage(char *myname); 60 static int getprocname(pid_t pid, char *buf, int len); 61 static int read_pages(struct ksm_pages *kp, pm_map_t **maps, size_t num_maps, uint8_t pr_flags); 62 static void print_pages(struct ksm_pages *kp, uint8_t pr_flags); 63 static void free_pages(struct ksm_pages *kp, uint8_t pr_flags); 64 static bool is_pattern(uint8_t *data, size_t len); 65 static int cmp_pages(const void *a, const void *b); 66 extern uint32_t hashword(const uint32_t *, size_t, int32_t); 67 68 int main(int argc, char *argv[]) { 69 pm_kernel_t *ker; 70 pm_process_t *proc; 71 pid_t *pids; 72 size_t num_procs; 73 size_t i; 74 pm_map_t **maps; 75 size_t num_maps; 76 char cmdline[256]; // this must be within the range of int 77 int error; 78 int rc = EXIT_SUCCESS; 79 uint8_t pr_flags = 0; 80 struct ksm_pages kp; 81 82 memset(&kp, 0, sizeof(kp)); 83 84 opterr = 0; 85 do { 86 int c = getopt(argc, argv, "hvsa"); 87 if (c == -1) 88 break; 89 90 switch (c) { 91 case 'a': 92 pr_flags |= PR_ALL; 93 break; 94 case 's': 95 pr_flags |= PR_SORTED; 96 break; 97 case 'v': 98 pr_flags |= PR_VERBOSE; 99 break; 100 case 'h': 101 usage(argv[0]); 102 exit(EXIT_SUCCESS); 103 case '?': 104 fprintf(stderr, "unknown option: %c\n", optopt); 105 usage(argv[0]); 106 exit(EXIT_FAILURE); 107 } 108 } while (1); 109 110 error = pm_kernel_create(&ker); 111 if (error) { 112 fprintf(stderr, "Error creating kernel interface -- " 113 "does this kernel have pagemap?\n"); 114 exit(EXIT_FAILURE); 115 } 116 117 if (pr_flags & PR_ALL) { 118 error = pm_kernel_pids(ker, &pids, &num_procs); 119 if (error) { 120 fprintf(stderr, "Error listing processes.\n"); 121 exit(EXIT_FAILURE); 122 } 123 } else { 124 if (optind != argc - 1) { 125 usage(argv[0]); 126 exit(EXIT_FAILURE); 127 } 128 129 pids = malloc(sizeof(*pids)); 130 if (pids == NULL) { 131 fprintf(stderr, "Error allocating pid memory\n"); 132 exit(EXIT_FAILURE); 133 } 134 135 *pids = strtoul(argv[optind], NULL, 10); 136 if (*pids == 0) { 137 fprintf(stderr, "Invalid PID\n"); 138 rc = EXIT_FAILURE; 139 goto exit; 140 } 141 num_procs = 1; 142 if (getprocname(*pids, cmdline, sizeof(cmdline)) < 0) { 143 cmdline[0] = '\0'; 144 } 145 printf("%s (%u):\n", cmdline, *pids); 146 } 147 148 printf("Warning: this tool only compares the KSM CRCs of pages, there is a chance of " 149 "collisions\n"); 150 151 for (i = 0; i < num_procs; i++) { 152 error = pm_process_create(ker, pids[i], &proc); 153 if (error) { 154 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 155 rc = EXIT_FAILURE; 156 goto exit; 157 } 158 159 error = pm_process_maps(proc, &maps, &num_maps); 160 if (error) { 161 pm_process_destroy(proc); 162 fprintf(stderr, "warning: could not read process map for %d\n", pids[i]); 163 rc = EXIT_FAILURE; 164 goto exit; 165 } 166 167 if (read_pages(&kp, maps, num_maps, pr_flags) < 0) { 168 free(maps); 169 pm_process_destroy(proc); 170 rc = EXIT_FAILURE; 171 goto exit; 172 } 173 174 free(maps); 175 pm_process_destroy(proc); 176 } 177 178 if (pr_flags & PR_SORTED) { 179 qsort(kp.pages, kp.len, sizeof(*kp.pages), cmp_pages); 180 } 181 print_pages(&kp, pr_flags); 182 183 exit: 184 free_pages(&kp, pr_flags); 185 free(pids); 186 return rc; 187 } 188 189 static int read_pages(struct ksm_pages *kp, pm_map_t **maps, size_t num_maps, uint8_t pr_flags) { 190 size_t i, j, k; 191 size_t len; 192 uint64_t *pagemap; 193 size_t map_len; 194 uint64_t flags; 195 pm_kernel_t *ker; 196 int error; 197 unsigned long vaddr; 198 int fd; 199 off_t off; 200 char filename[MAX_FILENAME]; 201 uint32_t *data; 202 uint32_t hash; 203 int rc = 0; 204 struct ksm_page *cur_page; 205 pid_t pid; 206 207 if (num_maps == 0) 208 return 0; 209 210 pid = pm_process_pid(maps[0]->proc); 211 ker = maps[0]->proc->ker; 212 error = snprintf(filename, MAX_FILENAME, "/proc/%d/mem", pid); 213 if (error < 0 || error >= MAX_FILENAME) { 214 return -1; 215 } 216 217 data = malloc(pm_kernel_pagesize(ker)); 218 if (data == NULL) { 219 fprintf(stderr, "warning: not enough memory to malloc data buffer\n"); 220 return -1; 221 } 222 223 fd = open(filename, O_RDONLY); 224 if (fd < 0) { 225 fprintf(stderr, "warning: could not open %s\n", filename); 226 rc = -1; 227 goto err_open; 228 } 229 230 for (i = 0; i < num_maps; i++) { 231 error = pm_map_pagemap(maps[i], &pagemap, &map_len); 232 if (error) { 233 fprintf(stderr, "warning: could not read the pagemap of %d\n", 234 pm_process_pid(maps[i]->proc)); 235 continue; 236 } 237 for (j = 0; j < map_len; j++) { 238 error = pm_kernel_flags(ker, pagemap[j], &flags); 239 if (error) { 240 fprintf(stderr, "warning: could not read flags for pfn at address 0x%016llx\n", 241 pagemap[i]); 242 continue; 243 } 244 if (!(flags & PM_PAGE_KSM)) { 245 continue; 246 } 247 vaddr = pm_map_start(maps[i]) + j * pm_kernel_pagesize(ker); 248 off = lseek(fd, vaddr, SEEK_SET); 249 if (off == (off_t)-1) { 250 fprintf(stderr, "warning: could not lseek to 0x%08lx\n", vaddr); 251 continue; 252 } 253 len = read(fd, data, pm_kernel_pagesize(ker)); 254 if (len != pm_kernel_pagesize(ker)) { 255 fprintf(stderr, "warning: could not read page at 0x%08lx\n", vaddr); 256 continue; 257 } 258 259 hash = hashword(data, pm_kernel_pagesize(ker) / sizeof(*data), 17); 260 261 for (k = 0; k < kp->len; k++) { 262 if (kp->pages[k].hash == hash) break; 263 } 264 265 if (k == kp->len) { 266 if (kp->len == kp->size) { 267 struct ksm_page *tmp = realloc(kp->pages, 268 (kp->size + GROWTH_FACTOR) * sizeof(*kp->pages)); 269 if (tmp == NULL) { 270 fprintf(stderr, "warning: not enough memory to realloc pages struct\n"); 271 free(pagemap); 272 rc = -1; 273 goto err_realloc; 274 } 275 memset(&tmp[k], 0, sizeof(tmp[k]) * GROWTH_FACTOR); 276 kp->pages = tmp; 277 kp->size += GROWTH_FACTOR; 278 } 279 rc = pm_kernel_count(ker, pagemap[j], &kp->pages[kp->len].count); 280 if (rc) { 281 fprintf(stderr, "error reading page count\n"); 282 free(pagemap); 283 goto err_count; 284 } 285 kp->pages[kp->len].hash = hash; 286 kp->pages[kp->len].pattern = 287 is_pattern((uint8_t *)data, pm_kernel_pagesize(ker)) ? 288 (data[0] & 0xFF) : NO_PATTERN; 289 kp->len++; 290 } 291 292 cur_page = &kp->pages[k]; 293 294 if (pr_flags & PR_VERBOSE) { 295 if (cur_page->vaddr_len > 0 && 296 cur_page->vaddr[cur_page->vaddr_len - 1].pid == pid && 297 cur_page->vaddr[cur_page->vaddr_len - 1].addr == 298 vaddr - (cur_page->vaddr[cur_page->vaddr_len - 1].num_pages * 299 pm_kernel_pagesize(ker))) { 300 cur_page->vaddr[cur_page->vaddr_len - 1].num_pages++; 301 } else { 302 if (cur_page->vaddr_len == cur_page->vaddr_size) { 303 struct vaddr *tmp = realloc(cur_page->vaddr, 304 (cur_page->vaddr_size + GROWTH_FACTOR) * sizeof(*(cur_page->vaddr))); 305 if (tmp == NULL) { 306 fprintf(stderr, "warning: not enough memory to realloc vaddr array\n"); 307 free(pagemap); 308 rc = -1; 309 goto err_realloc; 310 } 311 memset(&tmp[cur_page->vaddr_len], 0, sizeof(tmp[cur_page->vaddr_len]) * GROWTH_FACTOR); 312 cur_page->vaddr = tmp; 313 cur_page->vaddr_size += GROWTH_FACTOR; 314 } 315 cur_page->vaddr[cur_page->vaddr_len].addr = vaddr; 316 cur_page->vaddr[cur_page->vaddr_len].num_pages = 1; 317 cur_page->vaddr[cur_page->vaddr_len].pid = pid; 318 cur_page->vaddr_len++; 319 } 320 } 321 cur_page->vaddr_count++; 322 } 323 free(pagemap); 324 } 325 goto no_err; 326 327 err_realloc: 328 err_count: 329 if (pr_flags & PR_VERBOSE) { 330 for (i = 0; i < kp->len; i++) { 331 free(kp->pages[i].vaddr); 332 } 333 } 334 free(kp->pages); 335 336 no_err: 337 close(fd); 338 err_open: 339 free(data); 340 return rc; 341 } 342 343 static void print_pages(struct ksm_pages *kp, uint8_t pr_flags) { 344 size_t i, j, k; 345 char suffix[13]; 346 int index; 347 348 for (i = 0; i < kp->len; i++) { 349 if (kp->pages[i].pattern != NO_PATTERN) { 350 printf("0x%02x byte pattern: ", kp->pages[i].pattern); 351 } else { 352 printf("KSM CRC 0x%08x:", kp->pages[i].hash); 353 } 354 printf(" %4d page", kp->pages[i].vaddr_count); 355 if (kp->pages[i].vaddr_count > 1) { 356 printf("s"); 357 } 358 if (!(pr_flags & PR_ALL)) { 359 printf(" (%llu reference", kp->pages[i].count); 360 if (kp->pages[i].count > 1) { 361 printf("s"); 362 } 363 printf(")"); 364 } 365 printf("\n"); 366 367 if (pr_flags & PR_VERBOSE) { 368 j = 0; 369 while (j < kp->pages[i].vaddr_len) { 370 printf(" "); 371 for (k = 0; k < 8 && j < kp->pages[i].vaddr_len; k++, j++) { 372 printf(" 0x%08lx", kp->pages[i].vaddr[j].addr); 373 374 index = snprintf(suffix, sizeof(suffix), ":%d", 375 kp->pages[i].vaddr[j].num_pages); 376 if (pr_flags & PR_ALL) { 377 index += snprintf(suffix + index, sizeof(suffix) - index, "[%d]", 378 kp->pages[i].vaddr[j].pid); 379 } 380 printf("%-12s", suffix); 381 } 382 printf("\n"); 383 } 384 } 385 } 386 } 387 388 static void free_pages(struct ksm_pages *kp, uint8_t pr_flags) { 389 size_t i; 390 391 if (pr_flags & PR_VERBOSE) { 392 for (i = 0; i < kp->len; i++) { 393 free(kp->pages[i].vaddr); 394 } 395 } 396 free(kp->pages); 397 } 398 399 static void usage(char *myname) { 400 fprintf(stderr, "Usage: %s [-s | -v | -a | -h ] <pid>\n" 401 " -s Sort pages by usage count.\n" 402 " -v Verbose: print virtual addresses.\n" 403 " -a Display all the KSM pages in the system. Ignore the pid argument.\n" 404 " -h Display this help screen.\n", 405 myname); 406 } 407 408 static int cmp_pages(const void *a, const void *b) { 409 const struct ksm_page *pg_a = a; 410 const struct ksm_page *pg_b = b; 411 int cmp = pg_b->vaddr_count - pg_a->vaddr_count; 412 413 return cmp ? cmp : pg_b->count - pg_a->count; 414 } 415 416 static bool is_pattern(uint8_t *data, size_t len) { 417 size_t i; 418 uint8_t first_byte = data[0]; 419 420 for (i = 1; i < len; i++) { 421 if (first_byte != data[i]) return false; 422 } 423 424 return true; 425 } 426 427 /* 428 * Get the process name for a given PID. Inserts the process name into buffer 429 * buf of length len. The size of the buffer must be greater than zero to get 430 * any useful output. 431 * 432 * Note that fgets(3) only declares length as an int, so our buffer size is 433 * also declared as an int. 434 * 435 * Returns 0 on success, a positive value on partial success, and -1 on 436 * failure. Other interesting values: 437 * 1 on failure to create string to examine proc cmdline entry 438 * 2 on failure to open proc cmdline entry 439 * 3 on failure to read proc cmdline entry 440 */ 441 static int getprocname(pid_t pid, char *buf, int len) { 442 char *filename; 443 FILE *f; 444 int rc = 0; 445 static const char* unknown_cmdline = "<unknown>"; 446 447 if (len <= 0) { 448 return -1; 449 } 450 451 if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { 452 rc = 1; 453 goto exit; 454 } 455 456 f = fopen(filename, "r"); 457 if (f == NULL) { 458 rc = 2; 459 goto releasefilename; 460 } 461 462 if (fgets(buf, len, f) == NULL) { 463 rc = 3; 464 goto closefile; 465 } 466 467 closefile: 468 (void) fclose(f); 469 releasefilename: 470 free(filename); 471 exit: 472 if (rc != 0) { 473 /* 474 * The process went away before we could read its process name. Try 475 * to give the user "<unknown>" here, but otherwise they get to look 476 * at a blank. 477 */ 478 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { 479 rc = 4; 480 } 481 } 482 483 return rc; 484 } 485 486