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 <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <pagemap/pagemap.h>
25
26 #include "pm_map.h"
27
28 static int read_maps(pm_process_t *proc);
29
30 #define MAX_FILENAME 64
31
pm_process_create(pm_kernel_t * ker,pid_t pid,pm_process_t ** proc_out)32 int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out) {
33 pm_process_t *proc;
34 char filename[MAX_FILENAME];
35 int error;
36
37 if (!ker || !proc_out)
38 return -1;
39
40 proc = calloc(1, sizeof(*proc));
41 if (!proc)
42 return errno;
43
44 proc->ker = ker;
45 proc->pid = pid;
46
47 error = snprintf(filename, MAX_FILENAME, "/proc/%d/pagemap", pid);
48 if (error < 0 || error >= MAX_FILENAME) {
49 error = (error < 0) ? (errno) : (-1);
50 free(proc);
51 return error;
52 }
53
54 proc->pagemap_fd = open(filename, O_RDONLY);
55 if (proc->pagemap_fd < 0) {
56 error = errno;
57 free(proc);
58 return error;
59 }
60
61 error = read_maps(proc);
62 if (error) {
63 free(proc);
64 return error;
65 }
66
67 *proc_out = proc;
68
69 return 0;
70 }
71
pm_process_usage(pm_process_t * proc,pm_memusage_t * usage_out)72 int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
73 pm_memusage_t usage, map_usage;
74 int error;
75 int i;
76
77 if (!proc || !usage_out)
78 return -1;
79
80 pm_memusage_zero(&usage);
81
82 for (i = 0; i < proc->num_maps; i++) {
83 error = pm_map_usage(proc->maps[i], &map_usage);
84 if (error) return error;
85
86 pm_memusage_add(&usage, &map_usage);
87 }
88
89 memcpy(usage_out, &usage, sizeof(pm_memusage_t));
90
91 return 0;
92 }
93
pm_process_pagemap_range(pm_process_t * proc,unsigned long low,unsigned long high,uint64_t ** range_out,size_t * len)94 int pm_process_pagemap_range(pm_process_t *proc,
95 unsigned long low, unsigned long high,
96 uint64_t **range_out, size_t *len) {
97 int firstpage, numpages;
98 uint64_t *range;
99 off_t off;
100 int error;
101
102 if (!proc || (low >= high) || !range_out || !len)
103 return -1;
104
105 firstpage = low / proc->ker->pagesize;
106 numpages = (high - low) / proc->ker->pagesize;
107
108 range = malloc(numpages * sizeof(uint64_t));
109 if (!range)
110 return errno;
111
112 off = lseek(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
113 if (off == (off_t)-1) {
114 error = errno;
115 free(range);
116 return error;
117 }
118 error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
119 if (error == 0) {
120 /* EOF, mapping is not in userspace mapping range (probably vectors) */
121 *len = 0;
122 free(range);
123 *range_out = NULL;
124 return 0;
125 } else if (error < 0 || (error > 0 && error < numpages * sizeof(uint64_t))) {
126 error = (error < 0) ? errno : -1;
127 free(range);
128 return error;
129 }
130
131 *range_out = range;
132 *len = numpages;
133
134 return 0;
135 }
136
pm_process_maps(pm_process_t * proc,pm_map_t *** maps_out,size_t * len)137 int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
138 pm_map_t **maps;
139
140 if (!proc || !maps_out || !len)
141 return -1;
142
143 if (proc->num_maps) {
144 maps = malloc(proc->num_maps * sizeof(pm_map_t*));
145 if (!maps)
146 return errno;
147
148 memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
149
150 *maps_out = maps;
151 } else {
152 *maps_out = NULL;
153 }
154 *len = proc->num_maps;
155
156 return 0;
157 }
158
pm_process_workingset(pm_process_t * proc,pm_memusage_t * ws_out,int reset)159 int pm_process_workingset(pm_process_t *proc,
160 pm_memusage_t *ws_out, int reset) {
161 pm_memusage_t ws, map_ws;
162 char filename[MAX_FILENAME];
163 int fd;
164 int i, j;
165 int error;
166
167 if (!proc)
168 return -1;
169
170 if (ws_out) {
171 pm_memusage_zero(&ws);
172 for (i = 0; i < proc->num_maps; i++) {
173 error = pm_map_workingset(proc->maps[i], &map_ws);
174 if (error) return error;
175
176 pm_memusage_add(&ws, &map_ws);
177 }
178
179 memcpy(ws_out, &ws, sizeof(ws));
180 }
181
182 if (reset) {
183 error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
184 proc->pid);
185 if (error < 0 || error >= MAX_FILENAME) {
186 return (error < 0) ? (errno) : (-1);
187 }
188
189 fd = open(filename, O_WRONLY);
190 if (fd < 0)
191 return errno;
192
193 write(fd, "1\n", strlen("1\n"));
194
195 close(fd);
196 }
197
198 return 0;
199 }
200
pm_process_destroy(pm_process_t * proc)201 int pm_process_destroy(pm_process_t *proc) {
202 if (!proc)
203 return -1;
204
205 free(proc->maps);
206 close(proc->pagemap_fd);
207 free(proc);
208
209 return 0;
210 }
211
212 #define INITIAL_MAPS 10
213 #define MAX_LINE 256
214 #define MAX_PERMS 5
215
216 /*
217 * #define FOO 123
218 * S(FOO) => "123"
219 */
220 #define _S(n) #n
221 #define S(n) _S(n)
222
read_maps(pm_process_t * proc)223 static int read_maps(pm_process_t *proc) {
224 char filename[MAX_FILENAME];
225 char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS];
226 FILE *maps_f;
227 pm_map_t *map, **maps, **new_maps;
228 int maps_count, maps_size;
229 int error;
230
231 if (!proc)
232 return -1;
233
234 maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
235 if (!maps)
236 return errno;
237 maps_count = 0; maps_size = INITIAL_MAPS;
238
239 error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
240 if (error < 0 || error >= MAX_FILENAME)
241 return (error < 0) ? (errno) : (-1);
242
243 maps_f = fopen(filename, "r");
244 if (!maps_f)
245 return errno;
246
247 while (fgets(line, MAX_LINE, maps_f)) {
248 if (maps_count >= maps_size) {
249 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
250 if (!new_maps) {
251 error = errno;
252 free(maps);
253 fclose(maps_f);
254 return error;
255 }
256 maps = new_maps;
257 maps_size *= 2;
258 }
259
260 maps[maps_count] = map = calloc(1, sizeof(*map));
261
262 map->proc = proc;
263
264 sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s",
265 &map->start, &map->end, perms, &map->offset, name);
266
267 map->name = malloc(strlen(name) + 1);
268 if (!map->name) {
269 error = errno;
270 for (; maps_count > 0; maps_count--)
271 pm_map_destroy(maps[maps_count]);
272 free(maps);
273 return error;
274 }
275 strcpy(map->name, name);
276 if (perms[0] == 'r') map->flags |= PM_MAP_READ;
277 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
278 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
279
280 maps_count++;
281 }
282
283 fclose(maps_f);
284
285 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
286 if (maps_count && !new_maps) {
287 error = errno;
288 free(maps);
289 return error;
290 }
291
292 proc->maps = new_maps;
293 proc->num_maps = maps_count;
294
295 return 0;
296 }
297