]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
Makefile: Apply CFLAGS also when linking the final binary
[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_PROCESS_NAME)) {
111                 name = get_name_by_pid(process->pid);
112                 if (!strcmp(opts->name, name ? name : ""))
113                         match = 1;
114         }
115
116         if (is_parse_option(opts, PARSE_PID)) {
117                 list_for_each_entry(pid, &opts->pidlist, list) {
118                         if (pid->pid == process->pid) {
119                                 match = 1;
120                                 break;
121                         }
122                 }
123         }
124
125         if (is_parse_option(opts, PARSE_MAP_NAME))
126                 match = 1;
127
128         if (is_parse_option(opts, PARSE_NOADD_TREE))
129                 match = !match;
130
131         return match;
132 }
133
134 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
135 {
136         int match = 0;
137
138         if (is_parse_option(opts, PARSE_MAP_NAME)) {
139                 if (!strcmp(opts->name, map->name))
140                         match = 1;
141
142                 if (is_parse_option(opts, PARSE_NOADD_TREE))
143                         match = !match;
144         } else
145                 match = 1;
146
147         return match;
148 }
149
150 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
151                 struct maps *map)
152 {
153         if (is_parse_option(opts, PARSE_NOADD_TREE))
154                 return 0;
155
156         return 1;
157 }
158
159 /* Read data from the /proc/pid/pagemap file */
160 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
161                         struct maps *maps, struct parse_opts *opts)
162 {
163         struct maps *map;
164         struct maps_list *tmp;
165         struct pageframe *match, *pageframe = NULL;
166         long start, len, i;
167         unsigned long long pf[10240];
168         int ret, error;
169
170         if (maps == NULL)
171                 return 0;
172
173         /* Go through the list of allocated memory areas */
174         list_for_each_entry(map, &maps->list, list) {
175                 start = map->start >> (PAGE_SHIFT - 3);
176                 len = map->size >> (PAGE_SHIFT);
177
178                 if (!should_scan_mapping(opts, map))
179                         continue;
180
181                 ret = fseek(file, start, SEEK_SET);
182                 if (ret) {
183                         error = errno;
184                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
185                                 strerror(error));
186                         continue;
187                 }
188
189                 for (i = 0; i < len; i++) {
190                         if (!ret) {
191                                 ret = fread(&pf, 1,
192                                         MIN(sizeof(pf), (len - i) * 8), file);
193                         }
194                         if (ret < 0) {
195                                 error = errno;
196                                 continue;
197                         }
198                         if (!pageframe)
199                                 pageframe = alloc_pageframe();
200                         ret -= sizeof(pf[0]);
201
202                         /* ignore unused pages */
203                         if (!pf[ret / sizeof(pf[0])])
204                                 continue;
205
206                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
207
208                         /* ignore unused pages */
209                         if (!(page_swapped(pageframe) ||
210                                         page_present(pageframe)))
211                                 continue;
212
213                         if (should_add_to_tree(opts, pageframe, map)) {
214                                 match = tree_to_pageframe(
215                                         bintree_add(&pf_tree->tree,
216                                                 &pageframe->tree,
217                                                 &pageframe_ops));
218                         } else {
219                                 match = tree_to_pageframe(
220                                         bintree_find(&pf_tree->tree,
221                                                 &pageframe->tree,
222                                                 &pageframe_ops));
223                         }
224
225                         if (match == NULL)
226                                 continue;
227
228                         if (match == pageframe)
229                                 pageframe = NULL;
230
231                         match->refcount++;
232                         /*
233                          * Add a link from the physical page to this
234                          * process's page map
235                          */
236                         tmp = alloc_maplist();
237                         tmp->map = map;
238                         list_add(&tmp->list, &match->ml);
239
240                         if (page_present(match))
241                                 map->pages_present++;
242                         else if (page_swapped(match))
243                                 map->pages_swapped++;
244                 }
245         }
246
247         return 0;
248 }
249
250 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
251                         struct process *process_list, struct parse_opts *opts)
252 {
253         struct maps *maps;
254         struct process *process;
255         FILE *file;
256         char path[512];
257
258         process = malloc(sizeof(*process));
259         memset(process, 0, sizeof(*process));
260         INIT_LIST_HEAD(&process->list);
261
262         process->pid = pid;
263         process->tid = tid;
264
265         if (!should_scan_process(opts, process))
266                 goto free;
267
268         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
269         file = fopen(path, "rb");
270
271         if (!file)
272                 goto free;
273
274         maps = parse_maps(file, pid, tid);
275         fclose(file);
276         process->maps = maps;
277
278         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
279         file = fopen(path, "rb");
280
281         if (!file)
282                 goto free;
283
284         parse_pageframe(file, pageframe, maps, opts);
285         fclose(file);
286
287         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
288                 goto free;
289
290         if (maps != NULL) {
291                 list_for_each_entry(maps, &process->maps->list, list) {
292                         process->pages_present += maps->pages_present;
293                         process->pages_swapped += maps->pages_swapped;
294                 }
295         }
296
297         if (!is_parse_option(opts, PARSE_NOADD_TREE))
298                 process->is_initial_pid = 1;
299
300         list_add_tail(&process->list, &process_list->list);
301
302         return 1;
303 free:
304         free(process);
305
306         return 0;
307 }
308
309 static int read_pageframe_with_threads(int pid,
310                                 struct pageframe *pageframe,
311                                 struct process *process_list,
312                                 struct parse_opts *opts)
313 {
314         DIR *dir = NULL;
315         int tid;
316         int count = 0;
317
318         while (1) {
319                 if (opts->with_threads)
320                         tid = get_next_tid(pid, &dir);
321                 else
322                         tid = pid;
323
324                 if (tid <= 0)
325                         return count;
326
327                 count += read_pageframe(pid, tid, pageframe, process_list,
328                                         opts);
329
330                 if (!opts->with_threads)
331                         break;
332         }
333
334         return count;
335 }
336
337 int scan_all_pids(struct pageframe *pf, struct process *process_list,
338                 struct parse_opts *opts)
339 {
340         struct pidlist *pidlist;
341         DIR *dir = NULL;
342         int pid;
343         int count = 0;
344
345         if (is_parse_option(opts, PARSE_PROCESS_NAME)) {
346                 while ((pid = get_next_pid_by_name(&dir, opts->name))) {
347                         count += read_pageframe_with_threads(pid, pf,
348                                                         process_list,
349                                                         opts);
350                 }
351                 dir = NULL;
352         }
353
354         if (is_parse_option(opts, PARSE_PID)) {
355                 list_for_each_entry(pidlist, &opts->pidlist, list) {
356                         count += read_pageframe_with_threads(pidlist->pid, pf,
357                                                         process_list, opts);
358                 }
359         }
360
361         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
362                 printf("Failed to find any matching processes "
363                         "with given arguments\n");
364                 return -1;
365         }
366
367         if (is_parse_option(opts, PARSE_DUMP))
368                 return 0;
369
370         if (is_parse_option(opts, PARSE_MAP_NAME)) {
371                 while (1) {
372                         pid = get_next_pid(&dir);
373                         if (pid <= 0)
374                                 break;
375                         read_pageframe_with_threads(pid, pf, process_list,
376                                                 opts);
377                 }
378         }
379         /* Do not add new pages in the tree after the initial scan */
380         opts->parse_mask |= PARSE_NOADD_TREE;
381
382         while (1) {
383                 pid = get_next_pid(&dir);
384                 if (pid <= 0)
385                         break;
386                 read_pageframe_with_threads(pid, pf, process_list, opts);
387         }
388
389         return 0;
390 }