]> git.itanic.dy.fi Git - scan-pagemap/blob - parse.c
parser: Remove unused variable
[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 (!strcmp(opts->name, map->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         FILE *file;
249         char path[512];
250
251         process = malloc(sizeof(*process));
252         memset(process, 0, sizeof(*process));
253         INIT_LIST_HEAD(&process->list);
254
255         process->pid = pid;
256         process->tid = tid;
257
258         if (!should_scan_process(opts, process))
259                 goto free;
260
261         snprintf(path, sizeof(path), "/proc/%d/task/%d/maps", pid, tid);
262         file = fopen(path, "rb");
263
264         if (!file)
265                 goto free;
266
267         maps = parse_maps(file, pid, tid);
268         fclose(file);
269         process->maps = maps;
270
271         snprintf(path, sizeof(path), "/proc/%d/task/%d/pagemap", pid, tid);
272         file = fopen(path, "rb");
273
274         if (!file)
275                 goto free;
276
277         parse_pageframe(file, pageframe, maps, opts);
278         fclose(file);
279
280         if (read_cmdline(pid, tid, process->name, sizeof(process->name)))
281                 goto free;
282
283         if (maps != NULL) {
284                 list_for_each_entry(maps, &process->maps->list, list) {
285                         process->pages_present += maps->pages_present;
286                         process->pages_swapped += maps->pages_swapped;
287                 }
288         }
289
290         if (!is_parse_option(opts, PARSE_NOADD_TREE))
291                 process->is_initial_pid = 1;
292
293         list_add_tail(&process->list, &process_list->list);
294
295         return 1;
296 free:
297         free(process);
298
299         return 0;
300 }
301
302 static int read_pageframe_with_threads(int pid,
303                                 struct pageframe *pageframe,
304                                 struct process *process_list,
305                                 struct parse_opts *opts)
306 {
307         DIR *dir = NULL;
308         int tid;
309         int count = 0;
310
311         while (1) {
312                 if (opts->with_threads)
313                         tid = get_next_tid(pid, &dir);
314                 else
315                         tid = pid;
316
317                 if (tid <= 0)
318                         return count;
319
320                 count += read_pageframe(pid, tid, pageframe, process_list,
321                                         opts);
322
323                 if (!opts->with_threads)
324                         break;
325         }
326
327         return count;
328 }
329
330 int scan_all_pids(struct pageframe *pf, struct process *process_list,
331                 struct parse_opts *opts)
332 {
333         struct pidlist *pidlist;
334         DIR *dir = NULL;
335         int pid;
336         int count = 0;
337
338         if (is_parse_option(opts, PARSE_PID)) {
339                 list_for_each_entry(pidlist, &opts->pidlist, list) {
340                         count += read_pageframe_with_threads(pidlist->pid, pf,
341                                                         process_list, opts);
342                 }
343         }
344
345         if ((count == 0) && !(is_parse_option(opts, PARSE_MAP_NAME))) {
346                 printf("Failed to find any matching processes "
347                         "with given arguments\n");
348                 return -1;
349         }
350
351         if (is_parse_option(opts, PARSE_DUMP))
352                 return 0;
353
354         if (is_parse_option(opts, PARSE_MAP_NAME)) {
355                 while (1) {
356                         pid = get_next_pid(&dir);
357                         if (pid <= 0)
358                                 break;
359                         read_pageframe_with_threads(pid, pf, process_list,
360                                                 opts);
361                 }
362         }
363         /* Do not add new pages in the tree after the initial scan */
364         opts->parse_mask |= PARSE_NOADD_TREE;
365
366         while (1) {
367                 pid = get_next_pid(&dir);
368                 if (pid <= 0)
369                         break;
370                 read_pageframe_with_threads(pid, pf, process_list, opts);
371         }
372
373         return 0;
374 }