f2fstat.c revision 4ff7400c519a8f644b8181756939fcb313345c12
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;
33unsigned long bg_gc;
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		{ "  - meta",		&dirty_meta,		KEY_META },
101		{ "  - nodes",		&dirty_node,		KEY_NODE },
102		{ "GC calls",		&gc,			0 },
103		{ "LFS",		&lfs_blks,		0 },
104		{ "Memory",		&memory_kb,		0 },
105		{ "SSR",		&ssr_blks,		0 },
106		{ "Utilization",	&util,			0 },
107	};
108
109	f2fstat_table_cnt = sizeof(f2fstat_table)/sizeof(struct mm_table);
110
111	fd = open(F2FS_STATUS, O_RDONLY);
112	if (fd < 0) {
113		perror("open " F2FS_STATUS);
114		exit(EXIT_FAILURE);
115	}
116
117	ret = read(fd, buf, 4096);
118	if (ret < 0) {
119		perror("read " F2FS_STATUS);
120		exit(EXIT_FAILURE);
121	}
122	buf[ret] = '\0';
123
124	head = buf;
125
126	if (opt->partname[0] != '\0') {
127		head = strstr(buf, opt->partname);
128		if (head == NULL)
129			exit(EXIT_FAILURE);
130	}
131
132	for (;;) {
133		remove_newline(&head);
134		tail = strchr(head, ':');
135		if (!tail)
136			break;
137		*tail = '\0';
138		if (strlen(head) >= sizeof(keyname)) {
139			dbg("[OVER] %s\n", head);
140			*tail = ':';
141			tail = strchr(head, '\n');
142			head = tail + 1;
143			continue;
144		}
145
146		strcpy(keyname, head);
147
148		found = bsearch(&key, f2fstat_table, f2fstat_table_cnt, sizeof(struct mm_table), compare_mm_table);
149		dbg("[RESULT] %s (%s)\n", head, (found) ? "O" : "X");
150		head = tail + 1;
151		if (!found)
152			goto nextline;
153
154		*(found->val) = strtoul(head, &tail, 10);
155		if (found->flag) {
156			int npages;
157			tail = strstr(head, "in");
158			head = tail + 2;
159			npages = strtoul(head, &tail, 10);
160			switch (found->flag & (KEY_NODE | KEY_META)) {
161			case KEY_NODE:
162				node_kb = npages * 4;
163				break;
164			case KEY_META:
165				meta_kb = npages * 4;
166				break;
167			}
168		}
169		if (++found_cnt == f2fstat_table_cnt)
170			break;
171nextline:
172		tail = strchr(head, '\n');
173		if (!tail)
174			break;
175		head =  tail + 1;
176	}
177
178	close(fd);
179}
180
181void usage(void)
182{
183	printf("Usage: f2fstat [option]\n"
184			"    -d    delay (secs)\n"
185			"    -i    interval of head info\n"
186			"    -p    partition name (e.g. /dev/sda3)\n");
187	exit(EXIT_FAILURE);
188}
189
190void parse_option(int argc, char *argv[], struct options *opt)
191{
192	char option;
193	const char *option_string = "d:i:p:h";
194
195	while ((option = getopt(argc, argv, option_string)) != EOF) {
196		switch (option) {
197		case 'd':
198			opt->delay = atoi(optarg);
199			break;
200		case 'i':
201			opt->interval = atoi(optarg);
202			break;
203		case 'p':
204			strcpy(opt->partname, basename(optarg));
205			break;
206		default:
207			usage();
208			break;
209		}
210	}
211}
212
213void print_head(void)
214{
215	fprintf(stderr, "---utilization--- -----------main area-------- ---balancing async-- -gc- ---alloc--- -----memory-----\n");
216	fprintf(stderr, "util  node   data   free  valid  dirty prefree node  dent meta sit   gc    ssr    lfs  total  node  meta\n");
217}
218
219int main(int argc, char *argv[])
220{
221	char format[] = "%3ld %6ld %6ld %6ld %6ld %6ld %6ld %5ld %5ld %3ld %3ld %5ld %6ld %6ld %6ld %6ld %6ld\n";
222	int head_interval;
223	struct options opt = {
224		.delay = 1,
225		.interval = 20,
226		.partname = { 0, },
227	};
228
229	parse_option(argc, argv, &opt);
230	head_interval = opt.interval;
231
232	print_head();
233	while (1) {
234		if (head_interval-- == 0) {
235			print_head();
236			head_interval = opt.interval;
237		}
238
239		f2fstat(&opt);
240
241		fprintf(stderr, format, util, used_node_blks, used_data_blks,
242			free_segs, valid_segs, dirty_segs, prefree_segs,
243			dirty_node, dirty_dents, dirty_meta, dirty_sit,
244			gc, ssr_blks, lfs_blks, memory_kb, node_kb, meta_kb);
245
246		sleep(opt.delay);
247	}
248
249	return 0;
250}
251