]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
Show full process argument list instead only executable name
[scan-pagemap] / parse.c
1 /*
2  * Copyright (C) 2010 Timo Kokkonen <timo.t.kokkonen@iki.fi>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include "parse.h"
26 #include "pagemap.h"
27 #include "pidlib.h"
28
29 static struct maps_list *alloc_maplist(void)
30 {
31         struct maps_list *map;
32
33         map = calloc(sizeof *map, 1);
34         if (map == NULL)
35                 goto err;
36
37         INIT_LIST_HEAD(&map->list);
38 err:
39         return map;
40 }
41
42 static struct maps *alloc_map(void)
43 {
44         struct maps *map;
45
46         map = calloc(sizeof(*map), 1);
47         if (map == NULL)
48                 goto err;
49
50         INIT_LIST_HEAD(&map->list);
51 err:
52         return map;
53 }
54
55 static struct maps *parse_maps(FILE *file, int pid, int tid)
56 {
57         struct maps *the_map = NULL;
58         char line[1024];
59         int ret;
60
61         while (fgets(line, sizeof(line), file)) {
62                 struct maps *map = alloc_map();
63                 unsigned long start, end;
64                 int skip;
65
66                 if (map == NULL)
67                         return 0;
68
69                 if (the_map == NULL)
70                         the_map = map;
71
72                 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %n",
73                              &start, &end, &skip);
74
75                 if (ret < 2) {
76                         printf("Error reading input: %s\n", line);
77                         break;
78                 }
79
80                 map->start = start;
81                 map->end = end;
82                 map->size = end - start;
83                 map->pid = pid;
84                 map->tid = tid;
85
86                 strncpy(map->name, line + skip, sizeof(map->name));
87
88                 /* zero out the newline */
89                 map->name[MAX(strlen(map->name) - 1, 0)] = '\0';
90
91                 list_add_tail(&map->list, &the_map->list);
92         }
93
94         return the_map;
95 }
96
97 static struct pageframe *alloc_pageframe(void)
98 {
99         struct pageframe *pageframe;
100
101         pageframe = malloc(sizeof *pageframe);
102         if (pageframe == NULL)
103                 goto err;
104
105         clear_pageframe(pageframe);
106 err:
107         return pageframe;
108 }
109
110 static int pid_is_match(int pidn, struct list_head *pidlist)
111 {
112         struct pidlist *pid;
113
114         list_for_each_entry(pid, pidlist, list) {
115                 if (pid->pid == pidn)
116                         return 1;
117         }
118
119         return 0;
120 }
121
122 static int should_scan_process(struct parse_opts *opts, struct process *process)
123 {
124         int match = 0;
125
126         if (is_parse_option(opts, PARSE_PID) &&
127                 is_parse_option(opts, PARSE_MAP_NAME)) {
128                 if (pid_is_match(process->pid, &opts->pidlist))
129                         match = 1;
130         } else {
131                 if (is_parse_option(opts, PARSE_PID)) {
132                         if (pid_is_match(process->pid, &opts->pidlist))
133                                 match = 1;
134                 }
135
136                 if (is_parse_option(opts, PARSE_MAP_NAME))
137                         match = 1;
138         }
139
140         if (is_parse_option(opts, PARSE_NOADD_TREE))
141                 match = !match;
142
143         return match;
144 }
145
146 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
147 {
148         int match = 0;
149
150         if (is_parse_option(opts, PARSE_MAP_NAME)) {
151                 if (strstr(map->name, opts->name))
152                         match = 1;
153
154                 if (is_parse_option(opts, PARSE_NOADD_TREE))
155                         match = !match;
156         } else
157                 match = 1;
158
159         return match;
160 }
161
162 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
163                 struct maps *map)
164 {
165         if (is_parse_option(opts, PARSE_NOADD_TREE))
166                 return 0;
167
168         return 1;
169 }
170
171 /* Read data from the /proc/pid/pagemap file */
172 static int parse_pageframe(FILE *file, struct rb_root *root,
173                         struct maps *maps, struct parse_opts *opts)
174 {
175         struct maps *map;
176         struct maps_list *tmp;
177         struct pageframe *match, *pageframe = NULL;
178         long start, len, i;
179         unsigned long long pf[10240];
180         int ret;
181
182         if (maps == NULL)
183                 return 0;
184
185         /* Go through the list of allocated memory areas */
186         list_for_each_entry(map, &maps->list, list) {
187                 start = map->start >> (PAGE_SHIFT - 3);
188                 len = map->size >> (PAGE_SHIFT);
189
190                 if (!should_scan_mapping(opts, map))
191                         continue;
192
193                 ret = fseek(file, start, SEEK_SET);
194                 if (ret) {
195                         fprintf(stderr, "Error seeking to %lx: %m\n", start);
196                         continue;
197                 }
198
199                 for (i = 0; i < len; i++) {
200                         if (!ret) {
201                                 ret = fread(&pf, 1,
202                                         MIN(sizeof(pf), (len - i) * 8), file);
203                         }
204                         if (ret < 0) {
205                                 continue;
206                         }
207                         if (!pageframe)
208                                 pageframe = alloc_pageframe();
209                         ret -= sizeof(pf[0]);
210
211                         /* ignore unused pages */
212                         if (!pf[ret / sizeof(pf[0])])
213                                 continue;
214
215                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
216
217                         /* ignore unused pages */
218                         if (!(page_swapped(pageframe) ||
219                                         page_present(pageframe)))
220                                 continue;
221
222                         if (should_add_to_tree(opts, pageframe, map))
223                                 match = pf_insert(root, pageframe);
224                         else
225                                 match = pf_search(root, pageframe);
226
227                         if (match == NULL)
228                                 continue;
229
230                         if (match == pageframe)
231                                 pageframe = NULL;
232
233                         match->refcount++;
234                         /*
235                          * Add a link from the physical page to this
236                          * process's page map
237                          */
238                         tmp = alloc_maplist();
239                         tmp->map = map;
240                         list_add(&tmp->list, &match->ml);
241
242                         if (page_present(match))
243                                 map->pages_present++;
244                         else if (page_swapped(match))
245                                 map->pages_swapped++;
246                 }
247         }
248
249         return 0;
250 }
251
252 static int read_pageframe(int pid, int tid, struct rb_root *root,
253                         struct process *process_list, struct parse_opts *opts)
254 {
255         struct maps *maps;
256         struct process *process;
257         struct pidlist *pidl, *n;
258         FILE *file;
259         char path[512];
260
261         process = calloc(sizeof(*process), 1);
262         INIT_LIST_HEAD(&process->list);
263
264         process->pid = pid;
265         process->tid = tid;
266
267         if (!should_scan_process(opts, process))
268                 goto free;
269
270         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
271         file = fopen(path, "rb");
272
273         if (!file)
274                 goto free;
275
276         maps = parse_maps(file, pid, tid);
277         fclose(file);
278         process->maps = maps;
279
280         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
281         file = fopen(path, "rb");
282
283         if (!file)
284                 goto free;
285
286         parse_pageframe(file, root, maps, opts);
287         fclose(file);
288
289         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
290                 goto free;
291
292         if (!is_parse_option(opts, PARSE_NOADD_TREE))
293                 process->is_initial_pid = 1;
294
295         list_add_tail(&process->list, &process_list->list);
296
297         return 1;
298 free:
299         free(process);
300
301         /*
302          * Remove the pid from the list. It is no longer an
303          * interesting pid, since we can't access its data
304          */
305         list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
306                 if (pidl->pid == pid) {
307                         list_del(&pidl->list);
308                         free(pidl);
309                         break;
310                 }
311         }
312
313         return 0;
314 }
315
316 static int read_pageframe_with_threads(int pid,
317                                 struct rb_root *root,
318                                 struct process *process_list,
319                                 struct parse_opts *opts)
320 {
321         DIR *dir = NULL;
322         int tid;
323         int count = 0;
324
325         while (1) {
326                 if (opts->with_threads)
327                         tid = get_next_tid(pid, &dir);
328                 else
329                         tid = pid;
330
331                 if (tid <= 0)
332                         return count;
333
334                 count += read_pageframe(pid, tid, root, process_list, opts);
335
336                 if (!opts->with_threads)
337                         break;
338         }
339
340         return count;
341 }
342
343 int scan_all_pids(struct rb_root *root, struct process *process_list,
344                 struct parse_opts *opts)
345 {
346         struct pidlist *pidlist, *n;
347         DIR *dir = NULL;
348         int pid;
349         int count = 0;
350         int len = 0, i;
351
352         if (is_parse_option(opts, PARSE_PID)) {
353                 list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
354                         count += read_pageframe_with_threads(pidlist->pid, root,
355                                                         process_list, opts);
356                 }
357         }
358
359         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
360                 printf("Failed to find any matching processes "
361                         "with given arguments\n");
362                 return -1;
363         }
364
365         if (is_parse_option(opts, PARSE_DUMP))
366                 return 0;
367
368         if (is_parse_option(opts, PARSE_MAP_NAME) &&
369                 !is_parse_option(opts, PARSE_PID)) {
370                 printf("Scanning page mappings for process: ");
371                 while (1) {
372                         pid = get_next_pid(&dir);
373                         if (pid <= 0)
374                                 break;
375
376                         for (i = 0; i < len; i++)
377                                 putchar('\b');
378                         len = printf("% 5d", pid);
379                         fflush(stdout);
380
381                         read_pageframe_with_threads(pid, root, process_list,
382                                                 opts);
383                 }
384
385                 for (i = 0; i < len; i++)
386                         putchar('\b');
387                 printf("Done  \n");
388                 len = 0;
389         }
390         /* Do not add new pages in the tree after the initial scan */
391         opts->parse_mask |= PARSE_NOADD_TREE;
392
393         printf("Scanning page mappings for process: ");
394         while (1) {
395                 pid = get_next_pid(&dir);
396                 if (pid <= 0)
397                         break;
398
399                 for (i = 0; i < len; i++)
400                         putchar('\b');
401                 len = printf("% 5d", pid);
402                 fflush(stdout);
403
404                 read_pageframe_with_threads(pid, root, process_list, opts);
405         }
406         for (i = 0; i < len; i++)
407                 putchar('\b');
408         printf("Done  \n");
409
410         return 0;
411 }
412
413 int update_kpageflags(struct rb_root *root)
414 {
415         struct pageframe *pf;
416         int kpageflags_fd;
417         int kpagecount_fd;
418         int ret;
419
420         kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
421         if (kpageflags_fd < 0)
422                 return -1;
423
424         kpagecount_fd = open("/proc/kpagecount", O_RDONLY);
425         if (kpagecount_fd < 0)
426                 return -1;
427
428         pf = rb_to_pageframe(rb_first(root));
429
430         while(pf) {
431                 long int pfnn = pfn(pf) * sizeof(pf->kpageflags);
432
433                 ret = lseek(kpageflags_fd, pfnn, SEEK_SET);
434                 if (ret < 0) {
435                         fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
436                         return -1;
437                 }
438
439                 ret = read(kpageflags_fd, &pf->kpageflags,
440                         sizeof(pf->kpageflags));
441                 if (ret < 0) {
442                         fprintf(stderr, "Error reading from %llx: %m\n",
443                                 pf->pf * sizeof(pf->kpageflags));
444                         return -1;
445                 }
446
447                 ret = lseek(kpagecount_fd, pfnn, SEEK_SET);
448                 if (ret < 0) {
449                         fprintf(stderr, "Error seeking to %lx: %m\n", pfnn);
450                         return -1;
451                 }
452
453                 ret = read(kpagecount_fd, &pf->kpagecount,
454                         sizeof(pf->kpagecount));
455                 if (ret < 0) {
456                         fprintf(stderr, "Error reading from %llx: %m\n",
457                                 pf->pf * sizeof(pf->kpagecount));
458                         return -1;
459                 }
460
461                 pf = rb_to_pageframe(rb_next(&pf->tree));
462         }
463
464         close(kpageflags_fd);
465         close(kpagecount_fd);
466
467         return 0;
468 }