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