pm_process.c revision d6c0e17d4fc53db1d123134866d8ec610288221a
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 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 72int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out, 73 uint64_t flags_mask, uint64_t required_flags) 74{ 75 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++) { 85 error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask, 86 required_flags); 87 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; 95 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); 100} 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 unsigned long 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 if (low == high) { 114 *range_out = NULL; 115 *len = 0; 116 return 0; 117 } 118 119 firstpage = low / proc->ker->pagesize; 120 numpages = (high - low) / proc->ker->pagesize; 121 122 range = malloc(numpages * sizeof(uint64_t)); 123 if (!range) 124 return errno; 125 126 off = lseek(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET); 127 if (off == (off_t)-1) { 128 error = errno; 129 free(range); 130 return error; 131 } 132 error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t)); 133 if (error == 0) { 134 /* EOF, mapping is not in userspace mapping range (probably vectors) */ 135 *len = 0; 136 free(range); 137 *range_out = NULL; 138 return 0; 139 } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) { 140 error = (error < 0) ? errno : -1; 141 free(range); 142 return error; 143 } 144 145 *range_out = range; 146 *len = numpages; 147 148 return 0; 149} 150 151int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) { 152 pm_map_t **maps; 153 154 if (!proc || !maps_out || !len) 155 return -1; 156 157 if (proc->num_maps) { 158 maps = malloc(proc->num_maps * sizeof(pm_map_t*)); 159 if (!maps) 160 return errno; 161 162 memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*)); 163 164 *maps_out = maps; 165 } else { 166 *maps_out = NULL; 167 } 168 *len = proc->num_maps; 169 170 return 0; 171} 172 173int pm_process_workingset(pm_process_t *proc, 174 pm_memusage_t *ws_out, int reset) { 175 pm_memusage_t ws, map_ws; 176 char filename[MAX_FILENAME]; 177 int fd; 178 int i, j; 179 int error; 180 181 if (!proc) 182 return -1; 183 184 if (ws_out) { 185 pm_memusage_zero(&ws); 186 for (i = 0; i < proc->num_maps; i++) { 187 error = pm_map_workingset(proc->maps[i], &map_ws); 188 if (error) return error; 189 190 pm_memusage_add(&ws, &map_ws); 191 } 192 193 memcpy(ws_out, &ws, sizeof(ws)); 194 } 195 196 if (reset) { 197 error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs", 198 proc->pid); 199 if (error < 0 || error >= MAX_FILENAME) { 200 return (error < 0) ? (errno) : (-1); 201 } 202 203 fd = open(filename, O_WRONLY); 204 if (fd < 0) 205 return errno; 206 207 write(fd, "1\n", strlen("1\n")); 208 209 close(fd); 210 } 211 212 return 0; 213} 214 215int pm_process_destroy(pm_process_t *proc) { 216 int i; 217 218 if (!proc) 219 return -1; 220 221 for (i = 0; i < proc->num_maps; i++) { 222 pm_map_destroy(proc->maps[i]); 223 } 224 free(proc->maps); 225 close(proc->pagemap_fd); 226 free(proc); 227 228 return 0; 229} 230 231#define INITIAL_MAPS 10 232#define MAX_LINE 1024 233#define MAX_PERMS 5 234 235/* 236 * #define FOO 123 237 * S(FOO) => "123" 238 */ 239#define _S(n) #n 240#define S(n) _S(n) 241 242static int read_maps(pm_process_t *proc) { 243 char filename[MAX_FILENAME]; 244 char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS]; 245 FILE *maps_f; 246 pm_map_t *map, **maps, **new_maps; 247 int maps_count, maps_size; 248 int error; 249 250 if (!proc) 251 return -1; 252 253 maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*)); 254 if (!maps) 255 return errno; 256 maps_count = 0; maps_size = INITIAL_MAPS; 257 258 error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid); 259 if (error < 0 || error >= MAX_FILENAME) 260 return (error < 0) ? (errno) : (-1); 261 262 maps_f = fopen(filename, "r"); 263 if (!maps_f) 264 return errno; 265 266 while (fgets(line, MAX_LINE, maps_f)) { 267 if (maps_count >= maps_size) { 268 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*)); 269 if (!new_maps) { 270 error = errno; 271 free(maps); 272 fclose(maps_f); 273 return error; 274 } 275 maps = new_maps; 276 maps_size *= 2; 277 } 278 279 maps[maps_count] = map = calloc(1, sizeof(*map)); 280 281 map->proc = proc; 282 283 name[0] = '\0'; 284 sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s", 285 &map->start, &map->end, perms, &map->offset, name); 286 287 map->name = malloc(strlen(name) + 1); 288 if (!map->name) { 289 error = errno; 290 for (; maps_count > 0; maps_count--) 291 pm_map_destroy(maps[maps_count]); 292 free(maps); 293 return error; 294 } 295 strcpy(map->name, name); 296 if (perms[0] == 'r') map->flags |= PM_MAP_READ; 297 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE; 298 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC; 299 300 maps_count++; 301 } 302 303 fclose(maps_f); 304 305 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*)); 306 if (maps_count && !new_maps) { 307 error = errno; 308 free(maps); 309 return error; 310 } 311 312 proc->maps = new_maps; 313 proc->num_maps = maps_count; 314 315 return 0; 316} 317