OSDN Git Service

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