1 1.2 rillig /* $NetBSD: fincore.c,v 1.2 2024/11/03 10:43:27 rillig Exp $ */ 2 1.1 yamt 3 1.1 yamt /*- 4 1.1 yamt * Copyright (c) 2011 YAMAMOTO Takashi, 5 1.1 yamt * All rights reserved. 6 1.1 yamt * 7 1.1 yamt * Redistribution and use in source and binary forms, with or without 8 1.1 yamt * modification, are permitted provided that the following conditions 9 1.1 yamt * are met: 10 1.1 yamt * 1. Redistributions of source code must retain the above copyright 11 1.1 yamt * notice, this list of conditions and the following disclaimer. 12 1.1 yamt * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 yamt * notice, this list of conditions and the following disclaimer in the 14 1.1 yamt * documentation and/or other materials provided with the distribution. 15 1.1 yamt * 16 1.1 yamt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 yamt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 yamt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 yamt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 yamt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 yamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 yamt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 yamt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 yamt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 yamt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 yamt * SUCH DAMAGE. 27 1.1 yamt */ 28 1.1 yamt 29 1.1 yamt /* 30 1.1 yamt * a utility to query which file pages are cached 31 1.1 yamt * 32 1.1 yamt * inspired by: 33 1.1 yamt * http://net.doit.wisc.edu/~plonka/fincore/ 34 1.1 yamt * http://www.usenix.org/events/lisa07/tech/plonka.html 35 1.1 yamt */ 36 1.1 yamt 37 1.1 yamt #include <sys/cdefs.h> 38 1.1 yamt #if defined(__NetBSD__) 39 1.1 yamt #ifndef lint 40 1.2 rillig __RCSID("$NetBSD: fincore.c,v 1.2 2024/11/03 10:43:27 rillig Exp $"); 41 1.1 yamt #endif /* not lint */ 42 1.1 yamt #endif /* defined(__NetBSD__) */ 43 1.1 yamt 44 1.1 yamt #include <sys/param.h> 45 1.1 yamt #include <sys/stat.h> 46 1.1 yamt #include <sys/mman.h> 47 1.1 yamt 48 1.1 yamt #include <err.h> 49 1.1 yamt #include <fcntl.h> 50 1.1 yamt #include <stdbool.h> 51 1.1 yamt #include <stdio.h> 52 1.1 yamt #include <stdint.h> 53 1.1 yamt #include <stdlib.h> 54 1.1 yamt #include <string.h> 55 1.1 yamt #include <unistd.h> 56 1.1 yamt 57 1.1 yamt #if !defined(__arraycount) 58 1.1 yamt #define __arraycount(a) (sizeof(a)/sizeof(*a)) 59 1.1 yamt #endif /* !defined(__arraycount) */ 60 1.1 yamt 61 1.1 yamt size_t page_size; 62 1.1 yamt bool do_summary; 63 1.1 yamt bool be_quiet; 64 1.1 yamt 65 1.1 yamt /* 66 1.1 yamt * fincore: query which pages of the file are in-core. 67 1.1 yamt * 68 1.1 yamt * this function is intended to be compatible with: 69 1.1 yamt * http://lwn.net/Articles/371538/ 70 1.1 yamt * http://libprefetch.cs.ucla.edu/ 71 1.1 yamt * 72 1.1 yamt * while this can be implemented in kernel much more efficiently, i'm not 73 1.1 yamt * sure if making this a syscall in 2011 is a good idea. this API does not 74 1.1 yamt * seem scalable for sparsely cached huge files. the expected scalability 75 1.1 yamt * has been changed since the time when mincore was invented. 76 1.1 yamt * 77 1.1 yamt * some references: 78 1.1 yamt * http://wiki.postgresql.org/images/a/a2/Pgfincore_pgday10.pdf 79 1.1 yamt */ 80 1.1 yamt 81 1.1 yamt static int 82 1.1 yamt fincore(int fd, off_t startoff, off_t endoff, unsigned char *vec) 83 1.1 yamt { 84 1.1 yamt off_t off; 85 1.1 yamt size_t chunk_size; 86 1.1 yamt 87 1.1 yamt for (off = startoff; off < endoff; 88 1.1 yamt off += chunk_size, vec += chunk_size / page_size) { 89 1.1 yamt void *vp; 90 1.1 yamt 91 1.1 yamt chunk_size = MIN((off_t)(1024 * page_size), endoff - off); 92 1.1 yamt vp = mmap(NULL, chunk_size, PROT_NONE, MAP_FILE|MAP_SHARED, 93 1.1 yamt fd, off); 94 1.1 yamt if (vp == MAP_FAILED) { 95 1.1 yamt return -1; 96 1.1 yamt } 97 1.1 yamt if (mincore(vp, chunk_size, 98 1.1 yamt #if !defined(__linux__) 99 1.1 yamt (char *) 100 1.1 yamt #endif /* !defined(__linux__) */ 101 1.1 yamt vec)) { 102 1.1 yamt munmap(vp, chunk_size); 103 1.1 yamt return -1; 104 1.1 yamt } 105 1.1 yamt if (munmap(vp, chunk_size)) { 106 1.1 yamt return -1; 107 1.1 yamt } 108 1.1 yamt } 109 1.1 yamt return 0; 110 1.1 yamt } 111 1.1 yamt 112 1.1 yamt static void 113 1.1 yamt do_file(const char *name) 114 1.1 yamt { 115 1.1 yamt unsigned char vec[4096]; 116 1.1 yamt struct stat st; 117 1.1 yamt uintmax_t n; /* number of pages in-core */ 118 1.1 yamt off_t off; 119 1.1 yamt size_t chunk_size; 120 1.1 yamt int fd; 121 1.1 yamt bool header_done = false; 122 1.1 yamt 123 1.1 yamt fd = open(name, O_RDONLY); 124 1.1 yamt if (fd == -1) { 125 1.1 yamt err(EXIT_FAILURE, "open %s", name); 126 1.1 yamt } 127 1.1 yamt if (fstat(fd, &st)) { 128 1.1 yamt err(EXIT_FAILURE, "fstat %s", name); 129 1.1 yamt } 130 1.1 yamt n = 0; 131 1.1 yamt for (off = 0; off < st.st_size; off += chunk_size) { 132 1.1 yamt unsigned int i; 133 1.1 yamt 134 1.1 yamt chunk_size = MIN(__arraycount(vec) * page_size, 135 1.1 yamt roundup(st.st_size - off, page_size)); 136 1.1 yamt if (fincore(fd, off, off + chunk_size, vec)) { 137 1.1 yamt printf("\n"); 138 1.1 yamt err(EXIT_FAILURE, "fincore %s", name); 139 1.1 yamt } 140 1.1 yamt for (i = 0; i < chunk_size / page_size; i++) { 141 1.1 yamt if (vec[i] == 0) { 142 1.1 yamt continue; 143 1.1 yamt } 144 1.1 yamt if (!do_summary) { 145 1.1 yamt if (!header_done) { 146 1.1 yamt printf("%s:", name); 147 1.1 yamt header_done = true; 148 1.1 yamt } 149 1.1 yamt printf(" %ju", 150 1.1 yamt (uintmax_t)(off / page_size + i)); 151 1.1 yamt } 152 1.1 yamt n++; 153 1.1 yamt } 154 1.1 yamt } 155 1.1 yamt close(fd); 156 1.1 yamt if (do_summary && (n != 0 || !be_quiet)) { 157 1.1 yamt const uintmax_t total = howmany(st.st_size, page_size); 158 1.1 yamt const double pct = (total != 0) ? ((double)n / total * 100) : 0; 159 1.1 yamt 160 1.1 yamt if (!header_done) { 161 1.1 yamt printf("%s:", name); 162 1.1 yamt header_done = true; 163 1.1 yamt } 164 1.1 yamt printf(" %ju / %ju in-core pages (%0.2f%%)", n, total, pct); 165 1.1 yamt } 166 1.1 yamt if (header_done) { 167 1.1 yamt printf("\n"); 168 1.1 yamt } else if (!be_quiet) { 169 1.1 yamt printf("%s: \n", name); 170 1.1 yamt } 171 1.1 yamt } 172 1.1 yamt 173 1.1 yamt int 174 1.1 yamt /*ARGSUSED*/ 175 1.1 yamt main(int argc, char *argv[]) 176 1.1 yamt { 177 1.1 yamt long l; 178 1.1 yamt int ch; 179 1.1 yamt 180 1.1 yamt while ((ch = getopt(argc, argv, "sq")) != -1) { 181 1.1 yamt switch (ch) { 182 1.1 yamt case 's': 183 1.1 yamt do_summary = true; 184 1.1 yamt break; 185 1.1 yamt case 'q': 186 1.1 yamt be_quiet = true; 187 1.1 yamt break; 188 1.1 yamt default: 189 1.1 yamt exit(EXIT_FAILURE); 190 1.1 yamt } 191 1.1 yamt } 192 1.1 yamt 193 1.1 yamt l = sysconf(_SC_PAGESIZE); 194 1.1 yamt if (l == -1) { 195 1.1 yamt /* 196 1.1 yamt * sysconf doesn't always set errno. bad API. 197 1.1 yamt */ 198 1.1 yamt errx(EXIT_FAILURE, "_SC_PAGESIZE"); 199 1.1 yamt } 200 1.1 yamt page_size = (size_t)l; 201 1.1 yamt 202 1.1 yamt argc -= optind; 203 1.1 yamt argv += optind; 204 1.1 yamt while (argc > 0) { 205 1.1 yamt do_file(argv[0]); 206 1.1 yamt argc--; 207 1.1 yamt argv++; 208 1.1 yamt } 209 1.1 yamt return EXIT_SUCCESS; 210 1.1 yamt } 211