Home | History | Annotate | Download | only in pagecache
      1 #include <ftw.h>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <math.h>
      5 #include <string.h>
      6 #include <errno.h>
      7 #include <unistd.h>
      8 #include <fcntl.h>
      9 
     10 #include <ctype.h>
     11 #include <stddef.h>
     12 #include <mntent.h>
     13 #include <sys/mman.h>
     14 #include <sys/stat.h>
     15 
     16 // Initial size of the array holding struct file_info
     17 #define INITIAL_NUM_FILES 512
     18 
     19 // Max number of file descriptors to use for ntfw
     20 #define MAX_NUM_FD 1
     21 
     22 struct file_info {
     23     char *name;
     24     size_t file_size;
     25     size_t num_cached_pages;
     26 };
     27 
     28 // Size of pages on this system
     29 static int g_page_size;
     30 
     31 // Total number of cached pages found so far
     32 static size_t g_total_cached = 0;
     33 
     34 // Total number of files scanned so far
     35 static size_t g_num_files = 0;
     36 
     37 // Scanned files and their associated cached page counts
     38 static struct file_info **g_files;
     39 
     40 // Current size of files array
     41 size_t g_files_size;
     42 
     43 static struct file_info *get_file_info(const char* fpath, size_t file_size) {
     44     struct file_info *info;
     45     if (g_num_files >= g_files_size) {
     46         g_files = realloc(g_files, 2 * g_files_size * sizeof(struct file_info*));
     47         if (!g_files) {
     48             fprintf(stderr, "Couldn't allocate space for files array: %s\n", strerror(errno));
     49             exit(EXIT_FAILURE);
     50         }
     51         g_files_size = 2 * g_files_size;
     52     }
     53 
     54     info = calloc(1, sizeof(*info));
     55     if (!info) {
     56         fprintf(stderr, "Couldn't allocate space for file struct: %s\n", strerror(errno));
     57         exit(EXIT_FAILURE);
     58     }
     59 
     60     info->name = malloc(strlen(fpath) + 1);
     61     if (!info->name) {
     62         fprintf(stderr, "Couldn't allocate space for file struct: %s\n", strerror(errno));
     63         exit(EXIT_FAILURE);
     64     }
     65     strcpy(info->name, fpath);
     66 
     67     info->num_cached_pages = 0;
     68     info->file_size = file_size;
     69 
     70     g_files[g_num_files++] = info;
     71 
     72     return info;
     73 }
     74 
     75 static int store_num_cached(const char* fpath, const struct stat *sb) {
     76     int fd, ret = -1;
     77     fd = open (fpath, O_RDONLY);
     78 
     79     if (fd == -1) {
     80         fprintf(stderr, "Could not open file: %s\n", fpath);
     81         return ret;
     82     }
     83 
     84     void* mapped_addr = mmap(NULL, sb->st_size, PROT_NONE, MAP_SHARED, fd, 0);
     85 
     86     if (mapped_addr != MAP_FAILED) {
     87         // Calculate bit-vector size
     88         size_t num_file_pages = (sb->st_size + g_page_size - 1) / g_page_size;
     89         unsigned char* mincore_data = calloc(1, num_file_pages);
     90         ret = mincore(mapped_addr, sb->st_size, mincore_data);
     91         if (!ret) {
     92             int num_cached = 0;
     93             unsigned int page = 0;
     94             for (page = 0; page < num_file_pages; page++) {
     95                 if (mincore_data[page]) num_cached++;
     96             }
     97             if (num_cached > 0) {
     98                 struct file_info *info = get_file_info(fpath, sb->st_size);
     99                 info->num_cached_pages += num_cached;
    100                 g_total_cached += num_cached;
    101             }
    102         }
    103         munmap(mapped_addr, sb->st_size);
    104     }
    105 
    106     close(fd);
    107     return ret;
    108 }
    109 
    110 static int scan_entry(const char *fpath, const struct stat *sb, int typeflag,
    111                       struct FTW * __attribute__((unused))ftwbuf) {
    112     if (typeflag == FTW_F) {
    113         store_num_cached(fpath, sb);
    114     }
    115     return 0;
    116 }
    117 
    118 static int cmpsize(size_t a, size_t b) {
    119     if (a < b) return -1;
    120     if (a > b) return 1;
    121     return 0;
    122 }
    123 
    124 static int cmpfiles(const void *a, const void *b) {
    125     return cmpsize((*((struct file_info**)a))->num_cached_pages,
    126             (*((struct file_info**)b))->num_cached_pages);
    127 }
    128 
    129 int main()
    130 {
    131     size_t i;
    132     g_page_size = getpagesize();
    133 
    134     g_files = malloc(INITIAL_NUM_FILES * sizeof(struct file_info*));
    135     g_files_size = INITIAL_NUM_FILES;
    136 
    137     // Walk filesystem trees through procfs except rootfs/devfs/sysfs/procfs
    138     FILE* fp = setmntent("/proc/mounts", "r");
    139     if (fp == NULL) {
    140         fprintf(stderr, "Error opening /proc/mounts\n");
    141         return -errno;
    142     }
    143     struct mntent* mentry;
    144     while ((mentry = getmntent(fp)) != NULL) {
    145         if (strcmp(mentry->mnt_type, "rootfs") != 0 &&
    146             strncmp("/dev", mentry->mnt_dir, strlen("/dev")) != 0 &&
    147             strncmp("/sys", mentry->mnt_dir, strlen("/sys")) != 0 &&
    148             strncmp("/proc", mentry->mnt_dir, strlen("/proc")) != 0) {
    149             nftw(mentry->mnt_dir, &scan_entry, MAX_NUM_FD, FTW_MOUNT | FTW_PHYS | FTW_DEPTH);
    150         }
    151     }
    152     endmntent(fp);
    153 
    154     // Sort entries
    155     qsort(g_files, g_num_files, sizeof(g_files[0]), &cmpfiles);
    156 
    157     // Dump entries
    158     for (i = 0; i < g_num_files; i++) {
    159         struct file_info *info = g_files[i];
    160         fprintf(stdout, "%s: %zu cached pages (%.2f MB, %zu%% of total file size.)\n", info->name,
    161                 info->num_cached_pages,
    162                 (float) (info->num_cached_pages * g_page_size) / 1024 / 1024,
    163                 (100 * info->num_cached_pages * g_page_size) / info->file_size);
    164     }
    165 
    166     fprintf(stdout, "TOTAL CACHED: %zu pages (%f MB)\n", g_total_cached,
    167             (float) (g_total_cached * 4096) / 1024 / 1024);
    168     return 0;
    169 }
    170