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