OSDN Git Service

ksmutils: display page count
[android-x86/system-extras.git] / ksmutils / ksminfo.c
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