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(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 
     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 < (int)(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 
    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 
    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 
    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 1024
    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 
    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