/* * Copyright (C) 2012 Fusion-io * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Parts of this file were imported from Jens Axboe's blktrace sources (also GPL) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plot.h" #include "blkparse.h" #include "list.h" #include "tracers.h" extern char **environ; static pid_t blktrace_pid = 0; static pid_t mpstat_pid = 0; static void sig_handler_for_quit(int val) { fprintf(stderr, "Received signal %d. Terminating tracers.\n", val); wait_for_tracers(SIGTERM); } int start_blktrace(char **devices, int num_devices, char *trace_name, char *dest) { int ret; int i; char *argv[15 + MAX_DEVICES_PER_TRACE * 2]; int argc = 0; if (!trace_name) trace_name = "trace"; if (!dest) dest = "."; argv[argc++] = "blktrace"; argv[argc++] = "-b"; argv[argc++] = "8192"; argv[argc++] = "-a"; argv[argc++] = "queue"; argv[argc++] = "-a"; argv[argc++] = "complete"; argv[argc++] = "-a"; argv[argc++] = "issue"; argv[argc++] = "-a"; argv[argc++] = "notify"; if (num_devices == 1) { argv[argc++] = "-o"; argv[argc++] = trace_name; } else { /* Multiple devices output to a directory named trace_name */ dest = trace_name; } argv[argc++] = "-D"; argv[argc++] = dest; for (i = 0; i < num_devices; i++) { argv[argc++] = "-d"; argv[argc++] = devices[i]; } argv[argc] = NULL; signal(SIGTERM, sig_handler_for_quit); signal(SIGINT, sig_handler_for_quit); ret = run_program(argc, argv, 0, &blktrace_pid, NULL); return ret; } int wait_program(pid_t pid, const char *pname, int sig) { int status; int ret = 0; if (sig) { ret = kill(pid, sig); if (ret) { fprintf(stderr, "Failed to send signal %d to %s (%lu): %s\n", sig, pname, (long)pid, strerror(errno)); return ret; } fprintf(stderr, "Kill (%d): %s (%ld)\n", sig, pname, (long)pid); } waitpid(pid, &status, 0); if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret == 127) /* spawnp failed after forking */ fprintf(stderr, "Failed to run '%s'\n", pname); else fprintf(stderr, "Exit (%d): %s\n", ret, pname); } else if (WIFSIGNALED(status) && sig && WTERMSIG(status) != sig) { fprintf(stderr, "'%s' killed by signal %d\n", pname, WTERMSIG(status)); ret = -1; } return ret; } int run_program(int argc, char **argv, int wait, pid_t *pid, char *outpath) { int i; int err; pid_t _pid; posix_spawn_file_actions_t facts; posix_spawn_file_actions_t *factp = NULL; if (outpath != NULL) { posix_spawn_file_actions_init(&facts); posix_spawn_file_actions_addopen(&facts, 1, outpath, O_WRONLY|O_CREAT|O_TRUNC, 0600); factp = &facts; } fprintf(stderr, "Start"); for (i = 0; i < argc; i++) fprintf(stderr, " %s", argv[i]); fprintf(stderr, "\n"); err = posix_spawnp(&_pid, argv[0], factp, NULL, argv, environ); if (err != 0) { fprintf(stderr, "Could not run '%s': %s\n", argv[0], strerror(err)); } else if (wait) { err = wait_program(_pid, argv[0], 0); } else if (!pid) { fprintf(stderr, "Warning: %s (%ld): Not saving pid and not waiting for it.\n", argv[0], (long)_pid); } else { *pid = _pid; } return err; } int wait_for_tracers(int sig) { int err; if (blktrace_pid != 0) { err = wait_program(blktrace_pid, "blktrace", sig); if (err) exit(1); blktrace_pid = 0; } if (mpstat_pid != 0) { err = wait_program(mpstat_pid, "mpstat", sig); if (err) exit(1); mpstat_pid = 0; } return 0; } int start_mpstat(char *path) { int ret; int argc = 4; char *argv[] = { "mpstat", "-P", "ALL", "1", NULL, }; ret = run_program(argc, argv, 0, &mpstat_pid, path); return ret; }