Home | History | Annotate | Download | only in tools
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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