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