summaryrefslogtreecommitdiff
path: root/libpagemap/pm_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpagemap/pm_process.c')
-rw-r--r--libpagemap/pm_process.c290
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;
+}