/*
* 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, " ");
/* int op_flags = 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;
}