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 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); 88 89 for (i = 0; i < proc->num_maps; i++) { 90 error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask, 91 required_flags); 92 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; 100 101 } 102 103 int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) { 104 return pm_process_usage_flags(proc, usage_out, 0, 0); 105 } 106 107 int pm_process_pagemap_range(pm_process_t *proc, 108 uint64_t low, uint64_t high, 109 uint64_t **range_out, size_t *len) { 110 uint64_t firstpage; 111 uint64_t numpages; 112 uint64_t *range; 113 off64_t off; 114 int error; 115 116 if (!proc || (low > high) || !range_out || !len) 117 return -1; 118 119 if (low == high) { 120 *range_out = NULL; 121 *len = 0; 122 return 0; 123 } 124 125 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 132 off = lseek64(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET); 133 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)); 139 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; 145 } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) { 146 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 157 int 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 179 int 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); 192 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 197 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 226 int pm_process_destroy(pm_process_t *proc) { 227 int i; 228 229 if (!proc) 230 return -1; 231 232 for (i = 0; i < proc->num_maps; i++) { 233 pm_map_destroy(proc->maps[i]); 234 } 235 free(proc->maps); 236 close(proc->pagemap_fd); 237 free(proc); 238 239 return 0; 240 } 241 242 #define INITIAL_MAPS 10 243 #define MAX_PERMS 5 244 245 static int read_maps(pm_process_t *proc) { 246 char filename[MAX_FILENAME]; 247 char *line = NULL; 248 size_t line_length = 0; 249 char perms[MAX_PERMS]; 250 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); 264 if (error < 0 || error >= MAX_FILENAME) { 265 free(maps); 266 return (error < 0) ? (errno) : (-1); 267 } 268 269 maps_f = fopen(filename, "r"); 270 if (!maps_f) { 271 free(maps); 272 return errno; 273 } 274 275 while (getline(&line, &line_length, maps_f) != -1) { 276 line[strlen(line) - 1] = '\0'; // Lose the newline. 277 278 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); 283 free(line); 284 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 295 int name_offset; 296 sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*s %*d %n", 297 &map->start, &map->end, perms, &map->offset, &name_offset); 298 299 map->name = strdup(line + name_offset); 300 if (!map->name) { 301 error = errno; 302 for (; maps_count > 0; maps_count--) 303 pm_map_destroy(maps[maps_count]); 304 free(maps); 305 free(line); 306 fclose(maps_f); 307 return error; 308 } 309 310 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 317 free(line); 318 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 } 332