Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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> |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 12 | #include <mntent.h> |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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) { |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 76 | int fd, ret = -1; |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 77 | fd = open (fpath, O_RDONLY); |
| 78 | |
| 79 | if (fd == -1) { |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 80 | fprintf(stderr, "Could not open file: %s\n", fpath); |
| 81 | return ret; |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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); |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 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 | } |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 102 | } |
| 103 | munmap(mapped_addr, sb->st_size); |
| 104 | } |
| 105 | |
| 106 | close(fd); |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 107 | return ret; |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 108 | } |
| 109 | |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 110 | static int scan_entry(const char *fpath, const struct stat *sb, int typeflag, |
| 111 | struct FTW * __attribute__((unused))ftwbuf) { |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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 | { |
Martijn Coenen | 31af987 | 2015-12-07 09:51:12 +0100 | [diff] [blame] | 131 | size_t i; |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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 | |
Wei Wang | ce1cef8 | 2016-09-26 16:47:49 -0700 | [diff] [blame] | 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); |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 153 | |
| 154 | // Sort entries |
| 155 | qsort(g_files, g_num_files, sizeof(g_files[0]), &cmpfiles); |
| 156 | |
| 157 | // Dump entries |
Martijn Coenen | 31af987 | 2015-12-07 09:51:12 +0100 | [diff] [blame] | 158 | for (i = 0; i < g_num_files; i++) { |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 159 | struct file_info *info = g_files[i]; |
Martijn Coenen | 31af987 | 2015-12-07 09:51:12 +0100 | [diff] [blame] | 160 | fprintf(stdout, "%s: %zu cached pages (%.2f MB, %zu%% of total file size.)\n", info->name, |
Martijn Coenen | 0a1b018 | 2015-11-30 14:43:10 +0100 | [diff] [blame] | 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 | } |