6ecf24ce4ce2c9faaf3ed8a69ecf3af72b37a1d0
[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 #include "tracers.h"
40
41 extern char **environ;
42
43 static int line_len = 1024;
44 static char line[1024];
45
46 static pid_t blktrace_pid = 0;
47 static pid_t mpstat_pid = 0;
48
49 char *mpstat_args[] = {
50         "mpstat",
51         "-P", "ALL", "1",
52         NULL,
53 };
54
55 int stop_tracer(pid_t *tracer_pid)
56 {
57         int ret;
58         pid_t pid = *tracer_pid;
59         pid_t pid_ret;
60         int status = 0;
61
62         if (pid == 0)
63                 return 0;
64
65         *tracer_pid = 0;
66         ret = kill(pid, SIGTERM);
67         if (ret) {
68                 fprintf(stderr, "failed to stop tracer pid %lu error %s\n",
69                         (unsigned long)pid, strerror(errno));
70                 return -errno;
71         }
72         pid_ret = waitpid(pid, &status, WUNTRACED);
73         if (pid_ret == pid && WIFEXITED(status) == 0) {
74                 fprintf(stderr, "blktrace returns error %d\n", WEXITSTATUS(status));
75         }
76         return 0;
77 }
78
79 static void sig_handler_for_quit(int val)
80 {
81         fprintf(stderr, "Received signal %d. Terminating tracers.\n", val);
82         if (blktrace_pid) {
83                 wait_program(blktrace_pid, "blktrace", SIGTERM);
84                 blktrace_pid = 0;
85         }
86         stop_tracer(&mpstat_pid);
87 }
88
89 int start_blktrace(char **devices, int num_devices, char *trace_name, char *dest)
90 {
91         int ret;
92         int i;
93         char *argv[15 + MAX_DEVICES_PER_TRACE * 2];
94         int argc = 0;
95
96         if (!trace_name)
97                 trace_name = "trace";
98         if (!dest)
99                 dest = ".";
100
101         argv[argc++] = "blktrace";
102         argv[argc++] = "-b";
103         argv[argc++] = "8192";
104         argv[argc++] = "-a";
105         argv[argc++] = "queue";
106         argv[argc++] = "-a";
107         argv[argc++] = "complete";
108         argv[argc++] = "-a";
109         argv[argc++] = "issue";
110         argv[argc++] = "-a";
111         argv[argc++] = "notify";
112
113         if (num_devices == 1) {
114                 argv[argc++] = "-o";
115                 argv[argc++] = trace_name;
116         } else {
117                 /* Multiple devices output to a directory named trace_name */
118                 dest = trace_name;
119         }
120         argv[argc++] = "-D";
121         argv[argc++] = dest;
122
123         for (i = 0; i < num_devices; i++) {
124                 argv[argc++] = "-d";
125                 argv[argc++] = devices[i];
126         }
127         argv[argc] = NULL;
128         signal(SIGTERM, sig_handler_for_quit);
129         signal(SIGINT, sig_handler_for_quit);
130         ret = run_program(argc, argv, 0, &blktrace_pid);
131         return ret;
132 }
133
134 int wait_program(pid_t pid, const char *pname, int sig)
135 {
136         int status;
137         int ret = 0;
138
139         if (sig) {
140                 ret = kill(pid, sig);
141                 if (ret) {
142                         fprintf(stderr, "Failed to send signal %d to %s (%lu): %s\n",
143                                 sig, pname, (long)pid, strerror(errno));
144                         return ret;
145                 }
146                 fprintf(stderr, "Kill (%d): %s (%ld)\n", sig, pname, (long)pid);
147         }
148
149         waitpid(pid, &status, 0);
150         if (WIFEXITED(status)) {
151                 ret = WEXITSTATUS(status);
152                 if (ret == 127) /* spawnp failed after forking */
153                         fprintf(stderr, "Failed to run '%s'\n", pname);
154         } else if (WIFSIGNALED(status) && sig && WTERMSIG(status) != sig) {
155                 fprintf(stderr, "'%s' killed by signal %d\n", pname, WTERMSIG(status));
156                 ret = -1;
157         }
158         return ret;
159 }
160
161 int run_program(int argc, char **argv, int wait, pid_t *pid)
162 {
163         int i;
164         int err;
165         pid_t _pid;
166
167         fprintf(stderr, "running");
168         for (i = 0; i < argc; i++)
169                 fprintf(stderr, " '%s'", argv[i]);
170         fprintf(stderr, "\n");
171
172         err = posix_spawnp(&_pid, argv[0], NULL, NULL, argv, environ);
173         if (err != 0) {
174                 fprintf(stderr, "Could not run '%s': %s\n", argv[0], strerror(err));
175         } else if (wait) {
176                 err = wait_program(_pid, argv[0], 0);
177         } else if (!pid) {
178                 fprintf(stderr, "Warning: %s (%ld): Not saving pid and not waiting for it.\n",
179                         argv[0], (long)_pid);
180         } else {
181                 *pid = _pid;
182         }
183         return err;
184 }
185
186 int wait_for_tracers(void)
187 {
188         if (blktrace_pid != 0) {
189                 int err = wait_program(blktrace_pid, "blktrace", SIGINT);
190                 if (err)
191                         exit(1);
192                 blktrace_pid = 0;
193         }
194         if (mpstat_pid != 0) {
195                 int status = 0;
196                 stop_tracer(&mpstat_pid);
197                 waitpid(mpstat_pid, &status, WUNTRACED);
198                 mpstat_pid = 0;
199         }
200         return 0;
201 }
202
203 int start_mpstat(char *trace_name)
204 {
205         int fd;
206         pid_t pid;
207
208         snprintf(line, line_len, "%s.mpstat", trace_name);
209
210         fd = open(line, O_WRONLY | O_CREAT | O_TRUNC, 0600);
211         if (fd < 0) {
212                 fprintf(stderr, "unable to open %s for writing err %s\n",
213                         line, strerror(errno));
214                 exit(1);
215         }
216         pid = fork();
217         if (pid == 0) {
218                 int ret;
219
220                 close(1);
221                 ret = dup2(fd, 1);
222                 if (ret < 0) {
223                         fprintf(stderr, "failed to setup output file for mpstat\n");
224                         exit(1);
225                 }
226                 ret = execvp("mpstat", mpstat_args);
227                 if (ret < 0) {
228                         fprintf(stderr, "failed to exec mpstat err %s\n",
229                                 strerror(ret));
230                         exit(1);
231                 }
232         }
233         mpstat_pid = pid;
234         return 0;
235 }