You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.9 KiB
169 lines
4.9 KiB
#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; |
|
}
|
|
|