pcu.git  about / heads / tags
page cache utilities for Linux
blob ae127db0694249a801f42cbb1d7891d50a597d6a 2561 bytes (raw)
$ git show HEAD:mincore.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
 
#include "compat-util.h"
static int summary;

static int usage(const char * argv0)
{
	fprintf(stderr,
	        "Usage: %s [-o OFFSET] [-l LENGTH] [-s] FILE...\n", argv0);
	return 1;
}

static void mincore_stats(const char *path, off_t offset, off_t len)
{
	char *map;
	unsigned char *vec;
	size_t vec_len;
	size_t map_len;
	off_t map_offset;
	int fd;
	size_t i;
	static const char *fmt = sizeof(void *) == 8 ?
	                         "%s: %016lx %x\n": "%s: %08lx %x\n";

	if ((fd = open_noatime(path)) < 0) {
		fprintf(stderr, "%s: open(): %s\n", path, strerror(errno));
		return;
	}

	if (!len) {
		struct stat sb;

		if (fstat(fd, &sb) < 0) {
			fprintf(stderr, "%s: fstat(%d): %s\n",
				path, fd, strerror(errno));
			goto err_close;
		}
		len = sb.st_size - offset;
	}

	vec_len = (len + page_size() - 1) / page_size();
	if (!(vec = malloc(vec_len))) {
		fprintf(stderr, "%s: malloc(%lu): %s\n",
		        path, (unsigned long)vec_len, strerror(errno));
		goto err_close;
	}

	map_len = PAGE_ALIGN(len);
	map_offset = PAGE_ALIGN_DOWN(offset + 1);

	map = mmap(NULL, map_len, PROT_READ, MAP_SHARED, fd, map_offset);
	if (map == MAP_FAILED) {
		fprintf(stderr, "%s: mmap(%lu): %s\n",
		        path, (unsigned long)vec_len, strerror(errno));
		goto err_free;
	}

	if (mincore(map, map_len, vec) < 0) {
		fprintf(stderr, "%s: mincore(%lu): %s\n",
		        path, (unsigned long)vec_len, strerror(errno));
		goto err_munmap;
	}

	if (summary) {
		size_t n = 0;

		for (i = 0; i < vec_len; ++i)
			if (vec[i] & 1)
				++n;
		printf("%s: %F\n", path, (double)n / (double)vec_len);
	} else {
		for (i = 0; i < vec_len; ++i)
			printf(fmt, path,
			       (unsigned long)((page_size() * i) + map_offset),
			       vec[i] & 1);
	}
err_munmap:
	munmap(map, map_len);
err_free:
	free(vec);
err_close:
	close(fd);
}

int main(int argc, char * const argv[])
{
	off_t offset = 0;
	off_t len = 0;
	int argi = 1;
	int opt;

	while ((opt = getopt(argc, argv, "o:l:hs")) != -1) {
		char *err;

		++argi;
		switch(opt) {
		case 'o':
			++argi;
			offset = cstr_to_off_t(optarg, &err, 10);
			if (*err || offset < 0) {
				fprintf(stderr, "offset must be >= 0\n");
				return 1;
			}
			break;
		case 'l':
			++argi;
			len = cstr_to_off_t(optarg, &err, 10);
			if (*err || len < 0) {
				fprintf(stderr, "length must be >= 0\n");
				return 1;
			}
			break;
		case 's':
			summary = 1;
			break;
		default:
			return usage(argv[0]);
		}
	}

	if (argi >= argc)
		return usage(argv[0]);

	for (; argi < argc; ++argi)
		mincore_stats(argv[argi], offset, len);
	return 0;
}

git clone https://yhbt.net/pcu.git