iowatcher: Add bounds checking in find_step
[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
35 #include "plot.h"
36 #include "blkparse.h"
37 #include "list.h"
38
39 static int line_len = 1024;
40 static char line[1024];
41
42 static pid_t blktrace_pid = 0;
43 static pid_t mpstat_pid = 0;
44
45 char *blktrace_args[17 + MAX_DEVICES_PER_TRACE * 2] = {
46         "blktrace",
47         "-b", "8192",
48         "-a", "queue",
49         "-a", "complete",
50         "-a", "issue",
51         "-a", "notify",
52         "-D", ".",
53         NULL,
54 };
55
56 char *mpstat_args[] = {
57         "mpstat",
58         "-P", "ALL", "1",
59         NULL,
60 };
61
62 #define DEST_DIR_INDEX 12
63 #define LAST_ARG 13
64
65 int stop_tracer(pid_t *tracer_pid)
66 {
67         int ret;
68         pid_t pid = *tracer_pid;
69         pid_t pid_ret;
70         int status = 0;
71
72         if (pid == 0)
73                 return 0;
74
75         *tracer_pid = 0;
76         ret = kill(pid, SIGTERM);
77         if (ret) {
78                 fprintf(stderr, "failed to stop tracer pid %lu error %s\n",
79                         (unsigned long)pid, strerror(errno));
80                 return -errno;
81         }
82         pid_ret = waitpid(pid, &status, WUNTRACED);
83         if (pid_ret == pid && WIFEXITED(status) == 0) {
84                 fprintf(stderr, "blktrace returns error %d\n", WEXITSTATUS(status));
85         }
86         return 0;
87 }
88
89
90 void stop_all_tracers(void)
91 {
92         stop_tracer(&blktrace_pid);
93         stop_tracer(&mpstat_pid);
94 }
95
96 void sig_handler_for_quit(int val)
97 {
98         fprintf(stderr, "iowatcher exiting with %d, stopping tracers\n", val);
99         stop_all_tracers();
100 }
101
102
103 int start_blktrace(char **devices, int num_devices, char *trace_name, char *dest)
104 {
105         pid_t pid;
106         int ret;
107         char **arg = blktrace_args;
108         int i;
109         int arg_index;
110
111         fprintf(stderr, "running blktrace");
112         if (!trace_name)
113                 trace_name = "trace";
114
115         arg_index = LAST_ARG;
116         for (i = 0; i < num_devices; i++) {
117                 blktrace_args[arg_index++] = "-d";
118                 blktrace_args[arg_index++] = devices[i];
119         }
120
121         /*
122          * single device traces use -o and are put into
123          * the dest dir if provided
124          */
125         if (num_devices == 1) {
126                 blktrace_args[arg_index++] = "-o";
127                 blktrace_args[arg_index++] = trace_name;
128                 if (dest)
129                         blktrace_args[DEST_DIR_INDEX] = dest;
130         } else {
131                 /*
132                  * multi device traces are put into a dest
133                  * dir based on the trace name
134                  */
135                 blktrace_args[DEST_DIR_INDEX] = trace_name;
136         }
137
138         blktrace_args[arg_index] = NULL;
139
140         while(*arg) {
141                 fprintf(stderr, " %s", *arg);
142                 arg++;
143         }
144         fprintf(stderr, "\n");
145
146
147         pid = fork();
148         if (pid == 0) {
149                 ret = execvp("blktrace", blktrace_args);
150                 if (ret) {
151                         fprintf(stderr, "failed to exec blktrace error %s\n", strerror(errno));
152                         exit(errno);
153                 }
154
155         } else {
156                 blktrace_pid = pid;
157                 signal(SIGTERM, sig_handler_for_quit);
158                 signal(SIGINT, sig_handler_for_quit);
159         }
160         return 0;
161 }
162
163 int run_program(char *str)
164 {
165         int ret;
166
167         fprintf(stderr, "running program %s\n", str);
168         ret = system(str);
169         if (ret == -1) {
170                 fprintf(stderr, "failed to run program %s error %s\n", str, strerror(errno));
171                 stop_all_tracers();
172                 return -errno;
173         }
174         stop_all_tracers();
175         return 0;
176 }
177
178 int wait_for_tracers(void)
179 {
180         int status = 0;
181         if (blktrace_pid != 0) {
182                 waitpid(blktrace_pid, &status, WUNTRACED);
183                 blktrace_pid = 0;
184         }
185         if (mpstat_pid != 0) {
186                 waitpid(mpstat_pid, &status, WUNTRACED);
187                 mpstat_pid = 0;
188         }
189         return 0;
190 }
191
192 int blktrace_to_dump(char *trace_name)
193 {
194         snprintf(line, line_len, "blkparse -O -i %s -d '%s.%s'",
195                 trace_name, trace_name, "dump");
196
197         system(line);
198         return 0;
199 }
200
201 int start_mpstat(char *trace_name)
202 {
203         int fd;
204         pid_t pid;
205
206         snprintf(line, line_len, "%s.mpstat", trace_name);
207
208         fd = open(line, O_WRONLY | O_CREAT | O_TRUNC, 0600);
209         if (fd < 0) {
210                 fprintf(stderr, "unable to open %s for writing err %s\n",
211                         line, strerror(errno));
212                 exit(1);
213         }
214         pid = fork();
215         if (pid == 0) {
216                 int ret;
217
218                 close(1);
219                 ret = dup2(fd, 1);
220                 if (ret < 0) {
221                         fprintf(stderr, "failed to setup output file for mpstat\n");
222                         exit(1);
223                 }
224                 ret = execvp("mpstat", mpstat_args);
225                 if (ret < 0) {
226                         fprintf(stderr, "failed to exec mpstat err %s\n",
227                                 strerror(ret));
228                         exit(1);
229                 }
230         }
231         mpstat_pid = pid;
232         return 0;
233 }