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