2 * fincore - count pages of file contents in core
4 * Copyright (C) 2017 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include "closestream.h"
35 #include "libsmartcols.h"
37 /* For large files, mmap is called in iterative way.
38 Window is the unit of vma prepared in each mmap
41 Window size depends on page size.
42 e.g. 128MB on x86_64. ( = N_PAGES_IN_WINDOW * 4096 ). */
43 #define N_PAGES_IN_WINDOW ((size_t)(32 * 1024))
60 static struct colinfo infos
[] = {
61 [COL_PAGES
] = { "PAGES", 1, SCOLS_FL_RIGHT
, N_("file data resident in memory in pages")},
62 [COL_RES
] = { "RES", 5, SCOLS_FL_RIGHT
, N_("file data resident in memory in bytes")},
63 [COL_SIZE
] = { "SIZE", 5, SCOLS_FL_RIGHT
, N_("size of the file")},
64 [COL_FILE
] = { "FILE", 4, 0, N_("file name")},
67 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
68 static size_t ncolumns
;
70 struct fincore_control
{
71 const size_t pagesize
;
73 struct libscols_table
*tb
; /* output */
75 unsigned int bytes
: 1,
82 static int column_name_to_id(const char *name
, size_t namesz
)
86 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
87 const char *cn
= infos
[i
].name
;
89 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
92 warnx(_("unknown column: %s"), name
);
96 static int get_column_id(int num
)
99 assert((size_t) num
< ncolumns
);
100 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
104 static const struct colinfo
*get_column_info(int num
)
106 return &infos
[ get_column_id(num
) ];
109 static int add_output_data(struct fincore_control
*ctl
,
116 struct libscols_line
*ln
;
121 ln
= scols_table_new_line(ctl
->tb
, NULL
);
123 err(EXIT_FAILURE
, _("failed to allocate output line"));
125 for (i
= 0; i
< ncolumns
; i
++) {
128 switch(get_column_id(i
)) {
130 rc
= scols_line_set_data(ln
, i
, name
);
133 xasprintf(&tmp
, "%jd", (intmax_t) count_incore
);
134 rc
= scols_line_refer_data(ln
, i
, tmp
);
138 uintmax_t res
= (uintmax_t) count_incore
* ctl
->pagesize
;
141 xasprintf(&tmp
, "%ju", res
);
143 tmp
= size_to_human_string(SIZE_SUFFIX_1LETTER
, res
);
144 rc
= scols_line_refer_data(ln
, i
, tmp
);
149 xasprintf(&tmp
, "%jd", (intmax_t) file_size
);
151 tmp
= size_to_human_string(SIZE_SUFFIX_1LETTER
, file_size
);
152 rc
= scols_line_refer_data(ln
, i
, tmp
);
159 err(EXIT_FAILURE
, _("failed to add output data"));
165 static int do_mincore(struct fincore_control
*ctl
,
166 void *window
, const size_t len
,
170 static unsigned char vec
[N_PAGES_IN_WINDOW
];
171 int n
= (len
/ ctl
->pagesize
) + ((len
% ctl
->pagesize
)? 1: 0);
173 if (mincore (window
, len
, vec
) < 0) {
174 warn(_("failed to do mincore: %s"), name
);
190 static int fincore_fd (struct fincore_control
*ctl
,
196 size_t window_size
= N_PAGES_IN_WINDOW
* ctl
->pagesize
;
197 off_t file_offset
, len
;
200 for (file_offset
= 0; file_offset
< file_size
; file_offset
+= len
) {
203 len
= file_size
- file_offset
;
204 if (len
>= (off_t
) window_size
)
207 /* PROT_NONE is enough for Linux, but qemu-user wants PROT_READ */
208 window
= mmap(window
, len
, PROT_READ
, MAP_PRIVATE
, fd
, file_offset
);
209 if (window
== MAP_FAILED
) {
211 warn(_("failed to do mmap: %s"), name
);
215 rc
= do_mincore(ctl
, window
, len
, name
, count_incore
);
219 munmap (window
, len
);
226 * Returns: <0 on error, 0 success, 1 ignore.
228 static int fincore_name(struct fincore_control
*ctl
,
236 if ((fd
= open (name
, O_RDONLY
)) < 0) {
237 warn(_("failed to open: %s"), name
);
241 if (fstat (fd
, sb
) < 0) {
242 warn(_("failed to do fstat: %s"), name
);
247 if (S_ISDIR(sb
->st_mode
))
250 else if (sb
->st_size
)
251 rc
= fincore_fd(ctl
, fd
, name
, sb
->st_size
, count_incore
);
257 static void __attribute__((__noreturn__
)) usage(void)
262 fputs(USAGE_HEADER
, out
);
263 fprintf(out
, _(" %s [options] file...\n"), program_invocation_short_name
);
265 fputs(USAGE_OPTIONS
, out
);
266 fputs(_(" -J, --json use JSON output format\n"), out
);
267 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out
);
268 fputs(_(" -n, --noheadings don't print headings\n"), out
);
269 fputs(_(" -o, --output <list> output columns\n"), out
);
270 fputs(_(" -r, --raw use raw output format\n"), out
);
272 fputs(USAGE_SEPARATOR
, out
);
273 printf(USAGE_HELP_OPTIONS(23));
275 fprintf(out
, USAGE_COLUMNS
);
277 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
278 fprintf(out
, " %11s %s\n", infos
[i
].name
, _(infos
[i
].help
));
280 printf(USAGE_MAN_TAIL("fincore(1)"));
285 int main(int argc
, char ** argv
)
289 int rc
= EXIT_SUCCESS
;
292 struct fincore_control ctl
= {
293 .pagesize
= getpagesize()
296 static const struct option longopts
[] = {
297 { "bytes", no_argument
, NULL
, 'b' },
298 { "noheadings", no_argument
, NULL
, 'n' },
299 { "output", required_argument
, NULL
, 'o' },
300 { "version", no_argument
, NULL
, 'V' },
301 { "help", no_argument
, NULL
, 'h' },
302 { "json", no_argument
, NULL
, 'J' },
303 { "raw", no_argument
, NULL
, 'r' },
304 { NULL
, 0, NULL
, 0 },
307 setlocale(LC_ALL
, "");
308 bindtextdomain(PACKAGE
, LOCALEDIR
);
310 close_stdout_atexit();
312 while ((c
= getopt_long (argc
, argv
, "bno:JrVh", longopts
, NULL
)) != -1) {
330 print_version(EXIT_SUCCESS
);
334 errtryhelp(EXIT_FAILURE
);
338 if (optind
== argc
) {
339 warnx(_("no file specified"));
340 errtryhelp(EXIT_FAILURE
);
344 columns
[ncolumns
++] = COL_RES
;
345 columns
[ncolumns
++] = COL_PAGES
;
346 columns
[ncolumns
++] = COL_SIZE
;
347 columns
[ncolumns
++] = COL_FILE
;
350 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
351 &ncolumns
, column_name_to_id
) < 0)
355 ctl
.tb
= scols_new_table();
357 err(EXIT_FAILURE
, _("failed to allocate output table"));
359 scols_table_enable_noheadings(ctl
.tb
, ctl
.noheadings
);
360 scols_table_enable_raw(ctl
.tb
, ctl
.raw
);
361 scols_table_enable_json(ctl
.tb
, ctl
.json
);
363 scols_table_set_name(ctl
.tb
, "fincore");
365 for (i
= 0; i
< ncolumns
; i
++) {
366 const struct colinfo
*col
= get_column_info(i
);
367 struct libscols_column
*cl
;
369 cl
= scols_table_new_column(ctl
.tb
, col
->name
, col
->whint
, col
->flags
);
371 err(EXIT_FAILURE
, _("failed to allocate output column"));
374 int id
= get_column_id(i
);
378 scols_column_set_json_type(cl
, SCOLS_JSON_STRING
);
386 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
392 for(; optind
< argc
; optind
++) {
393 char *name
= argv
[optind
];
395 off_t count_incore
= 0;
397 switch (fincore_name(&ctl
, name
, &sb
, &count_incore
)) {
399 add_output_data(&ctl
, name
, sb
.st_size
, count_incore
);
409 scols_print_table(ctl
.tb
);
410 scols_unref_table(ctl
.tb
);