fincore.c revision 1.2 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