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 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 73int 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 99int 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 103int 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 153int 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 175int 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 217int 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 244static 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 free(maps); 263 return (error < 0) ? (errno) : (-1); 264 } 265 266 maps_f = fopen(filename, "r"); 267 if (!maps_f) { 268 free(maps); 269 return errno; 270 } 271 272 while (fgets(line, MAX_LINE, maps_f)) { 273 if (maps_count >= maps_size) { 274 new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*)); 275 if (!new_maps) { 276 error = errno; 277 free(maps); 278 fclose(maps_f); 279 return error; 280 } 281 maps = new_maps; 282 maps_size *= 2; 283 } 284 285 maps[maps_count] = map = calloc(1, sizeof(*map)); 286 287 map->proc = proc; 288 289 name[0] = '\0'; 290 sscanf(line, "%" SCNx64 "-%" SCNx64 " %s %" SCNx64 " %*s %*d %" S(MAX_LINE) "s", 291 &map->start, &map->end, perms, &map->offset, name); 292 293 map->name = malloc(strlen(name) + 1); 294 if (!map->name) { 295 error = errno; 296 for (; maps_count > 0; maps_count--) 297 pm_map_destroy(maps[maps_count]); 298 free(maps); 299 fclose(maps_f); 300 return error; 301 } 302 strcpy(map->name, name); 303 if (perms[0] == 'r') map->flags |= PM_MAP_READ; 304 if (perms[1] == 'w') map->flags |= PM_MAP_WRITE; 305 if (perms[2] == 'x') map->flags |= PM_MAP_EXEC; 306 307 maps_count++; 308 } 309 310 fclose(maps_f); 311 312 new_maps = realloc(maps, maps_count * sizeof(pm_map_t*)); 313 if (maps_count && !new_maps) { 314 error = errno; 315 free(maps); 316 return error; 317 } 318 319 proc->maps = new_maps; 320 proc->num_maps = maps_count; 321 322 return 0; 323} 324