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