aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk@motorola.com>2015-03-24 02:57:50 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-03-24 02:57:50 +0000
commit988eb658633d0fe1cadbb71d96343300cd462453 (patch)
tree41b976a63183621236239af2384d1f8ba1315c6a
parent0e01ed60eb8f845db5728d2c24d87be5f5f0c3ca (diff)
parent744352418daf27a99d73649d8d22b315ceafcd34 (diff)
downloadf2fs-tools-988eb658633d0fe1cadbb71d96343300cd462453.tar.gz
am 74435241: parse.f2fs: add a tool to parse IO traces made by runtime f2fs
* commit '744352418daf27a99d73649d8d22b315ceafcd34': parse.f2fs: add a tool to parse IO traces made by runtime f2fs
-rw-r--r--tools/Makefile.am3
-rw-r--r--tools/f2fs_io_parse.c322
2 files changed, 324 insertions, 1 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 1ead174..69a0bb1 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,6 +2,7 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
-sbin_PROGRAMS = f2fstat fibmap.f2fs
+sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs
f2fstat_SOURCES = f2fstat.c
fibmap_f2fs_SOURCES = fibmap.c
+parse_f2fs_SOURCES = f2fs_io_parse.c
diff --git a/tools/f2fs_io_parse.c b/tools/f2fs_io_parse.c
new file mode 100644
index 0000000..7f97270
--- /dev/null
+++ b/tools/f2fs_io_parse.c
@@ -0,0 +1,322 @@
+/*
+ * f2fs IO tracer
+ *
+ * Copyright (c) 2014 Motorola Mobility
+ * Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define _LARGEFILE64_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <locale.h>
+
+#define P_NAMELEN 16
+
+/* For global trace methods */
+enum show_type {
+ SHOW_PID,
+ SHOW_FTYPE,
+ SHOW_ALL,
+};
+
+enum trace_types {
+ TP_PID,
+ TP_IOS,
+ TP_MAX,
+};
+
+struct tps {
+ enum trace_types type;
+ const char *name;
+};
+
+struct tps trace_points[] = {
+ { TP_PID, "f2fs_trace_pid" },
+ { TP_IOS, "f2fs_trace_ios" },
+};
+
+/* For f2fs_trace_pid and f2fs_trace_ios */
+enum rw_type {
+ READ,
+ WRITE,
+ MAX_RW,
+};
+
+enum file_type {
+ __NORMAL_FILE,
+ __DIR_FILE,
+ __NODE_FILE,
+ __META_FILE,
+ __ATOMIC_FILE,
+ __VOLATILE_FILE,
+ __MISC_FILE,
+ __NR_FILES,
+};
+
+char *file_type_string[] = {
+ "User ",
+ "Dir ",
+ "Node ",
+ "Meta ",
+ "Atomic ",
+ "Voltile ",
+ "Misc ",
+};
+
+struct pid_ent {
+ int pid;
+ char name[P_NAMELEN];
+ unsigned long long io[__NR_FILES][MAX_RW];
+ unsigned long long total_io[MAX_RW];
+ LIST_ENTRY(pid_ent) ptr;
+};
+
+/* global variables */
+int major = 0, minor = 0;
+int show_option = SHOW_ALL;
+unsigned long long total_io[__NR_FILES][MAX_RW];
+
+LIST_HEAD(plist, pid_ent) pid_info;
+
+/* Functions */
+static inline int atoh(char *str)
+{
+ int val;
+ sscanf(str, "%x", &val);
+ return val;
+}
+
+static void do_init()
+{
+ struct pid_ent *misc;
+
+ misc = calloc(1, sizeof(struct pid_ent));
+ assert(misc);
+
+ LIST_INIT(&pid_info);
+ LIST_INSERT_HEAD(&pid_info, misc, ptr);
+}
+
+void show_usage()
+{
+ printf("\nUsage: parse.f2fs [options] log_file\n");
+ printf("[options]:\n");
+ printf(" -a RW sorted by pid & file types\n");
+ printf(" -f RW sorted by file types\n");
+ printf(" -p RW sorted by pid\n");
+ printf(" -m major number\n");
+ printf(" -n minor number\n");
+ exit(1);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ const char *option_string = "fm:n:p";
+ int option = 0;
+
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ switch (option) {
+ case 'f':
+ show_option = SHOW_FTYPE;
+ break;
+ case 'm':
+ major = atoh(optarg);
+ break;
+ case 'n':
+ minor = atoh(optarg);
+ break;
+ case 'p':
+ show_option = SHOW_PID;
+ break;
+ default:
+ printf("\tError: Unknown option %c\n", option);
+ show_usage();
+ break;
+ }
+ }
+ if ((optind + 1) != argc) {
+ printf("\tError: Log file is not specified.\n");
+ show_usage();
+ }
+ return optind;
+}
+
+struct pid_ent *get_pid_entry(int pid)
+{
+ struct pid_ent *entry;
+
+ LIST_FOREACH(entry, &pid_info, ptr) {
+ if (entry->pid == pid)
+ return entry;
+ }
+ return LIST_FIRST(&pid_info);
+}
+
+static void handle_tp_pid(char *ptr)
+{
+ struct pid_ent *pent;
+
+ pent = calloc(1, sizeof(struct pid_ent));
+ assert(pent);
+
+ ptr = strtok(NULL, " ");
+ pent->pid = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ strcpy(pent->name, ptr);
+
+ LIST_INSERT_HEAD(&pid_info, pent, ptr);
+}
+
+static void handle_tp_ios(char *ptr)
+{
+ int pid, type, rw, len;
+ struct pid_ent *p;
+
+ ptr = strtok(NULL, " ");
+ pid = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ ptr = strtok(NULL, " ");
+ type = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ rw = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ /* unsigned long long blkaddr = atoh(ptr); */
+
+ ptr = strtok(NULL, " ");
+ len = atoh(ptr);
+
+ /* update per-pid stat */
+ p = get_pid_entry(pid);
+ p->io[type][rw & 0x1] += len;
+ p->total_io[rw & 0x1] += len;
+
+ /* update total stat */
+ total_io[type][rw & 0x1] += len;
+}
+
+static void do_parse(FILE *file)
+{
+ char line[300];
+ char *ptr;
+ int i;
+
+ while (fgets(line, sizeof(line), file) != NULL) {
+ ptr = strtok(line, ":");
+
+ ptr = strtok(NULL, " :");
+
+ for (i = 0; i < TP_MAX; i++) {
+ if (!strcmp(ptr, trace_points[i].name))
+ break;
+ }
+ if (i == TP_MAX)
+ continue;
+ ptr = strtok(NULL, " :");
+ if (major && major != atoh(ptr))
+ continue;
+ ptr = strtok(NULL, " :");
+ if (minor && minor != atoh(ptr))
+ continue;
+
+ switch (i) {
+ case TP_PID:
+ handle_tp_pid(ptr);
+ break;
+ case TP_IOS:
+ handle_tp_ios(ptr);
+ break;
+ }
+ }
+}
+
+static void __print_pid()
+{
+ struct pid_ent *entry;
+ int i;
+
+ setlocale(LC_ALL, "");
+ printf("%8s %16s %17s ||", "PID", "NAME", "R/W in 4KB");
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %17s |", file_type_string[i]);
+ printf("\n");
+
+ LIST_FOREACH(entry, &pid_info, ptr) {
+ printf("%8x %16s %'8lld %'8lld ||",
+ entry->pid, entry->name,
+ entry->total_io[READ],
+ entry->total_io[WRITE]);
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %'8lld %'8lld |",
+ entry->io[i][READ],
+ entry->io[i][WRITE]);
+ printf("\n");
+ }
+}
+
+static void __print_ftype()
+{
+ int i;
+
+ setlocale(LC_ALL, "");
+ printf("\n===== Data R/W in 4KB accoring to File types =====\n");
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %17s |", file_type_string[i]);
+ printf("\n");
+
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %'8lld %'8lld |",
+ total_io[i][READ],
+ total_io[i][WRITE]);
+ printf("\n");
+}
+
+static void do_print()
+{
+ switch (show_option) {
+ case SHOW_PID:
+ __print_pid();
+ break;
+ case SHOW_FTYPE:
+ __print_ftype();
+ break;
+ case SHOW_ALL:
+ __print_pid();
+ printf("\n\n");
+ __print_ftype();
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ int opt;
+
+ opt = parse_options(argc, argv);
+
+ file = fopen(argv[opt], "r");
+ if (!file) {
+ perror("open log file");
+ exit(EXIT_FAILURE);
+ }
+
+ do_init();
+
+ do_parse(file);
+
+ do_print();
+
+ fclose(file);
+ return 0;
+}