#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 <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; fd = open (fpath, O_RDONLY); if (fd == -1) { printf("Could not open file."); return -1; } 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); int ret = mincore(mapped_addr, sb->st_size, mincore_data); 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 0; } static int scan_entry(const char *fpath, const struct stat *sb, int typeflag, struct FTW *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 nftw("/system/", &scan_entry, MAX_NUM_FD, 0); nftw("/vendor/", &scan_entry, MAX_NUM_FD, 0); nftw("/data/", &scan_entry, MAX_NUM_FD, 0); // 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; }