/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pagemap/pagemap.h> #include "pm_map.h" static int read_maps(pm_process_t *proc); #define MAX_FILENAME 64 int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out) { pm_process_t *proc; char filename[MAX_FILENAME]; int error; if (!ker || !proc_out) return -1; proc = calloc(1, sizeof(*proc)); if (!proc) return errno; proc->ker = ker; proc->pid = pid; error = snprintf(filename, MAX_FILENAME, "/proc/%d/pagemap", pid); if (error < 0 || error >= MAX_FILENAME) { error = (error < 0) ? (errno) : (-1); free(proc); return error; } proc->pagemap_fd = open(filename, O_RDONLY); if (proc->pagemap_fd < 0) { error = errno; free(proc); return error; } error = read_maps(proc); if (error) { free(proc); return error; } *proc_out = proc; return 0; } int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out, uint64_t flags_mask, uint64_t required_flags) { pm_memusage_t usage, map_usage; int error; int i; if (!proc || !usage_out) return -1; pm_memusage_zero(&usage); for (i = 0; i < proc->num_maps; i++) { error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask, required_flags); if (error) return error; pm_memusage_add(&usage, &map_usage); } memcpy(usage_out, &usage, sizeof(pm_memusage_t)); return 0; } int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) { return pm_process_usage_flags(proc, usage_out, 0, 0); } int pm_process_pagemap_range(pm_process_t *proc, unsigned long low, unsigned long high, uint64_t **range_out, size_t *len) { int firstpage, numpages; uint64_t *range; off_t off; int error; if (!proc || (low >= high) || !range_out || !len) return -1; firstpage = low / proc->ker->pagesize; numpages = (high - low) / proc->ker->pagesize; range = malloc(numpages * sizeof(uint64_t)); if (!range) return errno; off = lseek(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET); if (off == (off_t)-1) { error = errno; free(range); return error; } error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t)); if (error == 0) { /* EOF, mapping is not in userspace mapping range (probably vectors) */ *len = 0; free(range); *range_out = NULL; return 0; } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) { error = (error < 0) ? errno : -1; free(range); return error; } *range_out = range; *len = numpages; return 0; } int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) { pm_map_t **maps; if (!proc || !maps_out || !len) return -1; if (proc->num_maps) { maps = malloc(proc->num_maps * sizeof(pm_map_t*)); if (!maps) return errno; memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*)); *maps_out = maps; } else { *maps_out = NULL; } *len = proc->num_maps; return 0; } int pm_process_workingset(pm_process_t *proc, pm_memusage_t *ws_out, int reset) { pm_memusage_t ws, map_ws; char filename[MAX_FILENAME]; int fd; int i, j; int error; if (!proc) return -1; if (ws_out) { pm_memusage_zero(&ws); for (i = 0; i < proc->num_maps; i++) { error = pm_map_workingset(proc->maps[i], &map_ws); if (error) return error; pm_memusage_add(&ws, &map_ws); } memcpy(ws_out, &ws, sizeof(ws)); } if (reset) { error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs", proc->pid); if (error < 0 || error >= MAX_FILENAME) { return (error < 0) ? (errno) : (-1); } fd = open(filename, O_WRONLY); if (fd < 0) return errno; write(fd, "1\n", strlen("1\n")); close(fd); } return 0; } int pm_process_destroy(pm_process_t *proc) { if (!proc) return -1; free(proc->maps); close(proc->pagemap_fd); free(proc); return 0; } #define INITIAL_MAPS 10 #define MAX_LINE 1024 #define MAX_PERMS 5 /* * #define FOO 123 * S(FOO) => "123" */ #define _S(n) #n #define S(n) _S(n) static int read_maps(pm_process_t *proc) { char filename[MAX_FILENAME]; char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS]; FILE *maps_f; pm_map_t *map, **maps, **new_maps; int maps_count, maps_size; int error; if (!proc) return -1; maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*)); if (!maps) return errno; maps_count = 0; maps_size = INITIAL_MAPS; error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid); if (error < 0 || error >= MAX_FILENAME) return (error < 0) ? (errno) : (-1); maps_f = fopen(filename, "r"); if (!maps_f) return errno; while (fgets(line, MAX_LINE, maps_f)) { if (maps_count >= maps_size) { new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*)); if (!new_maps) { error = errno; free(maps); fclose(maps_f); return error; } maps = new_maps; maps_size *= 2; } maps[maps_count] = map = calloc(1, sizeof(*map)); map->proc = proc; name[0] = '\0'; sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s", &map->start, &map->end, perms, &map->offset, name); map->name = malloc(strlen(name) + 1); if (!map->name) { error = errno; for (; maps_count > 0; maps_count--) pm_map_destroy(maps[maps_count]); free(maps); return error; } strcpy(map->name, name); if (perms[0] == 'r') map->flags |= PM_MAP_READ; if (perms[1] == 'w') map->flags |= PM_MAP_WRITE; if (perms[2] == 'x') map->flags |= PM_MAP_EXEC; maps_count++; } fclose(maps_f); new_maps = realloc(maps, maps_count * sizeof(pm_map_t*)); if (maps_count && !new_maps) { error = errno; free(maps); return error; } proc->maps = new_maps; proc->num_maps = maps_count; return 0; }