]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
Parser: Remove support for parsing processes by name
[scan-pagemap] / parse.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5
6 #include "parse.h"
7 #include "pagemap.h"
8 #include "pidlib.h"
9
10 static struct maps_list *alloc_maplist(void)
11 {
12         struct maps_list *map;
13
14         map = malloc(sizeof *map);
15         if (map == NULL)
16                 goto err;
17
18         memset(map, 0, sizeof(*map));
19         INIT_LIST_HEAD(&map->list);
20 err:
21         return map;
22 }
23
24 static struct maps *alloc_map(void)
25 {
26         struct maps *map;
27
28         map = malloc(sizeof *map);
29         if (map == NULL)
30                 goto err;
31
32         memset(map, 0, sizeof(*map));
33         INIT_LIST_HEAD(&map->list);
34 err:
35         return map;
36 }
37
38 static struct maps *parse_maps(FILE *file, int pid, int tid)
39 {
40         struct maps *the_map = NULL;
41         char line[1024];
42         int ret;
43
44         while (fgets(line, sizeof(line), file)) {
45                 struct maps *map = alloc_map();
46                 unsigned long start, end;
47                 char name[1024];
48
49                 if (map == NULL)
50                         return 0;
51
52                 if (the_map == NULL)
53                         the_map = map;
54
55                 ret = sscanf(line, "%lx-%lx %*s %*s %*s %*s %s",
56                              &start, &end, name);
57
58                 if (ret < 2) {
59                         printf("Error reading input: %s\n", line);
60                         break;
61                 }
62
63                 map->start = start;
64                 map->end = end;
65                 map->size = end - start;
66                 map->pid = pid;
67                 map->tid = tid;
68
69                 if (ret >= 3)
70                         strncpy(map->name, name, sizeof(map->name));
71
72                 list_add_tail(&map->list, &the_map->list);
73         }
74
75         return the_map;
76 }
77
78 static struct pageframe *alloc_pageframe(void)
79 {
80         struct pageframe *pageframe;
81
82         pageframe = malloc(sizeof *pageframe);
83         if (pageframe == NULL)
84                 goto err;
85
86         clear_pageframe(pageframe);
87 err:
88         return pageframe;
89 }
90
91 static int compare_pageframe(struct bintree *at, struct bintree *bt)
92 {
93         struct pageframe *a, *b;
94         a = tree_to_pageframe(at);
95         b = tree_to_pageframe(bt);
96
97         return a->pf - b->pf;
98 }
99
100 struct bintree_ops pageframe_ops = {
101         .compare = compare_pageframe,
102 };
103
104 static int should_scan_process(struct parse_opts *opts, struct process *process)
105 {
106         struct pidlist *pid;
107         int match = 0;
108         char *name;
109
110         if (is_parse_option(opts, PARSE_PID)) {
111                 list_for_each_entry(pid, &opts->pidlist, list) {
112                         if (pid->pid == process->pid) {
113                                 match = 1;
114                                 break;
115                         }
116                 }
117         }
118
119         if (is_parse_option(opts, PARSE_MAP_NAME))
120                 match = 1;
121
122         if (is_parse_option(opts, PARSE_NOADD_TREE))
123                 match = !match;
124
125         return match;
126 }
127
128 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
129 {
130         int match = 0;
131
132         if (is_parse_option(opts, PARSE_MAP_NAME)) {
133                 if (!strcmp(opts->name, map->name))
134                         match = 1;
135
136                 if (is_parse_option(opts, PARSE_NOADD_TREE))
137                         match = !match;
138         } else
139                 match = 1;
140
141         return match;
142 }
143
144 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
145                 struct maps *map)
146 {
147         if (is_parse_option(opts, PARSE_NOADD_TREE))
148                 return 0;
149
150         return 1;
151 }
152
153 /* Read data from the /proc/pid/pagemap file */
154 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
155                         struct maps *maps, struct parse_opts *opts)
156 {
157         struct maps *map;
158         struct maps_list *tmp;
159         struct pageframe *match, *pageframe = NULL;
160         long start, len, i;
161         unsigned long long pf[10240];
162         int ret, error;
163
164         if (maps == NULL)
165                 return 0;
166
167         /* Go through the list of allocated memory areas */
168         list_for_each_entry(map, &maps->list, list) {
169                 start = map->start >> (PAGE_SHIFT - 3);
170                 len = map->size >> (PAGE_SHIFT);
171
172                 if (!should_scan_mapping(opts, map))
173                         continue;
174
175                 ret = fseek(file, start, SEEK_SET);
176                 if (ret) {
177                         error = errno;
178                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
179                                 strerror(error));
180                         continue;
181                 }
182
183                 for (i = 0; i < len; i++) {
184                         if (!ret) {
185                                 ret = fread(&pf, 1,
186                                         MIN(sizeof(pf), (len - i) * 8), file);
187                         }
188                         if (ret < 0) {
189                                 error = errno;
190                                 continue;
191                         }
192                         if (!pageframe)
193                                 pageframe = alloc_pageframe();
194                         ret -= sizeof(pf[0]);
195
196                         /* ignore unused pages */
197                         if (!pf[ret / sizeof(pf[0])])
198                                 continue;
199
200                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
201
202                         /* ignore unused pages */
203                         if (!(page_swapped(pageframe) ||
204                                         page_present(pageframe)))
205                                 continue;
206
207                         if (should_add_to_tree(opts, pageframe, map)) {
208                                 match = tree_to_pageframe(
209                                         bintree_add(&pf_tree->tree,
210                                                 &pageframe->tree,
211                                                 &pageframe_ops));
212                         } else {
213                                 match = tree_to_pageframe(
214                                         bintree_find(&pf_tree->tree,
215                                                 &pageframe->tree,
216                                                 &pageframe_ops));
217                         }
218
219                         if (match == NULL)
220                                 continue;
221
222                         if (match == pageframe)
223                                 pageframe = NULL;
224
225                         match->refcount++;
226                         /*
227                          * Add a link from the physical page to this
228                          * process's page map
229                          */
230                         tmp = alloc_maplist();
231                         tmp->map = map;
232                         list_add(&tmp->list, &match->ml);
233
234                         if (page_present(match))
235                                 map->pages_present++;
236                         else if (page_swapped(match))
237                                 map->pages_swapped++;
238                 }
239         }
240
241         return 0;
242 }
243
244 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
245                         struct process *process_list, struct parse_opts *opts)
246 {
247         struct maps *maps;
248         struct process *process;
249         FILE *file;
250         char path[512];
251
252         process = malloc(sizeof(*process));
253         memset(process, 0, sizeof(*process));
254         INIT_LIST_HEAD(&process->list);
255
256         process->pid = pid;
257         process->tid = tid;
258
259         if (!should_scan_process(opts, process))
260                 goto free;
261
262         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
263         file = fopen(path, "rb");
264
265         if (!file)
266                 goto free;
267
268         maps = parse_maps(file, pid, tid);
269         fclose(file);
270         process->maps = maps;
271
272         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
273         file = fopen(path, "rb");
274
275         if (!file)
276                 goto free;
277
278         parse_pageframe(file, pageframe, maps, opts);
279         fclose(file);
280
281         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
282                 goto free;
283
284         if (maps != NULL) {
285                 list_for_each_entry(maps, &process->maps->list, list) {
286                         process->pages_present += maps->pages_present;
287                         process->pages_swapped += maps->pages_swapped;
288                 }
289         }
290
291         if (!is_parse_option(opts, PARSE_NOADD_TREE))
292                 process->is_initial_pid = 1;
293
294         list_add_tail(&process->list, &process_list->list);
295
296         return 1;
297 free:
298         free(process);
299
300         return 0;
301 }
302
303 static int read_pageframe_with_threads(int pid,
304                                 struct pageframe *pageframe,
305                                 struct process *process_list,
306                                 struct parse_opts *opts)
307 {
308         DIR *dir = NULL;
309         int tid;
310         int count = 0;
311
312         while (1) {
313                 if (opts->with_threads)
314                         tid = get_next_tid(pid, &dir);
315                 else
316                         tid = pid;
317
318                 if (tid <= 0)
319                         return count;
320
321                 count += read_pageframe(pid, tid, pageframe, process_list,
322                                         opts);
323
324                 if (!opts->with_threads)
325                         break;
326         }
327
328         return count;
329 }
330
331 int scan_all_pids(struct pageframe *pf, struct process *process_list,
332                 struct parse_opts *opts)
333 {
334         struct pidlist *pidlist;
335         DIR *dir = NULL;
336         int pid;
337         int count = 0;
338
339         if (is_parse_option(opts, PARSE_PID)) {
340                 list_for_each_entry(pidlist, &opts->pidlist, list) {
341                         count += read_pageframe_with_threads(pidlist->pid, pf,
342                                                         process_list, opts);
343                 }
344         }
345
346         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
347                 printf("Failed to find any matching processes "
348                         "with given arguments\n");
349                 return -1;
350         }
351
352         if (is_parse_option(opts, PARSE_DUMP))
353                 return 0;
354
355         if (is_parse_option(opts, PARSE_MAP_NAME)) {
356                 while (1) {
357                         pid = get_next_pid(&dir);
358                         if (pid <= 0)
359                                 break;
360                         read_pageframe_with_threads(pid, pf, process_list,
361                                                 opts);
362                 }
363         }
364         /* Do not add new pages in the tree after the initial scan */
365         opts->parse_mask |= PARSE_NOADD_TREE;
366
367         while (1) {
368                 pid = get_next_pid(&dir);
369                 if (pid <= 0)
370                         break;
371                 read_pageframe_with_threads(pid, pf, process_list, opts);
372         }
373
374         return 0;
375 }