]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
parser: Free pidlist entries after removed from the list
[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
109         if (is_parse_option(opts, PARSE_PID)) {
110                 list_for_each_entry(pid, &opts->pidlist, list) {
111                         if (pid->pid == process->pid) {
112                                 match = 1;
113                                 break;
114                         }
115                 }
116         }
117
118         if (is_parse_option(opts, PARSE_MAP_NAME))
119                 match = 1;
120
121         if (is_parse_option(opts, PARSE_NOADD_TREE))
122                 match = !match;
123
124         return match;
125 }
126
127 static int should_scan_mapping(struct parse_opts *opts, struct maps *map)
128 {
129         int match = 0;
130
131         if (is_parse_option(opts, PARSE_MAP_NAME)) {
132                 if (strstr(map->name, opts->name))
133                         match = 1;
134
135                 if (is_parse_option(opts, PARSE_NOADD_TREE))
136                         match = !match;
137         } else
138                 match = 1;
139
140         return match;
141 }
142
143 static int should_add_to_tree(struct parse_opts *opts, struct pageframe *pf,
144                 struct maps *map)
145 {
146         if (is_parse_option(opts, PARSE_NOADD_TREE))
147                 return 0;
148
149         return 1;
150 }
151
152 /* Read data from the /proc/pid/pagemap file */
153 static int parse_pageframe(FILE *file, struct pageframe *pf_tree,
154                         struct maps *maps, struct parse_opts *opts)
155 {
156         struct maps *map;
157         struct maps_list *tmp;
158         struct pageframe *match, *pageframe = NULL;
159         long start, len, i;
160         unsigned long long pf[10240];
161         int ret, error;
162
163         if (maps == NULL)
164                 return 0;
165
166         /* Go through the list of allocated memory areas */
167         list_for_each_entry(map, &maps->list, list) {
168                 start = map->start >> (PAGE_SHIFT - 3);
169                 len = map->size >> (PAGE_SHIFT);
170
171                 if (!should_scan_mapping(opts, map))
172                         continue;
173
174                 ret = fseek(file, start, SEEK_SET);
175                 if (ret) {
176                         error = errno;
177                         fprintf(stderr, "Error seeking to %lx: %s\n", start,
178                                 strerror(error));
179                         continue;
180                 }
181
182                 for (i = 0; i < len; i++) {
183                         if (!ret) {
184                                 ret = fread(&pf, 1,
185                                         MIN(sizeof(pf), (len - i) * 8), file);
186                         }
187                         if (ret < 0) {
188                                 error = errno;
189                                 continue;
190                         }
191                         if (!pageframe)
192                                 pageframe = alloc_pageframe();
193                         ret -= sizeof(pf[0]);
194
195                         /* ignore unused pages */
196                         if (!pf[ret / sizeof(pf[0])])
197                                 continue;
198
199                         pageframe->pf = (pf[ret / sizeof(pf[0])]);
200
201                         /* ignore unused pages */
202                         if (!(page_swapped(pageframe) ||
203                                         page_present(pageframe)))
204                                 continue;
205
206                         if (should_add_to_tree(opts, pageframe, map)) {
207                                 match = tree_to_pageframe(
208                                         bintree_add(&pf_tree->tree,
209                                                 &pageframe->tree,
210                                                 &pageframe_ops));
211                         } else {
212                                 match = tree_to_pageframe(
213                                         bintree_find(&pf_tree->tree,
214                                                 &pageframe->tree,
215                                                 &pageframe_ops));
216                         }
217
218                         if (match == NULL)
219                                 continue;
220
221                         if (match == pageframe)
222                                 pageframe = NULL;
223
224                         match->refcount++;
225                         /*
226                          * Add a link from the physical page to this
227                          * process's page map
228                          */
229                         tmp = alloc_maplist();
230                         tmp->map = map;
231                         list_add(&tmp->list, &match->ml);
232
233                         if (page_present(match))
234                                 map->pages_present++;
235                         else if (page_swapped(match))
236                                 map->pages_swapped++;
237                 }
238         }
239
240         return 0;
241 }
242
243 static int read_pageframe(int pid, int tid, struct pageframe *pageframe,
244                         struct process *process_list, struct parse_opts *opts)
245 {
246         struct maps *maps;
247         struct process *process;
248         struct pidlist *pidl, *n;
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         /*
301          * Remove the pid from the list. It is no longer an
302          * interesting pid, since we can't access its data
303          */
304         list_for_each_entry_safe(pidl, n, &opts->pidlist, list) {
305                 if (pidl->pid == pid) {
306                         list_del(&pidl->list);
307                         free(pidl);
308                         break;
309                 }
310         }
311
312         return 0;
313 }
314
315 static int read_pageframe_with_threads(int pid,
316                                 struct pageframe *pageframe,
317                                 struct process *process_list,
318                                 struct parse_opts *opts)
319 {
320         DIR *dir = NULL;
321         int tid;
322         int count = 0;
323
324         while (1) {
325                 if (opts->with_threads)
326                         tid = get_next_tid(pid, &dir);
327                 else
328                         tid = pid;
329
330                 if (tid <= 0)
331                         return count;
332
333                 count += read_pageframe(pid, tid, pageframe, process_list,
334                                         opts);
335
336                 if (!opts->with_threads)
337                         break;
338         }
339
340         return count;
341 }
342
343 int scan_all_pids(struct pageframe *pf, 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
351         if (is_parse_option(opts, PARSE_PID)) {
352                 list_for_each_entry_safe(pidlist, n, &opts->pidlist, list) {
353                         count += read_pageframe_with_threads(pidlist->pid, pf,
354                                                         process_list, opts);
355                 }
356         }
357
358         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
359                 printf("Failed to find any matching processes "
360                         "with given arguments\n");
361                 return -1;
362         }
363
364         if (is_parse_option(opts, PARSE_DUMP))
365                 return 0;
366
367         if (is_parse_option(opts, PARSE_MAP_NAME)) {
368                 while (1) {
369                         pid = get_next_pid(&dir);
370                         if (pid <= 0)
371                                 break;
372                         read_pageframe_with_threads(pid, pf, process_list,
373                                                 opts);
374                 }
375         }
376         /* Do not add new pages in the tree after the initial scan */
377         opts->parse_mask |= PARSE_NOADD_TREE;
378
379         while (1) {
380                 pid = get_next_pid(&dir);
381                 if (pid <= 0)
382                         break;
383                 read_pageframe_with_threads(pid, pf, process_list, opts);
384         }
385
386         return 0;
387 }