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 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 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 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 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 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 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 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