OSDN Git Service

am b4cf7b30: Add switches for compressor
[android-x86/system-extras.git] / libpagemap / pm_process.c
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <inttypes.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include <pagemap/pagemap.h>
26
27 #include "pm_map.h"
28
29 static int read_maps(pm_process_t *proc);
30
31 #define MAX_FILENAME 64
32
33 int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out) {
34     pm_process_t *proc;
35     char filename[MAX_FILENAME];
36     int error;
37
38     if (!ker || !proc_out)
39         return -1;
40
41     proc = calloc(1, sizeof(*proc));
42     if (!proc)
43         return errno;
44
45     proc->ker = ker;
46     proc->pid = pid;
47
48     error = snprintf(filename, MAX_FILENAME, "/proc/%d/pagemap", pid);
49     if (error < 0 || error >= MAX_FILENAME) {
50         error = (error < 0) ? (errno) : (-1);
51         free(proc);
52         return error;
53     }
54
55     proc->pagemap_fd = open(filename, O_RDONLY);
56     if (proc->pagemap_fd < 0) {
57         error = errno;
58         free(proc);
59         return error;
60     }        
61
62     error = read_maps(proc);
63     if (error) {
64         free(proc);
65         return error;
66     }
67
68     *proc_out = proc;
69
70     return 0;
71 }
72
73 int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
74                         uint64_t flags_mask, uint64_t required_flags)
75 {
76     pm_memusage_t usage, map_usage;
77     int error;
78     int i;
79
80     if (!proc || !usage_out)
81         return -1;
82
83     pm_memusage_zero(&usage);
84
85     for (i = 0; i < proc->num_maps; i++) {
86         error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
87                                    required_flags);
88         if (error) return error;
89
90         pm_memusage_add(&usage, &map_usage);
91     }
92
93     memcpy(usage_out, &usage, sizeof(pm_memusage_t));
94
95     return 0;
96
97 }
98
99 int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
100     return pm_process_usage_flags(proc, usage_out, 0, 0);
101 }
102
103 int pm_process_pagemap_range(pm_process_t *proc,
104                              uint64_t low, uint64_t high,
105                              uint64_t **range_out, size_t *len) {
106     uint64_t firstpage;
107     uint64_t numpages;
108     uint64_t *range;
109     off64_t off;
110     int error;
111
112     if (!proc || (low > high) || !range_out || !len)
113         return -1;
114
115     if (low == high) {
116         *range_out = NULL;
117         *len = 0;
118         return 0;
119     }
120
121     firstpage = low / proc->ker->pagesize;
122     numpages = (high - low) / proc->ker->pagesize;
123
124     range = malloc(numpages * sizeof(uint64_t));
125     if (!range)
126         return errno;
127
128     off = lseek64(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
129     if (off == (off_t)-1) {
130         error = errno;
131         free(range);
132         return error;
133     }
134     error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
135     if (error == 0) {
136         /* EOF, mapping is not in userspace mapping range (probably vectors) */
137         *len = 0;
138         free(range);
139         *range_out = NULL;
140         return 0;
141     } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) {
142         error = (error < 0) ? errno : -1;
143         free(range);
144         return error;
145     }
146
147     *range_out = range;
148     *len = numpages;
149
150     return 0;
151 }
152
153 int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
154     pm_map_t **maps;
155
156     if (!proc || !maps_out || !len)
157         return -1;
158
159     if (proc->num_maps) {
160         maps = malloc(proc->num_maps * sizeof(pm_map_t*));
161         if (!maps)
162             return errno;
163
164         memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
165     
166         *maps_out = maps;
167     } else {
168         *maps_out = NULL;
169     }
170     *len = proc->num_maps;
171
172     return 0;
173 }
174
175 int pm_process_workingset(pm_process_t *proc,
176                           pm_memusage_t *ws_out, int reset) {
177     pm_memusage_t ws, map_ws;
178     char filename[MAX_FILENAME];
179     int fd;
180     int i, j;
181     int error;
182
183     if (!proc)
184         return -1;
185
186     if (ws_out) {
187         pm_memusage_zero(&ws);
188         for (i = 0; i < proc->num_maps; i++) {
189             error = pm_map_workingset(proc->maps[i], &map_ws);
190             if (error) return error;
191
192             pm_memusage_add(&ws, &map_ws);
193         }
194         
195         memcpy(ws_out, &ws, sizeof(ws));
196     }
197
198     if (reset) {
199         error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
200                          proc->pid);
201         if (error < 0 || error >= MAX_FILENAME) {
202             return (error < 0) ? (errno) : (-1);
203         }
204
205         fd = open(filename, O_WRONLY);
206         if (fd < 0)
207             return errno;
208
209         write(fd, "1\n", strlen("1\n"));
210
211         close(fd);
212     }
213
214     return 0;
215 }
216
217 int pm_process_destroy(pm_process_t *proc) {
218     int i;
219
220     if (!proc)
221         return -1;
222
223     for (i = 0; i < proc->num_maps; i++) {
224         pm_map_destroy(proc->maps[i]);
225     }
226     free(proc->maps);
227     close(proc->pagemap_fd);
228     free(proc);
229
230     return 0;
231 }
232
233 #define INITIAL_MAPS 10
234 #define MAX_LINE 1024
235 #define MAX_PERMS 5
236
237 /* 
238  * #define FOO 123
239  * S(FOO) => "123"
240  */
241 #define _S(n) #n
242 #define S(n) _S(n)
243
244 static int read_maps(pm_process_t *proc) {
245     char filename[MAX_FILENAME];
246     char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS];
247     FILE *maps_f;
248     pm_map_t *map, **maps, **new_maps;
249     int maps_count, maps_size;
250     int error;
251        
252     if (!proc)
253         return -1;
254
255     maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
256     if (!maps)
257         return errno;
258     maps_count = 0; maps_size = INITIAL_MAPS;
259
260     error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
261     if (error < 0 || error >= MAX_FILENAME) {
262         free(maps);
263         return (error < 0) ? (errno) : (-1);
264     }
265
266     maps_f = fopen(filename, "r");
267     if (!maps_f) {
268         free(maps);
269         return errno;
270     }
271
272     while (fgets(line, MAX_LINE, maps_f)) {
273         if (maps_count >= maps_size) {
274             new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
275             if (!new_maps) {
276                 error = errno;
277                 free(maps);
278                 fclose(maps_f);
279                 return error;
280             }
281             maps = new_maps;
282             maps_size *= 2;
283         }
284
285         maps[maps_count] = map = calloc(1, sizeof(*map));
286
287         map->proc = proc;
288
289         name[0] = '\0';
290         sscanf(line, "%" SCNx64 "-%" SCNx64 " %s %" SCNx64 " %*s %*d %" S(MAX_LINE) "s",
291                &map->start, &map->end, perms, &map->offset, name);
292
293         map->name = malloc(strlen(name) + 1);
294         if (!map->name) {
295             error = errno;
296             for (; maps_count > 0; maps_count--)
297                 pm_map_destroy(maps[maps_count]);
298             free(maps);
299             fclose(maps_f);
300             return error;
301         }
302         strcpy(map->name, name);
303         if (perms[0] == 'r') map->flags |= PM_MAP_READ;
304         if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
305         if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
306
307         maps_count++;
308     }
309
310     fclose(maps_f);
311
312     new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
313     if (maps_count && !new_maps) {
314         error = errno;
315         free(maps);
316         return error;
317     }
318
319     proc->maps = new_maps;
320     proc->num_maps = maps_count;
321
322     return 0;
323 }