2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <sys/types.h>
27 #include <pagemap/pagemap.h>
29 #define MAX_FILENAME 64
31 #define GROWTH_FACTOR 10
33 #define NO_PATTERN 0x100
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);
44 size_t vaddr_len, vaddr_size;
48 int main(int argc, char *argv[]) {
54 char cmdline[256]; // this must be within the range of int
56 int rc = EXIT_SUCCESS;
61 int c = getopt(argc, argv, "hv");
73 fprintf(stderr, "unknown option: %c\n", optopt);
79 if (optind != argc - 1) {
84 pid = strtoul(argv[optind], NULL, 10);
86 fprintf(stderr, "Invalid PID\n");
90 error = pm_kernel_create(&ker);
92 fprintf(stderr, "Error creating kernel interface -- "
93 "does this kernel have pagemap?\n");
97 error = pm_process_create(ker, pid, &proc);
99 fprintf(stderr, "warning: could not create process interface for %d\n", pid);
103 error = pm_process_maps(proc, &maps, &num_maps);
105 fprintf(stderr, "warning: could not read process map for %d\n", pid);
110 if (getprocname(pid, cmdline, sizeof(cmdline)) < 0) {
113 printf("%s (%u):\n", cmdline, pid);
114 printf("Warning: this tool only compares the KSM CRCs of pages, there is a chance of "
116 print_ksm_pages(maps, num_maps, verbose);
120 pm_process_destroy(proc);
124 static void print_ksm_pages(pm_map_t **maps, size_t num_maps, bool verbose) {
135 char filename[MAX_FILENAME];
138 struct ksm_page *pages;
139 size_t pages_len, pages_size;
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) {
150 data = malloc(pm_kernel_pagesize(ker));
152 fprintf(stderr, "warning: not enough memory to malloc data buffer\n");
156 fd = open(filename, O_RDONLY);
158 fprintf(stderr, "warning: could not open %s\n", filename);
166 for (i = 0; i < num_maps; i++) {
167 error = pm_map_pagemap(maps[i], &pagemap, &map_len);
169 fprintf(stderr, "warning: could not read the pagemap of %d\n",
170 pm_process_pid(maps[i]->proc));
172 for (j = 0; j < map_len; j++) {
173 error = pm_kernel_flags(ker, pagemap[j], &flags);
175 fprintf(stderr, "warning: could not read flags for pfn at address 0x%016llx\n",
179 if (!(flags & PM_PAGE_KSM)) {
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);
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);
194 hash = hashword(data, pm_kernel_pagesize(ker) / sizeof(*data), 17);
196 for (k = 0; k < pages_len; k++) {
197 if (pages[k].hash == hash) break;
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));
205 fprintf(stderr, "warning: not enough memory to realloc pages struct\n");
209 memset(&tmp[k], 0, sizeof(tmp[k]) * GROWTH_FACTOR);
211 pages_size += GROWTH_FACTOR;
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;
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)));
224 fprintf(stderr, "warning: not enough memory to realloc vaddr array\n");
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;
232 pages[k].vaddr[pages[k].vaddr_len] = vaddr;
234 pages[k].vaddr_len++;
239 for (i = 0; i < pages_len; i++) {
240 if (pages[i].pattern != NO_PATTERN) {
241 printf("0x%02x byte pattern: ", pages[i].pattern);
243 printf("KSM CRC 0x%08x:", pages[i].hash);
245 printf(" %4d page", pages[i].vaddr_len);
246 if (pages[i].vaddr_len > 1) {
253 while (j < pages[i].vaddr_len) {
255 for (k = 0; k < 8 && j < pages[i].vaddr_len; k++, j++) {
256 printf(" 0x%08lx", pages[i].vaddr[j]);
265 for (i = 0; i < pages_len; i++) {
266 free(pages[i].vaddr);
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",
283 static bool is_pattern(uint8_t *data, size_t len) {
285 uint8_t first_byte = data[0];
287 for (i = 1; i < len; i++) {
288 if (first_byte != data[i]) return false;
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
299 * Note that fgets(3) only declares length as an int, so our buffer size is
300 * also declared as an int.
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
308 static int getprocname(pid_t pid, char *buf, int len) {
312 static const char* unknown_cmdline = "<unknown>";
318 if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
323 f = fopen(filename, "r");
326 goto releasefilename;
329 if (fgets(buf, len, f) == NULL) {
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
345 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {