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