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