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