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