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 <inttypes.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 
     25 #include <pagemap/pagemap.h>
     26 
     27 #include "pm_map.h"
     28 
     29 static int read_maps(pm_process_t *proc);
     30 
     31 #define MAX_FILENAME 64
     32 
     33 int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out) {
     34     pm_process_t *proc;
     35     char filename[MAX_FILENAME];
     36     int error;
     37 
     38     if (!ker || !proc_out)
     39         return -1;
     40 
     41     proc = calloc(1, sizeof(*proc));
     42     if (!proc)
     43         return errno;
     44 
     45     proc->ker = ker;
     46     proc->pid = pid;
     47 
     48     error = snprintf(filename, MAX_FILENAME, "/proc/%d/pagemap", pid);
     49     if (error < 0 || error >= MAX_FILENAME) {
     50         error = (error < 0) ? (errno) : (-1);
     51         free(proc);
     52         return error;
     53     }
     54 
     55     proc->pagemap_fd = open(filename, O_RDONLY);
     56     if (proc->pagemap_fd < 0) {
     57         error = errno;
     58         free(proc);
     59         return error;
     60     }
     61 
     62     error = read_maps(proc);
     63     if (error) {
     64         free(proc);
     65         return error;
     66     }
     67 
     68     *proc_out = proc;
     69 
     70     return 0;
     71 }
     72 
     73 int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
     74                         uint64_t flags_mask, uint64_t required_flags)
     75 {
     76     pm_memusage_t usage, map_usage;
     77     int error;
     78     int i;
     79 
     80     if (!proc || !usage_out)
     81         return -1;
     82 
     83     pm_memusage_zero(&usage);
     84     pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
     85 
     86     pm_memusage_zero(&map_usage);
     87     pm_memusage_pswap_init_handle(&map_usage, usage_out->p_swap);
     88 
     89     for (i = 0; i < proc->num_maps; i++) {
     90         error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
     91                                    required_flags);
     92         if (error) return error;
     93 
     94         pm_memusage_add(&usage, &map_usage);
     95     }
     96 
     97     memcpy(usage_out, &usage, sizeof(pm_memusage_t));
     98 
     99     return 0;
    100 
    101 }
    102 
    103 int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
    104     return pm_process_usage_flags(proc, usage_out, 0, 0);
    105 }
    106 
    107 int pm_process_pagemap_range(pm_process_t *proc,
    108                              uint64_t low, uint64_t high,
    109                              uint64_t **range_out, size_t *len) {
    110     uint64_t firstpage;
    111     uint64_t numpages;
    112     uint64_t *range;
    113     off64_t off;
    114     int error;
    115 
    116     if (!proc || (low > high) || !range_out || !len)
    117         return -1;
    118 
    119     if (low == high) {
    120         *range_out = NULL;
    121         *len = 0;
    122         return 0;
    123     }
    124 
    125     firstpage = low / proc->ker->pagesize;
    126     numpages = (high - low) / proc->ker->pagesize;
    127 
    128     range = malloc(numpages * sizeof(uint64_t));
    129     if (!range)
    130         return errno;
    131 
    132     off = lseek64(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
    133     if (off == (off_t)-1) {
    134         error = errno;
    135         free(range);
    136         return error;
    137     }
    138     error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
    139     if (error == 0) {
    140         /* EOF, mapping is not in userspace mapping range (probably vectors) */
    141         *len = 0;
    142         free(range);
    143         *range_out = NULL;
    144         return 0;
    145     } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) {
    146         error = (error < 0) ? errno : -1;
    147         free(range);
    148         return error;
    149     }
    150 
    151     *range_out = range;
    152     *len = numpages;
    153 
    154     return 0;
    155 }
    156 
    157 int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
    158     pm_map_t **maps;
    159 
    160     if (!proc || !maps_out || !len)
    161         return -1;
    162 
    163     if (proc->num_maps) {
    164         maps = malloc(proc->num_maps * sizeof(pm_map_t*));
    165         if (!maps)
    166             return errno;
    167 
    168         memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
    169 
    170         *maps_out = maps;
    171     } else {
    172         *maps_out = NULL;
    173     }
    174     *len = proc->num_maps;
    175 
    176     return 0;
    177 }
    178 
    179 int pm_process_workingset(pm_process_t *proc,
    180                           pm_memusage_t *ws_out, int reset) {
    181     pm_memusage_t ws, map_ws;
    182     char filename[MAX_FILENAME];
    183     int fd;
    184     int i, j;
    185     int error;
    186 
    187     if (!proc)
    188         return -1;
    189 
    190     if (ws_out) {
    191         pm_memusage_zero(&ws);
    192         pm_memusage_pswap_init_handle(&ws, ws_out->p_swap);
    193 
    194         pm_memusage_zero(&map_ws);
    195         pm_memusage_pswap_init_handle(&map_ws, ws_out->p_swap);
    196 
    197         for (i = 0; i < proc->num_maps; i++) {
    198             error = pm_map_workingset(proc->maps[i], &map_ws);
    199             if (error) return error;
    200 
    201             pm_memusage_add(&ws, &map_ws);
    202         }
    203 
    204         memcpy(ws_out, &ws, sizeof(ws));
    205     }
    206 
    207     if (reset) {
    208         error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
    209                          proc->pid);
    210         if (error < 0 || error >= MAX_FILENAME) {
    211             return (error < 0) ? (errno) : (-1);
    212         }
    213 
    214         fd = open(filename, O_WRONLY);
    215         if (fd < 0)
    216             return errno;
    217 
    218         write(fd, "1\n", strlen("1\n"));
    219 
    220         close(fd);
    221     }
    222 
    223     return 0;
    224 }
    225 
    226 int pm_process_destroy(pm_process_t *proc) {
    227     int i;
    228 
    229     if (!proc)
    230         return -1;
    231 
    232     for (i = 0; i < proc->num_maps; i++) {
    233         pm_map_destroy(proc->maps[i]);
    234     }
    235     free(proc->maps);
    236     close(proc->pagemap_fd);
    237     free(proc);
    238 
    239     return 0;
    240 }
    241 
    242 #define INITIAL_MAPS 10
    243 #define MAX_PERMS 5
    244 
    245 static int read_maps(pm_process_t *proc) {
    246     char filename[MAX_FILENAME];
    247     char *line = NULL;
    248     size_t line_length = 0;
    249     char perms[MAX_PERMS];
    250     FILE *maps_f;
    251     pm_map_t *map, **maps, **new_maps;
    252     int maps_count, maps_size;
    253     int error;
    254 
    255     if (!proc)
    256         return -1;
    257 
    258     maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
    259     if (!maps)
    260         return errno;
    261     maps_count = 0; maps_size = INITIAL_MAPS;
    262 
    263     error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
    264     if (error < 0 || error >= MAX_FILENAME) {
    265         free(maps);
    266         return (error < 0) ? (errno) : (-1);
    267     }
    268 
    269     maps_f = fopen(filename, "r");
    270     if (!maps_f) {
    271         free(maps);
    272         return errno;
    273     }
    274 
    275     while (getline(&line, &line_length, maps_f) != -1) {
    276         line[strlen(line) - 1] = '\0'; // Lose the newline.
    277 
    278         if (maps_count >= maps_size) {
    279             new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
    280             if (!new_maps) {
    281                 error = errno;
    282                 free(maps);
    283                 free(line);
    284                 fclose(maps_f);
    285                 return error;
    286             }
    287             maps = new_maps;
    288             maps_size *= 2;
    289         }
    290 
    291         maps[maps_count] = map = calloc(1, sizeof(*map));
    292 
    293         map->proc = proc;
    294 
    295         int name_offset;
    296         sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*s %*d %n",
    297                &map->start, &map->end, perms, &map->offset, &name_offset);
    298 
    299         map->name = strdup(line + name_offset);
    300         if (!map->name) {
    301             error = errno;
    302             for (; maps_count > 0; maps_count--)
    303                 pm_map_destroy(maps[maps_count]);
    304             free(maps);
    305             free(line);
    306             fclose(maps_f);
    307             return error;
    308         }
    309 
    310         if (perms[0] == 'r') map->flags |= PM_MAP_READ;
    311         if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
    312         if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
    313 
    314         maps_count++;
    315     }
    316 
    317     free(line);
    318     fclose(maps_f);
    319 
    320     new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
    321     if (maps_count && !new_maps) {
    322         error = errno;
    323         free(maps);
    324         return error;
    325     }
    326 
    327     proc->maps = new_maps;
    328     proc->num_maps = maps_count;
    329 
    330     return 0;
    331 }
    332