1#include <stdio.h>
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <fcntl.h>
6#include <libgen.h>
7
8#ifdef DEBUG
9#define dbg(fmt, args...)	printf(fmt, __VA_ARGS__);
10#else
11#define dbg(fmt, args...)
12#endif
13
14/*
15 * f2fs status
16 */
17#define F2FS_STATUS	"/sys/kernel/debug/f2fs/status"
18
19#define KEY_NODE	0x00000001
20#define KEY_META	0x00000010
21
22unsigned long util;
23unsigned long used_node_blks;
24unsigned long used_data_blks;
25//unsigned long inline_inode;
26
27unsigned long free_segs;
28unsigned long valid_segs;
29unsigned long dirty_segs;
30unsigned long prefree_segs;
31
32unsigned long gc, bg_gc;
33unsigned long cp;
34unsigned long gc_data_blks;
35unsigned long gc_node_blks;
36
37//unsigned long extent_hit_ratio;
38
39unsigned long dirty_node, node_kb;
40unsigned long dirty_dents;
41unsigned long dirty_meta, meta_kb;
42unsigned long nat_caches;
43unsigned long dirty_sit;
44
45unsigned long free_nids;
46
47unsigned long ssr_blks;
48unsigned long lfs_blks;
49unsigned long memory_kb;
50
51struct options {
52	int delay;
53	int interval;
54	char partname[32];
55};
56
57struct mm_table {
58	const char *name;
59	unsigned long *val;
60	int flag;
61};
62
63static int compare_mm_table(const void *a, const void *b)
64{
65	dbg("[COMPARE] %s, %s\n", ((struct mm_table *)a)->name, ((struct mm_table *)b)->name);
66	return strcmp(((struct mm_table *)a)->name, ((struct mm_table *)b)->name);
67}
68
69static inline void remove_newline(char **head)
70{
71again:
72	if (**head == '\n') {
73		*head = *head + 1;
74		goto again;
75	}
76}
77
78void f2fstat(struct options *opt)
79{
80	int fd;
81	int ret;
82	char keyname[32];
83	char buf[4096];
84	struct mm_table key = { keyname, NULL };
85	struct mm_table *found;
86	int f2fstat_table_cnt;
87	char *head, *tail;
88	int found_cnt = 0;
89
90	static struct mm_table f2fstat_table[] = {
91		{ "  - Data",		&used_data_blks,	0 },
92		{ "  - Dirty",		&dirty_segs,		0 },
93		{ "  - Free",		&free_segs,		0 },
94		{ "  - NATs",		&nat_caches,		0 },
95		{ "  - Node",		&used_node_blks,	0 },
96		{ "  - Prefree",	&prefree_segs,		0 },
97		{ "  - SITs",		&dirty_sit,		0 },
98		{ "  - Valid",		&valid_segs,		0 },
99		{ "  - dents",		&dirty_dents,		0 },
100		{ "  - free_nids",	&free_nids,		0 },
101		{ "  - meta",		&dirty_meta,		KEY_META },
102		{ "  - nodes",		&dirty_node,		KEY_NODE },
103		{ "CP calls",		&cp,			0 },
104		{ "GC calls",		&gc,			0 },
105		{ "LFS",		&lfs_blks,		0 },
106		{ "Memory",		&memory_kb,		0 },
107		{ "SSR",		&ssr_blks,		0 },
108		{ "Utilization",	&util,			0 },
109	};
110
111	f2fstat_table_cnt = sizeof(f2fstat_table)/sizeof(struct mm_table);
112
113	fd = open(F2FS_STATUS, O_RDONLY);
114	if (fd < 0) {
115		perror("open " F2FS_STATUS);
116		exit(EXIT_FAILURE);
117	}
118
119	ret = read(fd, buf, 4096);
120	if (ret < 0) {
121		perror("read " F2FS_STATUS);
122		exit(EXIT_FAILURE);
123	}
124	buf[ret] = '\0';
125
126	head = buf;
127
128	if (opt->partname[0] != '\0') {
129		head = strstr(buf, opt->partname);
130		if (head == NULL)
131			exit(EXIT_FAILURE);
132	}
133
134	for (;;) {
135		remove_newline(&head);
136		tail = strchr(head, ':');
137		if (!tail)
138			break;
139		*tail = '\0';
140		if (strlen(head) >= sizeof(keyname)) {
141			dbg("[OVER] %s\n", head);
142			*tail = ':';
143			tail = strchr(head, '\n');
144			head = tail + 1;
145			continue;
146		}
147
148		strcpy(keyname, head);
149
150		found = bsearch(&key, f2fstat_table, f2fstat_table_cnt, sizeof(struct mm_table), compare_mm_table);
151		dbg("[RESULT] %s (%s)\n", head, (found) ? "O" : "X");
152		head = tail + 1;
153		if (!found)
154			goto nextline;
155
156		*(found->val) = strtoul(head, &tail, 10);
157		if (found->flag) {
158			int npages;
159			tail = strstr(head, "in");
160			head = tail + 2;
161			npages = strtoul(head, &tail, 10);
162			switch (found->flag & (KEY_NODE | KEY_META)) {
163			case KEY_NODE:
164				node_kb = npages * 4;
165				break;
166			case KEY_META:
167				meta_kb = npages * 4;
168				break;
169			}
170		}
171		if (++found_cnt == f2fstat_table_cnt)
172			break;
173nextline:
174		tail = strchr(head, '\n');
175		if (!tail)
176			break;
177		head =  tail + 1;
178	}
179
180	close(fd);
181}
182
183void usage(void)
184{
185	printf("Usage: f2fstat [option]\n"
186			"    -d    delay (secs)\n"
187			"    -i    interval of head info\n"
188			"    -p    partition name (e.g. /dev/sda3)\n");
189	exit(EXIT_FAILURE);
190}
191
192void parse_option(int argc, char *argv[], struct options *opt)
193{
194	char option;
195	const char *option_string = "d:i:p:h";
196
197	while ((option = getopt(argc, argv, option_string)) != EOF) {
198		switch (option) {
199		case 'd':
200			opt->delay = atoi(optarg);
201			break;
202		case 'i':
203			opt->interval = atoi(optarg);
204			break;
205		case 'p':
206			strcpy(opt->partname, basename(optarg));
207			break;
208		default:
209			usage();
210			break;
211		}
212	}
213}
214
215void __make_head(char *head, int index, int i, int len)
216{
217	char name_h[5][20] = {"main segments", "page/slab caches", "cp/gc", "blks", "memory"};
218	int half = (len - strlen(name_h[i])) / 2;
219
220	*(head + index) = '|';
221	index++;
222	memset(head + index, '-', half);
223	index += half;
224	strcpy(head + index, name_h[i]);
225	index += strlen(name_h[i]);
226	memset(head + index, '-', half);
227}
228
229void print_head(char *res)
230{
231	char *ptr, *ptr_buf;
232	char buf[1024], head[1024];
233	char name[20][10] = {"util", "node", "data", "free", "valid", "dirty", "prefree", "node", "dent", "meta",
234		"sit", "nat", "fnid", "cp", "gc", "ssr", "lfs", "total", "node", "meta"};
235	int i, len, prev_index = 0;
236
237	ptr_buf = buf;
238	memset(buf, ' ', 1024);
239	memset(head, ' ', 1024);
240
241	for (i = 0; i < 20; i++) {
242		ptr = (i == 0) ? strtok(res, " ") : strtok(NULL, " ");
243		strncpy(ptr_buf, name[i], strlen(name[i]));
244		if (i == 1) {
245			prev_index = ptr_buf - buf - 1;
246		} else if (i == 7) {
247			len = (ptr_buf - buf) - 1 - prev_index;
248			__make_head(head, prev_index, 0, len);
249			prev_index = ptr_buf - buf - 1;
250		} else if (i == 13) {
251			len = (ptr_buf - buf) - 1 - prev_index;
252			__make_head(head, prev_index, 1, len);
253			prev_index = ptr_buf - buf - 1;
254		} else if (i == 15) {
255			len = (ptr_buf - buf) - 1 - prev_index;
256			__make_head(head, prev_index, 2, len);
257			prev_index = ptr_buf - buf - 1;
258		} else if (i == 17) {
259			len = (ptr_buf - buf) - 1 - prev_index;
260			__make_head(head, prev_index, 3, len);
261			prev_index = ptr_buf - buf - 1;
262		}
263
264		len = strlen(ptr);
265		ptr_buf += (len > strlen(name[i]) ? len : strlen(name[i])) + 1;
266	}
267
268	len = (ptr_buf - buf) - 1 - prev_index;
269	__make_head(head, prev_index, 4, len);
270
271	*ptr_buf = 0;
272	*(head + (ptr_buf - buf - 1)) = '|';
273	*(head + (ptr_buf - buf)) = 0;
274	fprintf(stderr, "%s\n%s\n", head, buf);
275}
276
277int main(int argc, char *argv[])
278{
279	char format[] = "%4ld %4ld %4ld %4ld %5ld %5ld %7ld %4ld %4ld %4ld %3ld %3ld %4ld %2ld %2ld %3ld %3ld %5ld %4ld %4ld";
280	char buf[1024], tmp[1024];
281	int head_interval;
282	struct options opt = {
283		.delay = 1,
284		.interval = 20,
285		.partname = { 0, },
286	};
287
288	parse_option(argc, argv, &opt);
289	head_interval = opt.interval;
290
291	while (1) {
292		memset(buf, 0, 1024);
293		f2fstat(&opt);
294		sprintf(buf, format, util, used_node_blks, used_data_blks,
295			free_segs, valid_segs, dirty_segs, prefree_segs,
296			dirty_node, dirty_dents, dirty_meta, dirty_sit, nat_caches, free_nids,
297			cp, gc, ssr_blks, lfs_blks, memory_kb, node_kb, meta_kb);
298
299		strcpy(tmp, buf);
300		if (head_interval == opt.interval)
301			print_head(tmp);
302		if (head_interval-- == 0)
303			head_interval = opt.interval;
304
305		fprintf(stderr, "%s\n", buf);
306
307		sleep(opt.delay);
308	}
309
310	return 0;
311}
312