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 
22 unsigned long util;
23 unsigned long used_node_blks;
24 unsigned long used_data_blks;
25 //unsigned long inline_inode;
26 
27 unsigned long free_segs;
28 unsigned long valid_segs;
29 unsigned long dirty_segs;
30 unsigned long prefree_segs;
31 
32 unsigned long gc, bg_gc;
33 unsigned long cp;
34 unsigned long gc_data_blks;
35 unsigned long gc_node_blks;
36 
37 //unsigned long extent_hit_ratio;
38 
39 unsigned long dirty_node, node_kb;
40 unsigned long dirty_dents;
41 unsigned long dirty_meta, meta_kb;
42 unsigned long nat_caches;
43 unsigned long dirty_sit;
44 
45 unsigned long free_nids;
46 
47 unsigned long ssr_blks;
48 unsigned long lfs_blks;
49 unsigned long memory_kb;
50 
51 struct options {
52 	int delay;
53 	int interval;
54 	char partname[32];
55 };
56 
57 struct mm_table {
58 	const char *name;
59 	unsigned long *val;
60 	int flag;
61 };
62 
compare_mm_table(const void * a,const void * b)63 static 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 
remove_newline(char ** head)69 static inline void remove_newline(char **head)
70 {
71 again:
72 	if (**head == '\n') {
73 		*head = *head + 1;
74 		goto again;
75 	}
76 }
77 
f2fstat(struct options * opt)78 void 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;
173 nextline:
174 		tail = strchr(head, '\n');
175 		if (!tail)
176 			break;
177 		head =  tail + 1;
178 	}
179 
180 	close(fd);
181 }
182 
usage(void)183 void 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 
parse_option(int argc,char * argv[],struct options * opt)192 void parse_option(int argc, char *argv[], struct options *opt)
193 {
194 	int 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 
__make_head(char * head,int index,int i,int len)215 void __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 
print_head(char * res)229 void 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 
main(int argc,char * argv[])277 int 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