1/*
2 * f2fs IO tracer
3 *
4 * Copyright (c) 2014 Motorola Mobility
5 * Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#define _LARGEFILE64_SOURCE
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <string.h>
16#include <sys/queue.h>
17#include <assert.h>
18#include <locale.h>
19
20#define P_NAMELEN	16
21
22/* For global trace methods */
23enum show_type {
24	SHOW_PID,
25	SHOW_FTYPE,
26	SHOW_ALL,
27};
28
29enum trace_types {
30	TP_PID,
31	TP_IOS,
32	TP_MAX,
33};
34
35struct tps {
36	enum trace_types type;
37	const char *name;
38};
39
40struct tps trace_points[] = {
41	{ TP_PID,	"f2fs_trace_pid" },
42	{ TP_IOS,	"f2fs_trace_ios" },
43};
44
45/* For f2fs_trace_pid and f2fs_trace_ios */
46enum rw_type {
47	READ,
48	WRITE,
49	MAX_RW,
50};
51
52enum file_type {
53	__NORMAL_FILE,
54	__DIR_FILE,
55	__NODE_FILE,
56	__META_FILE,
57	__ATOMIC_FILE,
58	__VOLATILE_FILE,
59	__MISC_FILE,
60	__NR_FILES,
61};
62
63char *file_type_string[] = {
64	"User      ",
65	"Dir       ",
66	"Node      ",
67	"Meta      ",
68	"Atomic    ",
69	"Voltile   ",
70	"Misc      ",
71};
72
73struct pid_ent {
74	int pid;
75	char name[P_NAMELEN];
76	unsigned long long io[__NR_FILES][MAX_RW];
77	unsigned long long total_io[MAX_RW];
78	LIST_ENTRY(pid_ent) ptr;
79};
80
81/* global variables */
82int major = 0, minor = 0;
83int show_option = SHOW_ALL;
84unsigned long long total_io[__NR_FILES][MAX_RW];
85
86LIST_HEAD(plist, pid_ent) pid_info;
87
88/* Functions */
89static inline int atoh(char *str)
90{
91	int val;
92	sscanf(str, "%x", &val);
93	return val;
94}
95
96static void do_init()
97{
98	struct pid_ent *misc;
99
100	misc = calloc(1, sizeof(struct pid_ent));
101	assert(misc);
102
103	LIST_INIT(&pid_info);
104	LIST_INSERT_HEAD(&pid_info, misc, ptr);
105}
106
107void show_usage()
108{
109	printf("\nUsage: parse.f2fs [options] log_file\n");
110	printf("[options]:\n");
111	printf("  -a RW sorted by pid & file types\n");
112	printf("  -f RW sorted by file types\n");
113	printf("  -p RW sorted by pid\n");
114	printf("  -m major number\n");
115	printf("  -n minor number\n");
116	exit(1);
117}
118
119static int parse_options(int argc, char *argv[])
120{
121	const char *option_string = "fm:n:p";
122	int option = 0;
123
124	while ((option = getopt(argc, argv, option_string)) != EOF) {
125		switch (option) {
126		case 'f':
127			show_option = SHOW_FTYPE;
128			break;
129		case 'm':
130			major = atoh(optarg);
131			break;
132		case 'n':
133			minor = atoh(optarg);
134			break;
135		case 'p':
136			show_option = SHOW_PID;
137			break;
138		default:
139			printf("\tError: Unknown option %c\n", option);
140			show_usage();
141			break;
142		}
143	}
144	if ((optind + 1) != argc) {
145		printf("\tError: Log file is not specified.\n");
146		show_usage();
147	}
148	return optind;
149}
150
151struct pid_ent *get_pid_entry(int pid)
152{
153	struct pid_ent *entry;
154
155	LIST_FOREACH(entry, &pid_info, ptr) {
156		if (entry->pid == pid)
157			return entry;
158	}
159	return LIST_FIRST(&pid_info);
160}
161
162static void handle_tp_pid(char *ptr)
163{
164	struct pid_ent *pent;
165
166	pent = calloc(1, sizeof(struct pid_ent));
167	assert(pent);
168
169	ptr = strtok(NULL, " ");
170	pent->pid = atoh(ptr);
171
172	ptr = strtok(NULL, " ");
173	strcpy(pent->name, ptr);
174
175	LIST_INSERT_HEAD(&pid_info, pent, ptr);
176}
177
178static void handle_tp_ios(char *ptr)
179{
180	int pid, type, rw, len;
181	struct pid_ent *p;
182
183	ptr = strtok(NULL, " ");
184	pid = atoh(ptr);
185
186	ptr = strtok(NULL, " ");
187	ptr = strtok(NULL, " ");
188	type = atoh(ptr);
189
190	ptr = strtok(NULL, " ");
191	rw = atoh(ptr);
192
193	ptr = strtok(NULL, " ");
194	/* int op_flags = atoh(ptr) */
195	ptr = strtok(NULL, " ");
196	/* unsigned long long blkaddr = atoh(ptr); */
197
198	ptr = strtok(NULL, " ");
199	len = atoh(ptr);
200
201	/* update per-pid stat */
202	p = get_pid_entry(pid);
203	p->io[type][rw & 0x1] += len;
204	p->total_io[rw & 0x1] += len;
205
206	/* update total stat */
207	total_io[type][rw & 0x1] += len;
208}
209
210static void do_parse(FILE *file)
211{
212	char line[300];
213	char *ptr;
214	int i;
215
216	while (fgets(line, sizeof(line), file) != NULL) {
217		ptr = strtok(line, ":");
218
219		ptr = strtok(NULL, " :");
220
221		for (i = 0; i < TP_MAX; i++) {
222			if (!strcmp(ptr, trace_points[i].name))
223				break;
224		}
225		if (i == TP_MAX)
226			continue;
227		ptr = strtok(NULL, " :");
228		if (major && major != atoh(ptr))
229			continue;
230		ptr = strtok(NULL, " :");
231		if (minor && minor != atoh(ptr))
232			continue;
233
234		switch (i) {
235		case TP_PID:
236			handle_tp_pid(ptr);
237			break;
238		case TP_IOS:
239			handle_tp_ios(ptr);
240			break;
241		}
242	}
243}
244
245static void __print_pid()
246{
247	struct pid_ent *entry;
248	int i;
249
250	setlocale(LC_ALL, "");
251	printf("%8s %16s %17s ||", "PID", "NAME", "R/W in 4KB");
252	for (i = 0; i < __NR_FILES; i++)
253		printf(" %17s |", file_type_string[i]);
254	printf("\n");
255
256	LIST_FOREACH(entry, &pid_info, ptr) {
257		printf("%8x %16s %'8lld %'8lld ||",
258				entry->pid, entry->name,
259				entry->total_io[READ],
260				entry->total_io[WRITE]);
261		for (i = 0; i < __NR_FILES; i++)
262			printf(" %'8lld %'8lld |",
263				entry->io[i][READ],
264				entry->io[i][WRITE]);
265		printf("\n");
266	}
267}
268
269static void __print_ftype()
270{
271	int i;
272
273	setlocale(LC_ALL, "");
274	printf("\n===== Data R/W in 4KB according to File types =====\n");
275	for (i = 0; i < __NR_FILES; i++)
276		printf(" %17s |", file_type_string[i]);
277	printf("\n");
278
279	for (i = 0; i < __NR_FILES; i++)
280		printf(" %'8lld %'8lld |",
281				total_io[i][READ],
282				total_io[i][WRITE]);
283	printf("\n");
284}
285
286static void do_print()
287{
288	switch (show_option) {
289	case SHOW_PID:
290		__print_pid();
291		break;
292	case SHOW_FTYPE:
293		__print_ftype();
294		break;
295	case SHOW_ALL:
296		__print_pid();
297		printf("\n\n");
298		__print_ftype();
299		break;
300	}
301}
302
303int main(int argc, char **argv)
304{
305	FILE *file;
306	int opt;
307
308	opt = parse_options(argc, argv);
309
310	file = fopen(argv[opt], "r");
311	if (!file) {
312		perror("open log file");
313		exit(EXIT_FAILURE);
314	}
315
316	do_init();
317
318	do_parse(file);
319
320	do_print();
321
322	fclose(file);
323	return 0;
324}
325