OSDN Git Service

ksmutils: Add ksminfo utility
[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 static void usage(char *myname);
36 static int getprocname(pid_t pid, char *buf, int len);
37 static void print_ksm_pages(pm_map_t **maps, size_t num_maps, bool verbose);
38 static bool is_pattern(uint8_t *data, size_t len);
39 extern uint32_t hashword(const uint32_t *, size_t, int32_t);
40
41 struct ksm_page {
42     uint32_t hash;
43     unsigned long *vaddr;
44     size_t vaddr_len, vaddr_size;
45     uint16_t pattern;
46 };
47
48 int 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);
119 destroy_proc:
120     pm_process_destroy(proc);
121     return rc;
122 }
123
124 static 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
263 err_realloc:
264     if (verbose) {
265         for (i = 0; i < pages_len; i++) {
266             free(pages[i].vaddr);
267         }
268     }
269     free(pages);
270 err_pages:
271     close(fd);
272 err_open:
273     free(data);
274 }
275
276 static 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
283 static 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  */
308 static 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
334 closefile:
335     (void) fclose(f);
336 releasefilename:
337     free(filename);
338 exit:
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