• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fcntl.h>
19 #include <getopt.h>
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #include <pagemap/pagemap.h>
29 
30 #define MAX_FILENAME  64
31 
32 #define GROWTH_FACTOR 10
33 
34 #define NO_PATTERN    0x100
35 
36 #define PR_SORTED       1
37 #define PR_VERBOSE      2
38 #define PR_ALL          4
39 
40 struct vaddr {
41     unsigned long addr;
42     size_t num_pages;
43     pid_t pid;
44 };
45 
46 struct ksm_page {
47     uint64_t count;
48     uint32_t hash;
49     struct vaddr *vaddr;
50     size_t vaddr_len, vaddr_size;
51     size_t vaddr_count;
52     uint16_t pattern;
53 };
54 
55 struct ksm_pages {
56     struct ksm_page *pages;
57     size_t len, size;
58 };
59 
60 static void usage(char *myname);
61 static int getprocname(pid_t pid, char *buf, int len);
62 static int read_pages(struct ksm_pages *kp, pm_map_t **maps, size_t num_maps, uint8_t pr_flags);
63 static void print_pages(struct ksm_pages *kp, uint8_t pr_flags);
64 static void free_pages(struct ksm_pages *kp, uint8_t pr_flags);
65 static bool is_pattern(uint8_t *data, size_t len);
66 static int cmp_pages(const void *a, const void *b);
67 extern uint32_t hashword(const uint32_t *, size_t, int32_t);
68 
main(int argc,char * argv[])69 int main(int argc, char *argv[]) {
70     pm_kernel_t *ker;
71     pm_process_t *proc;
72     pid_t *pids;
73     size_t num_procs;
74     size_t i;
75     pm_map_t **maps;
76     size_t num_maps;
77     char cmdline[256]; // this must be within the range of int
78     int error;
79     int rc = EXIT_SUCCESS;
80     uint8_t pr_flags = 0;
81     struct ksm_pages kp;
82 
83     memset(&kp, 0, sizeof(kp));
84 
85     opterr = 0;
86     do {
87         int c = getopt(argc, argv, "hvsa");
88         if (c == -1)
89             break;
90 
91         switch (c) {
92             case 'a':
93                 pr_flags |= PR_ALL;
94                 break;
95             case 's':
96                 pr_flags |= PR_SORTED;
97                 break;
98             case 'v':
99                 pr_flags |= PR_VERBOSE;
100                 break;
101             case 'h':
102                 usage(argv[0]);
103                 exit(EXIT_SUCCESS);
104             case '?':
105                 fprintf(stderr, "unknown option: %c\n", optopt);
106                 usage(argv[0]);
107                 exit(EXIT_FAILURE);
108         }
109     } while (1);
110 
111     error = pm_kernel_create(&ker);
112     if (error) {
113         fprintf(stderr, "Error creating kernel interface -- "
114                         "does this kernel have pagemap?\n");
115         exit(EXIT_FAILURE);
116     }
117 
118     if (pr_flags & PR_ALL) {
119         error = pm_kernel_pids(ker, &pids, &num_procs);
120         if (error) {
121             fprintf(stderr, "Error listing processes.\n");
122             exit(EXIT_FAILURE);
123         }
124     } else {
125         if (optind != argc - 1) {
126             usage(argv[0]);
127             exit(EXIT_FAILURE);
128         }
129 
130         pids = malloc(sizeof(*pids));
131         if (pids == NULL) {
132            fprintf(stderr, "Error allocating pid memory\n");
133            exit(EXIT_FAILURE);
134         }
135 
136         *pids = strtoul(argv[optind], NULL, 10);
137         if (*pids == 0) {
138             fprintf(stderr, "Invalid PID\n");
139             rc = EXIT_FAILURE;
140             goto exit;
141         }
142         num_procs = 1;
143         if (getprocname(*pids, cmdline, sizeof(cmdline)) < 0) {
144             cmdline[0] = '\0';
145         }
146         printf("%s (%u):\n", cmdline, *pids);
147     }
148 
149     printf("Warning: this tool only compares the KSM CRCs of pages, there is a chance of "
150             "collisions\n");
151 
152     for (i = 0; i < num_procs; i++) {
153         error = pm_process_create(ker, pids[i], &proc);
154         if (error) {
155             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
156             rc = EXIT_FAILURE;
157             goto exit;
158         }
159 
160         error = pm_process_maps(proc, &maps, &num_maps);
161         if (error) {
162             pm_process_destroy(proc);
163             fprintf(stderr, "warning: could not read process map for %d\n", pids[i]);
164             rc = EXIT_FAILURE;
165             goto exit;
166         }
167 
168         if (read_pages(&kp, maps, num_maps, pr_flags) < 0) {
169             free(maps);
170             pm_process_destroy(proc);
171             rc = EXIT_FAILURE;
172             goto exit;
173         }
174 
175         free(maps);
176         pm_process_destroy(proc);
177     }
178 
179     if (pr_flags & PR_SORTED) {
180         qsort(kp.pages, kp.len, sizeof(*kp.pages), cmp_pages);
181     }
182     print_pages(&kp, pr_flags);
183 
184 exit:
185     free_pages(&kp, pr_flags);
186     free(pids);
187     return rc;
188 }
189 
read_pages(struct ksm_pages * kp,pm_map_t ** maps,size_t num_maps,uint8_t pr_flags)190 static int read_pages(struct ksm_pages *kp, pm_map_t **maps, size_t num_maps, uint8_t pr_flags) {
191     size_t i, j, k;
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, PM_PAGEMAP_PFN(pagemap[j]), &flags);
239             if (error) {
240                 fprintf(stderr, "warning: could not read flags for pfn at address 0x%016" PRIx64 "\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             ssize_t 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, PM_PAGEMAP_PFN(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 
print_pages(struct ksm_pages * kp,uint8_t pr_flags)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(" %4zu 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(" (%" PRIu64 " 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), ":%zu",
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 
free_pages(struct ksm_pages * kp,uint8_t pr_flags)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 
usage(char * myname)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 
cmp_pages(const void * a,const void * b)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 
is_pattern(uint8_t * data,size_t len)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  */
getprocname(pid_t pid,char * buf,int len)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/%d/cmdline", (int)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