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
pm_process_create(pm_kernel_t * ker,pid_t pid,pm_process_t ** proc_out)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
pm_process_usage_flags(pm_process_t * proc,pm_memusage_t * usage_out,uint64_t flags_mask,uint64_t required_flags)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
pm_process_usage(pm_process_t * proc,pm_memusage_t * usage_out)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
pm_process_pagemap_range(pm_process_t * proc,uint64_t low,uint64_t high,uint64_t ** range_out,size_t * len)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
pm_process_maps(pm_process_t * proc,pm_map_t *** maps_out,size_t * len)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
pm_process_workingset(pm_process_t * proc,pm_memusage_t * ws_out,int reset)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
pm_process_destroy(pm_process_t * proc)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
read_maps(pm_process_t * proc)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 return (error < 0) ? (errno) : (-1);
263
264 maps_f = fopen(filename, "r");
265 if (!maps_f)
266 return errno;
267
268 while (fgets(line, MAX_LINE, maps_f)) {
269 if (maps_count >= maps_size) {
270 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
271 if (!new_maps) {
272 error = errno;
273 free(maps);
274 fclose(maps_f);
275 return error;
276 }
277 maps = new_maps;
278 maps_size *= 2;
279 }
280
281 maps[maps_count] = map = calloc(1, sizeof(*map));
282
283 map->proc = proc;
284
285 name[0] = '\0';
286 sscanf(line, "%" SCNx64 "-%" SCNx64 " %s %" SCNx64 " %*s %*d %" S(MAX_LINE) "s",
287 &map->start, &map->end, perms, &map->offset, name);
288
289 map->name = malloc(strlen(name) + 1);
290 if (!map->name) {
291 error = errno;
292 for (; maps_count > 0; maps_count--)
293 pm_map_destroy(maps[maps_count]);
294 free(maps);
295 return error;
296 }
297 strcpy(map->name, name);
298 if (perms[0] == 'r') map->flags |= PM_MAP_READ;
299 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
300 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
301
302 maps_count++;
303 }
304
305 fclose(maps_f);
306
307 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
308 if (maps_count && !new_maps) {
309 error = errno;
310 free(maps);
311 return error;
312 }
313
314 proc->maps = new_maps;
315 proc->num_maps = maps_count;
316
317 return 0;
318 }
319