pm_process.c revision 5923f33ed7cf186ceac3541987efcc32beb29ab9
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
28static int read_maps(pm_process_t *proc);
29
30#define MAX_FILENAME 64
31
32int 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
72int 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
94int 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 < numpages * sizeof(uint64_t)) {
120        error = (error < 0) ? errno : -1;
121        free(range);
122        return error;
123    }
124
125    *range_out = range;
126    *len = numpages;
127
128    return 0;
129}
130
131int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
132    pm_map_t **maps;
133
134    if (!proc || !maps_out || !len)
135        return -1;
136
137    if (proc->num_maps) {
138        maps = malloc(proc->num_maps * sizeof(pm_map_t*));
139        if (!maps)
140            return errno;
141
142        memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
143
144        *maps_out = maps;
145    } else {
146        *maps_out = NULL;
147    }
148    *len = proc->num_maps;
149
150    return 0;
151}
152
153int pm_process_workingset(pm_process_t *proc,
154                          pm_memusage_t *ws_out, int reset) {
155    pm_memusage_t ws, map_ws;
156    char filename[MAX_FILENAME];
157    int fd;
158    int i, j;
159    int error;
160
161    if (!proc)
162        return -1;
163
164    if (ws_out) {
165        pm_memusage_zero(&ws);
166        for (i = 0; i < proc->num_maps; i++) {
167            error = pm_map_workingset(proc->maps[i], &map_ws);
168            if (error) return error;
169
170            pm_memusage_add(&ws, &map_ws);
171        }
172
173        memcpy(ws_out, &ws, sizeof(ws));
174    }
175
176    if (reset) {
177        error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
178                         proc->pid);
179        if (error < 0 || error >= MAX_FILENAME) {
180            return (error < 0) ? (errno) : (-1);
181        }
182
183        fd = open(filename, O_WRONLY);
184        if (fd < 0)
185            return errno;
186
187        write(fd, "1\n", strlen("1\n"));
188
189        close(fd);
190    }
191
192    return 0;
193}
194
195int pm_process_destroy(pm_process_t *proc) {
196    if (!proc)
197        return -1;
198
199    free(proc->maps);
200    close(proc->pagemap_fd);
201    free(proc);
202
203    return 0;
204}
205
206#define INITIAL_MAPS 10
207#define MAX_LINE 256
208#define MAX_PERMS 5
209
210/*
211 * #define FOO 123
212 * S(FOO) => "123"
213 */
214#define _S(n) #n
215#define S(n) _S(n)
216
217static int read_maps(pm_process_t *proc) {
218    char filename[MAX_FILENAME];
219    char line[MAX_LINE], name[MAX_LINE], perms[MAX_PERMS];
220    FILE *maps_f;
221    pm_map_t *map, **maps, **new_maps;
222    int maps_count, maps_size;
223    int error;
224
225    if (!proc)
226        return -1;
227
228    maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
229    if (!maps)
230        return errno;
231    maps_count = 0; maps_size = INITIAL_MAPS;
232
233    error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
234    if (error < 0 || error >= MAX_FILENAME)
235        return (error < 0) ? (errno) : (-1);
236
237    maps_f = fopen(filename, "r");
238    if (!maps_f)
239        return errno;
240
241    while (fgets(line, MAX_LINE, maps_f)) {
242        if (maps_count >= maps_size) {
243            new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
244            if (!new_maps) {
245                error = errno;
246                free(maps);
247                fclose(maps_f);
248                return error;
249            }
250            maps = new_maps;
251            maps_size *= 2;
252        }
253
254        maps[maps_count] = map = calloc(1, sizeof(*map));
255
256        map->proc = proc;
257
258        sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s",
259               &map->start, &map->end, perms, &map->offset, name);
260
261        if (!strcmp(name, "[vectors]"))
262            continue;
263
264        map->name = malloc(strlen(name) + 1);
265        if (!map->name) {
266            error = errno;
267            for (; maps_count > 0; maps_count--)
268                pm_map_destroy(maps[maps_count]);
269            free(maps);
270            return error;
271        }
272        strcpy(map->name, name);
273        if (perms[0] == 'r') map->flags |= PM_MAP_READ;
274        if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
275        if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
276
277        maps_count++;
278    }
279
280    fclose(maps_f);
281
282    new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
283    if (maps_count && !new_maps) {
284        error = errno;
285        free(maps);
286        return error;
287    }
288
289    proc->maps = new_maps;
290    proc->num_maps = maps_count;
291
292    return 0;
293}
294