blob: 73c9649221f45f21fd37ee804147b95de88e0b9d [file] [log] [blame]
The Android Open Source Projecte16cb842009-03-03 19:32:58 -08001/*
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
28static int read_maps(pm_process_t *proc);
29
30#define MAX_FILENAME 64
31
32int 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
Colin Cross931563e2013-06-21 17:01:06 -070072int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
73 uint64_t flags_mask, uint64_t required_flags)
74{
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080075 pm_memusage_t usage, map_usage;
76 int error;
77 int i;
78
79 if (!proc || !usage_out)
80 return -1;
81
82 pm_memusage_zero(&usage);
83
84 for (i = 0; i < proc->num_maps; i++) {
Colin Cross931563e2013-06-21 17:01:06 -070085 error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
86 required_flags);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080087 if (error) return error;
88
89 pm_memusage_add(&usage, &map_usage);
90 }
91
92 memcpy(usage_out, &usage, sizeof(pm_memusage_t));
93
94 return 0;
Colin Cross931563e2013-06-21 17:01:06 -070095
96}
97
98int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
99 return pm_process_usage_flags(proc, usage_out, 0, 0);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800100}
101
102int pm_process_pagemap_range(pm_process_t *proc,
103 unsigned long low, unsigned long high,
104 uint64_t **range_out, size_t *len) {
105 int firstpage, numpages;
106 uint64_t *range;
107 off_t off;
108 int error;
109
110 if (!proc || (low >= high) || !range_out || !len)
111 return -1;
112
113 firstpage = low / proc->ker->pagesize;
114 numpages = (high - low) / proc->ker->pagesize;
115
116 range = malloc(numpages * sizeof(uint64_t));
117 if (!range)
118 return errno;
119
120 off = lseek(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
121 if (off == (off_t)-1) {
122 error = errno;
123 free(range);
124 return error;
125 }
126 error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
Colin Cross66201572011-07-13 13:35:20 -0700127 if (error == 0) {
128 /* EOF, mapping is not in userspace mapping range (probably vectors) */
129 *len = 0;
130 free(range);
131 *range_out = NULL;
132 return 0;
Selim Gurun76e6cb52012-01-20 19:44:54 -0800133 } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800134 error = (error < 0) ? errno : -1;
135 free(range);
136 return error;
137 }
138
139 *range_out = range;
140 *len = numpages;
141
142 return 0;
143}
144
145int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
146 pm_map_t **maps;
147
148 if (!proc || !maps_out || !len)
149 return -1;
150
151 if (proc->num_maps) {
152 maps = malloc(proc->num_maps * sizeof(pm_map_t*));
153 if (!maps)
154 return errno;
155
156 memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
157
158 *maps_out = maps;
159 } else {
160 *maps_out = NULL;
161 }
162 *len = proc->num_maps;
163
164 return 0;
165}
166
167int pm_process_workingset(pm_process_t *proc,
168 pm_memusage_t *ws_out, int reset) {
169 pm_memusage_t ws, map_ws;
170 char filename[MAX_FILENAME];
171 int fd;
172 int i, j;
173 int error;
174
175 if (!proc)
176 return -1;
177
178 if (ws_out) {
179 pm_memusage_zero(&ws);
180 for (i = 0; i < proc->num_maps; i++) {
181 error = pm_map_workingset(proc->maps[i], &map_ws);
182 if (error) return error;
183
184 pm_memusage_add(&ws, &map_ws);
185 }
186
187 memcpy(ws_out, &ws, sizeof(ws));
188 }
189
190 if (reset) {
191 error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
192 proc->pid);
193 if (error < 0 || error >= MAX_FILENAME) {
194 return (error < 0) ? (errno) : (-1);
195 }
196
197 fd = open(filename, O_WRONLY);
198 if (fd < 0)
199 return errno;
200
201 write(fd, "1\n", strlen("1\n"));
202
203 close(fd);
204 }
205
206 return 0;
207}
208
209int pm_process_destroy(pm_process_t *proc) {
210 if (!proc)
211 return -1;
212
213 free(proc->maps);
214 close(proc->pagemap_fd);
215 free(proc);
216
217 return 0;
218}
219
220#define INITIAL_MAPS 10
Selim Gurun76e6cb52012-01-20 19:44:54 -0800221#define MAX_LINE 1024
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800222#define MAX_PERMS 5
223
224/*
225 * #define FOO 123
226 * S(FOO) => "123"
227 */
228#define _S(n) #n
229#define S(n) _S(n)
230
231static int read_maps(pm_process_t *proc) {
232 char filename[MAX_FILENAME];
233 char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS];
234 FILE *maps_f;
235 pm_map_t *map, **maps, **new_maps;
236 int maps_count, maps_size;
237 int error;
238
239 if (!proc)
240 return -1;
241
242 maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
243 if (!maps)
244 return errno;
245 maps_count = 0; maps_size = INITIAL_MAPS;
246
247 error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
248 if (error < 0 || error >= MAX_FILENAME)
249 return (error < 0) ? (errno) : (-1);
250
251 maps_f = fopen(filename, "r");
252 if (!maps_f)
253 return errno;
254
255 while (fgets(line, MAX_LINE, maps_f)) {
256 if (maps_count >= maps_size) {
257 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
258 if (!new_maps) {
259 error = errno;
260 free(maps);
261 fclose(maps_f);
262 return error;
263 }
264 maps = new_maps;
265 maps_size *= 2;
266 }
267
268 maps[maps_count] = map = calloc(1, sizeof(*map));
269
270 map->proc = proc;
271
Colin Cross5d461212013-05-15 18:31:05 -0700272 name[0] = '\0';
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800273 sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s",
274 &map->start, &map->end, perms, &map->offset, name);
275
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800276 map->name = malloc(strlen(name) + 1);
277 if (!map->name) {
278 error = errno;
279 for (; maps_count > 0; maps_count--)
280 pm_map_destroy(maps[maps_count]);
281 free(maps);
282 return error;
283 }
284 strcpy(map->name, name);
285 if (perms[0] == 'r') map->flags |= PM_MAP_READ;
286 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
287 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
288
289 maps_count++;
290 }
291
292 fclose(maps_f);
293
294 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
295 if (maps_count && !new_maps) {
296 error = errno;
297 free(maps);
298 return error;
299 }
300
301 proc->maps = new_maps;
302 proc->num_maps = maps_count;
303
304 return 0;
305}