summaryrefslogtreecommitdiff
path: root/pagecache/dumpcache.c
blob: 4503a6e6b0c63067fe71a296505dd44b4c13c222 (plain)
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;
}