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