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"
36 #include "libsmartcols.h"
38 /* For large files, mmap is called in iterative way.
39 Window is the unit of vma prepared in each mmap
42 Window size depends on page size.
43 e.g. 128MB on x86_64. ( = N_PAGES_IN_WINDOW * 4096 ). */
44 #define N_PAGES_IN_WINDOW ((size_t)(32 * 1024))
48 const char * const name
;
61 static const struct colinfo infos
[] = {
62 [COL_PAGES
] = { "PAGES", 1, SCOLS_FL_RIGHT
, N_("file data resident in memory in pages")},
63 [COL_RES
] = { "RES", 5, SCOLS_FL_RIGHT
, N_("file data resident in memory in bytes")},
64 [COL_SIZE
] = { "SIZE", 5, SCOLS_FL_RIGHT
, N_("size of the file")},
65 [COL_FILE
] = { "FILE", 4, 0, N_("file name")},
68 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
69 static size_t ncolumns
;
71 struct fincore_control
{
72 const size_t pagesize
;
74 struct libscols_table
*tb
; /* output */
76 unsigned int bytes
: 1,
83 static int column_name_to_id(const char *name
, size_t namesz
)
87 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
88 const char *cn
= infos
[i
].name
;
90 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
93 warnx(_("unknown column: %s"), name
);
97 static int get_column_id(int num
)
100 assert((size_t) num
< ncolumns
);
101 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
105 static const struct colinfo
*get_column_info(int num
)
107 return &infos
[ get_column_id(num
) ];
110 static int add_output_data(struct fincore_control
*ctl
,
117 struct libscols_line
*ln
;
122 ln
= scols_table_new_line(ctl
->tb
, NULL
);
124 err(EXIT_FAILURE
, _("failed to allocate output line"));
126 for (i
= 0; i
< ncolumns
; i
++) {
129 switch(get_column_id(i
)) {
131 rc
= scols_line_set_data(ln
, i
, name
);
134 xasprintf(&tmp
, "%jd", (intmax_t) count_incore
);
135 rc
= scols_line_refer_data(ln
, i
, tmp
);
139 uintmax_t res
= (uintmax_t) count_incore
* ctl
->pagesize
;
142 xasprintf(&tmp
, "%ju", res
);
144 tmp
= size_to_human_string(SIZE_SUFFIX_1LETTER
, res
);
145 rc
= scols_line_refer_data(ln
, i
, tmp
);
150 xasprintf(&tmp
, "%jd", (intmax_t) file_size
);
152 tmp
= size_to_human_string(SIZE_SUFFIX_1LETTER
, file_size
);
153 rc
= scols_line_refer_data(ln
, i
, tmp
);
160 err(EXIT_FAILURE
, _("failed to add output data"));
166 static int do_mincore(struct fincore_control
*ctl
,
167 void *window
, const size_t len
,
171 static unsigned char vec
[N_PAGES_IN_WINDOW
];
172 int n
= (len
/ ctl
->pagesize
) + ((len
% ctl
->pagesize
)? 1: 0);
174 if (mincore (window
, len
, vec
) < 0) {
175 warn(_("failed to do mincore: %s"), name
);
191 static int fincore_fd (struct fincore_control
*ctl
,
197 size_t window_size
= N_PAGES_IN_WINDOW
* ctl
->pagesize
;
198 off_t file_offset
, len
;
201 for (file_offset
= 0; file_offset
< file_size
; file_offset
+= len
) {
204 len
= file_size
- file_offset
;
205 if (len
>= (off_t
) window_size
)
208 /* PROT_NONE is enough for Linux, but qemu-user wants PROT_READ */
209 window
= mmap(window
, len
, PROT_READ
, MAP_PRIVATE
, fd
, file_offset
);
210 if (window
== MAP_FAILED
) {
212 warn(_("failed to do mmap: %s"), name
);
216 rc
= do_mincore(ctl
, window
, len
, name
, count_incore
);
220 munmap (window
, len
);
227 * Returns: <0 on error, 0 success, 1 ignore.
229 static int fincore_name(struct fincore_control
*ctl
,
231 unsigned long long *size
,
238 if ((fd
= open (name
, O_RDONLY
)) < 0) {
239 warn(_("failed to open: %s"), name
);
243 if (fstat (fd
, &sb
) < 0) {
244 warn(_("failed to do fstat: %s"), name
);
249 if (S_ISBLK(sb
.st_mode
)) {
250 rc
= blkdev_get_size(fd
, size
);
252 warn(_("failed ioctl to get size: %s"), name
);
253 } else if (S_ISREG(sb
.st_mode
)) {
256 rc
= 1; /* ignore things like symlinks
261 rc
= fincore_fd(ctl
, fd
, name
, *size
, count_incore
);
268 static void __attribute__((__noreturn__
)) usage(void)
273 fputs(USAGE_HEADER
, out
);
274 fprintf(out
, _(" %s [options] file...\n"), program_invocation_short_name
);
276 fputs(USAGE_OPTIONS
, out
);
277 fputs(_(" -J, --json use JSON output format\n"), out
);
278 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out
);
279 fputs(_(" -n, --noheadings don't print headings\n"), out
);
280 fputs(_(" -o, --output <list> output columns\n"), out
);
281 fputs(_(" -r, --raw use raw output format\n"), out
);
283 fputs(USAGE_SEPARATOR
, out
);
284 printf(USAGE_HELP_OPTIONS(23));
286 fprintf(out
, USAGE_COLUMNS
);
288 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
289 fprintf(out
, " %11s %s\n", infos
[i
].name
, _(infos
[i
].help
));
291 printf(USAGE_MAN_TAIL("fincore(1)"));
296 int main(int argc
, char ** argv
)
300 int rc
= EXIT_SUCCESS
;
303 struct fincore_control ctl
= {
304 .pagesize
= getpagesize()
307 static const struct option longopts
[] = {
308 { "bytes", no_argument
, NULL
, 'b' },
309 { "noheadings", no_argument
, NULL
, 'n' },
310 { "output", required_argument
, NULL
, 'o' },
311 { "version", no_argument
, NULL
, 'V' },
312 { "help", no_argument
, NULL
, 'h' },
313 { "json", no_argument
, NULL
, 'J' },
314 { "raw", no_argument
, NULL
, 'r' },
315 { NULL
, 0, NULL
, 0 },
318 setlocale(LC_ALL
, "");
319 bindtextdomain(PACKAGE
, LOCALEDIR
);
321 close_stdout_atexit();
323 while ((c
= getopt_long (argc
, argv
, "bno:JrVh", longopts
, NULL
)) != -1) {
341 print_version(EXIT_SUCCESS
);
345 errtryhelp(EXIT_FAILURE
);
349 if (optind
== argc
) {
350 warnx(_("no file specified"));
351 errtryhelp(EXIT_FAILURE
);
355 columns
[ncolumns
++] = COL_RES
;
356 columns
[ncolumns
++] = COL_PAGES
;
357 columns
[ncolumns
++] = COL_SIZE
;
358 columns
[ncolumns
++] = COL_FILE
;
361 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
362 &ncolumns
, column_name_to_id
) < 0)
366 ctl
.tb
= scols_new_table();
368 err(EXIT_FAILURE
, _("failed to allocate output table"));
370 scols_table_enable_noheadings(ctl
.tb
, ctl
.noheadings
);
371 scols_table_enable_raw(ctl
.tb
, ctl
.raw
);
372 scols_table_enable_json(ctl
.tb
, ctl
.json
);
374 scols_table_set_name(ctl
.tb
, "fincore");
376 for (i
= 0; i
< ncolumns
; i
++) {
377 const struct colinfo
*col
= get_column_info(i
);
378 struct libscols_column
*cl
;
380 cl
= scols_table_new_column(ctl
.tb
, col
->name
, col
->whint
, col
->flags
);
382 err(EXIT_FAILURE
, _("failed to allocate output column"));
385 int id
= get_column_id(i
);
389 scols_column_set_json_type(cl
, SCOLS_JSON_STRING
);
397 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
403 for(; optind
< argc
; optind
++) {
404 char *name
= argv
[optind
];
405 off_t count_incore
= 0;
406 unsigned long long size
= 0;
408 switch (fincore_name(&ctl
, name
, &size
, &count_incore
)) {
410 add_output_data(&ctl
, name
, size
, count_incore
);
420 scols_print_table(ctl
.tb
);
421 scols_unref_table(ctl
.tb
);