iowatcher: Fix up directory trace processing
[blktrace.git] / iowatcher / tracers.c
1 /*
2  * Copyright (C) 2012 Fusion-io
3  *
4  *  This program is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU General Public
6  *  License v2 as published by the Free Software Foundation.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16  *
17  *  Parts of this file were imported from Jens Axboe's blktrace sources (also GPL)
18  */
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <inttypes.h>
27 #include <string.h>
28 #include <asm/types.h>
29 #include <errno.h>
30 #include <sys/mman.h>
31 #include <time.h>
32 #include <signal.h>
33 #include <sys/wait.h>
34 #include <spawn.h>
35
36 #include "plot.h"
37 #include "blkparse.h"
38 #include "list.h"
39
40 extern char **environ;
41
42 static int line_len = 1024;
43 static char line[1024];
44
45 static pid_t blktrace_pid = 0;
46 static pid_t mpstat_pid = 0;
47
48 char *blktrace_args[17 + MAX_DEVICES_PER_TRACE * 2] = {
49         "blktrace",
50         "-b", "8192",
51         "-a", "queue",
52         "-a", "complete",
53         "-a", "issue",
54         "-a", "notify",
55         "-D", ".",
56         NULL,
57 };
58
59 char *mpstat_args[] = {
60         "mpstat",
61         "-P", "ALL", "1",
62         NULL,
63 };
64
65 #define DEST_DIR_INDEX 12
66 #define LAST_ARG 13
67
68 int stop_tracer(pid_t *tracer_pid)
69 {
70         int ret;
71         pid_t pid = *tracer_pid;
72         pid_t pid_ret;
73         int status = 0;
74
75         if (pid == 0)
76                 return 0;
77
78         *tracer_pid = 0;
79         ret = kill(pid, SIGTERM);
80         if (ret) {
81                 fprintf(stderr, "failed to stop tracer pid %lu error %s\n",
82                         (unsigned long)pid, strerror(errno));
83                 return -errno;
84         }
85         pid_ret = waitpid(pid, &status, WUNTRACED);
86         if (pid_ret == pid && WIFEXITED(status) == 0) {
87                 fprintf(stderr, "blktrace returns error %d\n", WEXITSTATUS(status));
88         }
89         return 0;
90 }
91
92
93 void stop_all_tracers(void)
94 {
95         stop_tracer(&blktrace_pid);
96         stop_tracer(&mpstat_pid);
97 }
98
99 void sig_handler_for_quit(int val)
100 {
101         fprintf(stderr, "iowatcher exiting with %d, stopping tracers\n", val);
102         stop_all_tracers();
103 }
104
105
106 int start_blktrace(char **devices, int num_devices, char *trace_name, char *dest)
107 {
108         pid_t pid;
109         int ret;
110         char **arg = blktrace_args;
111         int i;
112         int arg_index;
113
114         fprintf(stderr, "running blktrace");
115         if (!trace_name)
116                 trace_name = "trace";
117
118         arg_index = LAST_ARG;
119         for (i = 0; i < num_devices; i++) {
120                 blktrace_args[arg_index++] = "-d";
121                 blktrace_args[arg_index++] = devices[i];
122         }
123
124         /*
125          * single device traces use -o and are put into
126          * the dest dir if provided
127          */
128         if (num_devices == 1) {
129                 blktrace_args[arg_index++] = "-o";
130                 blktrace_args[arg_index++] = trace_name;
131                 if (dest)
132                         blktrace_args[DEST_DIR_INDEX] = dest;
133         } else {
134                 /*
135                  * multi device traces are put into a dest
136                  * dir based on the trace name
137                  */
138                 blktrace_args[DEST_DIR_INDEX] = trace_name;
139         }
140
141         blktrace_args[arg_index] = NULL;
142
143         while(*arg) {
144                 fprintf(stderr, " %s", *arg);
145                 arg++;
146         }
147         fprintf(stderr, "\n");
148
149
150         pid = fork();
151         if (pid == 0) {
152                 ret = execvp("blktrace", blktrace_args);
153                 if (ret) {
154                         fprintf(stderr, "failed to exec blktrace error %s\n", strerror(errno));
155                         exit(errno);
156                 }
157
158         } else {
159                 blktrace_pid = pid;
160                 signal(SIGTERM, sig_handler_for_quit);
161                 signal(SIGINT, sig_handler_for_quit);
162         }
163         return 0;
164 }
165
166 int run_program(char *str)
167 {
168         int ret;
169
170         fprintf(stderr, "running program %s\n", str);
171         ret = system(str);
172         if (ret == -1) {
173                 fprintf(stderr, "failed to run program %s error %s\n", str, strerror(errno));
174                 stop_all_tracers();
175                 return -errno;
176         }
177         stop_all_tracers();
178         return 0;
179 }
180
181 int run_program2(int argc, char **argv)
182 {
183         int i;
184         int err;
185         pid_t pid;
186
187         fprintf(stderr, "running");
188         for (i = 0; i < argc; i++)
189                 fprintf(stderr, " '%s'", argv[i]);
190         fprintf(stderr, "\n");
191
192         err = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ);
193         if (err != 0) {
194                 fprintf(stderr, "%s failed with exit code %d\n", argv[0], err);
195                 return err;
196         }
197         waitpid(pid, NULL, 0);
198         return 0;
199 }
200
201 int wait_for_tracers(void)
202 {
203         int status = 0;
204         if (blktrace_pid != 0) {
205                 waitpid(blktrace_pid, &status, WUNTRACED);
206                 blktrace_pid = 0;
207         }
208         if (mpstat_pid != 0) {
209                 waitpid(mpstat_pid, &status, WUNTRACED);
210                 mpstat_pid = 0;
211         }
212         return 0;
213 }
214
215 int blktrace_to_dump(char *trace_name)
216 {
217         char *argv[] = {
218                 "blkparse", "-O",
219                 "-i", NULL,
220                 "-d", NULL,
221                 NULL
222         };
223
224         argv[3] = trace_name;
225         snprintf(line, line_len, "%s.dump", trace_name);
226         argv[5] = line;
227
228         return run_program2(6, argv);
229 }
230
231 int start_mpstat(char *trace_name)
232 {
233         int fd;
234         pid_t pid;
235
236         snprintf(line, line_len, "%s.mpstat", trace_name);
237
238         fd = open(line, O_WRONLY | O_CREAT | O_TRUNC, 0600);
239         if (fd < 0) {
240                 fprintf(stderr, "unable to open %s for writing err %s\n",
241                         line, strerror(errno));
242                 exit(1);
243         }
244         pid = fork();
245         if (pid == 0) {
246                 int ret;
247
248                 close(1);
249                 ret = dup2(fd, 1);
250                 if (ret < 0) {
251                         fprintf(stderr, "failed to setup output file for mpstat\n");
252                         exit(1);
253                 }
254                 ret = execvp("mpstat", mpstat_args);
255                 if (ret < 0) {
256                         fprintf(stderr, "failed to exec mpstat err %s\n",
257                                 strerror(ret));
258                         exit(1);
259                 }
260         }
261         mpstat_pid = pid;
262         return 0;
263 }