diff options
Diffstat (limited to 'libpagemap/pm_process.c')
-rw-r--r-- | libpagemap/pm_process.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/libpagemap/pm_process.c b/libpagemap/pm_process.c new file mode 100644 index 00000000..1ab367d8 --- /dev/null +++ b/libpagemap/pm_process.c @@ -0,0 +1,290 @@ +/* + * 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(pm_process_t *proc, pm_memusage_t *usage_out) { + 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(proc->maps[i], &map_usage); + if (error) return error; + + pm_memusage_add(&usage, &map_usage); + } + + memcpy(usage_out, &usage, sizeof(pm_memusage_t)); + + return 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 < 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 256 +#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; + + 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; +} |