blob: 3c5c39148b36c9ccfcee999960e56fc21486cbdc [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>
Colin Crosse2998ff2014-06-17 14:37:06 -070019#include <inttypes.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080020#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
29static int read_maps(pm_process_t *proc);
30
31#define MAX_FILENAME 64
32
33int 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
Colin Cross931563e2013-06-21 17:01:06 -070073int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
74 uint64_t flags_mask, uint64_t required_flags)
75{
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080076 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);
Thierry Strudel08ea6e72015-11-20 18:07:15 -080084 pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
85
86 pm_memusage_zero(&map_usage);
87 pm_memusage_pswap_init_handle(&map_usage, usage_out->p_swap);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080088
89 for (i = 0; i < proc->num_maps; i++) {
Colin Cross931563e2013-06-21 17:01:06 -070090 error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
91 required_flags);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080092 if (error) return error;
93
94 pm_memusage_add(&usage, &map_usage);
95 }
96
97 memcpy(usage_out, &usage, sizeof(pm_memusage_t));
98
99 return 0;
Colin Cross931563e2013-06-21 17:01:06 -0700100
101}
102
103int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
104 return pm_process_usage_flags(proc, usage_out, 0, 0);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800105}
106
107int pm_process_pagemap_range(pm_process_t *proc,
Colin Crosse2998ff2014-06-17 14:37:06 -0700108 uint64_t low, uint64_t high,
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800109 uint64_t **range_out, size_t *len) {
Colin Crosse2998ff2014-06-17 14:37:06 -0700110 uint64_t firstpage;
111 uint64_t numpages;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800112 uint64_t *range;
Colin Crosse2998ff2014-06-17 14:37:06 -0700113 off64_t off;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800114 int error;
115
Colin Crossbac60032014-05-14 18:20:38 -0700116 if (!proc || (low > high) || !range_out || !len)
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800117 return -1;
118
Colin Crossbac60032014-05-14 18:20:38 -0700119 if (low == high) {
120 *range_out = NULL;
121 *len = 0;
122 return 0;
123 }
124
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800125 firstpage = low / proc->ker->pagesize;
126 numpages = (high - low) / proc->ker->pagesize;
127
128 range = malloc(numpages * sizeof(uint64_t));
129 if (!range)
130 return errno;
131
Colin Crosse2998ff2014-06-17 14:37:06 -0700132 off = lseek64(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800133 if (off == (off_t)-1) {
134 error = errno;
135 free(range);
136 return error;
137 }
138 error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
Colin Cross66201572011-07-13 13:35:20 -0700139 if (error == 0) {
140 /* EOF, mapping is not in userspace mapping range (probably vectors) */
141 *len = 0;
142 free(range);
143 *range_out = NULL;
144 return 0;
Selim Gurun76e6cb52012-01-20 19:44:54 -0800145 } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800146 error = (error < 0) ? errno : -1;
147 free(range);
148 return error;
149 }
150
151 *range_out = range;
152 *len = numpages;
153
154 return 0;
155}
156
157int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
158 pm_map_t **maps;
159
160 if (!proc || !maps_out || !len)
161 return -1;
162
163 if (proc->num_maps) {
164 maps = malloc(proc->num_maps * sizeof(pm_map_t*));
165 if (!maps)
166 return errno;
167
168 memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
169
170 *maps_out = maps;
171 } else {
172 *maps_out = NULL;
173 }
174 *len = proc->num_maps;
175
176 return 0;
177}
178
179int pm_process_workingset(pm_process_t *proc,
180 pm_memusage_t *ws_out, int reset) {
181 pm_memusage_t ws, map_ws;
182 char filename[MAX_FILENAME];
183 int fd;
184 int i, j;
185 int error;
186
187 if (!proc)
188 return -1;
189
190 if (ws_out) {
191 pm_memusage_zero(&ws);
Thierry Strudel08ea6e72015-11-20 18:07:15 -0800192 pm_memusage_pswap_init_handle(&ws, ws_out->p_swap);
193
194 pm_memusage_zero(&map_ws);
195 pm_memusage_pswap_init_handle(&map_ws, ws_out->p_swap);
196
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800197 for (i = 0; i < proc->num_maps; i++) {
198 error = pm_map_workingset(proc->maps[i], &map_ws);
199 if (error) return error;
200
201 pm_memusage_add(&ws, &map_ws);
202 }
203
204 memcpy(ws_out, &ws, sizeof(ws));
205 }
206
207 if (reset) {
208 error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
209 proc->pid);
210 if (error < 0 || error >= MAX_FILENAME) {
211 return (error < 0) ? (errno) : (-1);
212 }
213
214 fd = open(filename, O_WRONLY);
215 if (fd < 0)
216 return errno;
217
218 write(fd, "1\n", strlen("1\n"));
219
220 close(fd);
221 }
222
223 return 0;
224}
225
226int pm_process_destroy(pm_process_t *proc) {
Carton Hed3fa8e52013-08-28 11:19:34 +0800227 int i;
228
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800229 if (!proc)
230 return -1;
231
Carton Hed3fa8e52013-08-28 11:19:34 +0800232 for (i = 0; i < proc->num_maps; i++) {
233 pm_map_destroy(proc->maps[i]);
234 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800235 free(proc->maps);
236 close(proc->pagemap_fd);
237 free(proc);
238
239 return 0;
240}
241
242#define INITIAL_MAPS 10
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800243#define MAX_PERMS 5
244
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800245static int read_maps(pm_process_t *proc) {
246 char filename[MAX_FILENAME];
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700247 char *line = NULL;
248 size_t line_length = 0;
249 char perms[MAX_PERMS];
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800250 FILE *maps_f;
251 pm_map_t *map, **maps, **new_maps;
252 int maps_count, maps_size;
253 int error;
254
255 if (!proc)
256 return -1;
257
258 maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
259 if (!maps)
260 return errno;
261 maps_count = 0; maps_size = INITIAL_MAPS;
262
263 error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
daegeun.song1cd49842015-04-22 14:14:02 +0900264 if (error < 0 || error >= MAX_FILENAME) {
265 free(maps);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800266 return (error < 0) ? (errno) : (-1);
daegeun.song1cd49842015-04-22 14:14:02 +0900267 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800268
269 maps_f = fopen(filename, "r");
daegeun.song1cd49842015-04-22 14:14:02 +0900270 if (!maps_f) {
271 free(maps);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800272 return errno;
daegeun.song1cd49842015-04-22 14:14:02 +0900273 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800274
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700275 while (getline(&line, &line_length, maps_f) != -1) {
276 line[strlen(line) - 1] = '\0'; // Lose the newline.
277
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800278 if (maps_count >= maps_size) {
279 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
280 if (!new_maps) {
281 error = errno;
282 free(maps);
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700283 free(line);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800284 fclose(maps_f);
285 return error;
286 }
287 maps = new_maps;
288 maps_size *= 2;
289 }
290
291 maps[maps_count] = map = calloc(1, sizeof(*map));
292
293 map->proc = proc;
294
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700295 int name_offset;
296 sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*s %*d %n",
297 &map->start, &map->end, perms, &map->offset, &name_offset);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800298
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700299 map->name = strdup(line + name_offset);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800300 if (!map->name) {
301 error = errno;
302 for (; maps_count > 0; maps_count--)
303 pm_map_destroy(maps[maps_count]);
304 free(maps);
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700305 free(line);
daegeun.song1cd49842015-04-22 14:14:02 +0900306 fclose(maps_f);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800307 return error;
308 }
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700309
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800310 if (perms[0] == 'r') map->flags |= PM_MAP_READ;
311 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
312 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
313
314 maps_count++;
315 }
316
Elliott Hughes72f4a2f2015-09-22 18:28:04 -0700317 free(line);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800318 fclose(maps_f);
319
320 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
321 if (maps_count && !new_maps) {
322 error = errno;
323 free(maps);
324 return error;
325 }
326
327 proc->maps = new_maps;
328 proc->num_maps = maps_count;
329
330 return 0;
331}