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