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