diff --git a/Makefile.am b/Makefile.am index 2993833..034666f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,9 @@ -bin_PROGRAMS = kmemtraced kmemtrace-report kmemtrace-check kmemtrace-show +bin_PROGRAMS = kmemtraced kmemtraced-utt kmemtrace-report kmemtrace-check kmemtrace-show kmemtraced_SOURCES = kmemtraced.c common.h common.c kmemtrace.h kmemtraced_CFLAGS = @PTHREAD_CFLAGS@ $(AM_FLAGS) +kmemtraced_utt_SOURCES = kmemtraced-utt.c kmemtrace.h +kmemtraced_utt_CFLAGS = @PTHREAD_CFLAGS@ $(AM_FLAGS) -lutt kmemtrace_report_SOURCES = kmemtrace-report.c common.h common.c \ hashtable.h hashtable.c kmemtrace.h diff --git a/kmemtraced-utt.c b/kmemtraced-utt.c new file mode 100644 index 0000000..814f2e6 --- /dev/null +++ b/kmemtraced-utt.c @@ -0,0 +1,404 @@ +/* + * kmemtraced, converted to utt by Tom Zanussi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "kmemtrace.h" + +/* Trace files will have the form CHANNEL_NAME.APP_NAME.0, etc */ +#define APP_NAME "kmem" +#define CHANNEL_NAME "trace" +static char kmemtrace_version[] = "0.90.0"; + +#define KERNEL_DEBUG_PATH "/sys/kernel/debug" + +static struct trace_info *ti; +volatile int trace_started; + +#define tracing() (*(volatile int *)(&trace_started)) +#define set_tracing(v) (trace_started = (v)) + +/* + * You may want to increase this even more, if you are logging at a high + * rate and see skipped/missed events + */ +#define KMEMTRACE_SUBBUF_SIZE (8192 * sizeof(struct kmemtrace_event)) +#define KMEMTRACE_N_SUBBUFS 20 + +/* command line option globals */ +static int kill_running_trace; +static unsigned long buf_size = KMEMTRACE_SUBBUF_SIZE; +static unsigned long buf_nr = KMEMTRACE_N_SUBBUFS; + +/* our command line options, pretty much all boilerplate */ +#define S_OPTS "r:o:kw:Vb:n:D:lh:p:s:t" +static struct option l_opts[] = { + { + .name = "relay", + .has_arg = required_argument, + .flag = NULL, + .val = 'r' + }, + { + .name = "output", + .has_arg = required_argument, + .flag = NULL, + .val = 'o' + }, + { + .name = "kill", + .has_arg = no_argument, + .flag = NULL, + .val = 'k' + }, + { + .name = "stopwatch", + .has_arg = required_argument, + .flag = NULL, + .val = 'w' + }, + { + .name = "version", + .has_arg = no_argument, + .flag = NULL, + .val = 'V' + }, + { + .name = "buffer-size", + .has_arg = required_argument, + .flag = NULL, + .val = 'b' + }, + { + .name = "num-sub-buffers", + .has_arg = required_argument, + .flag = NULL, + .val = 'n' + }, + { + .name = "output-dir", + .has_arg = required_argument, + .flag = NULL, + .val = 'D' + }, + { + .name = "listen", + .has_arg = no_argument, + .flag = NULL, + .val = 'l' + }, + { + .name = "host", + .has_arg = required_argument, + .flag = NULL, + .val = 'h' + }, + { + .name = "port", + .has_arg = required_argument, + .flag = NULL, + .val = 'p' + }, + { + .name = "no-sendfile", + .has_arg = no_argument, + .flag = NULL, + .val = 's' + }, + { + .name = "attach", + .has_arg = no_argument, + .flag = NULL, + .val = 't' + }, + { + .name = NULL, + } +}; + +/** + * control_write - write a value to a control file + */ +static int control_write(const char *filename, int val) +{ + char tmp[4096]; + int fd, err = 0; + + sprintf(tmp, "%s/%s/%s/%s", KERNEL_DEBUG_PATH, APP_NAME, CHANNEL_NAME, + filename); + fd = open(tmp, O_RDWR); + if (fd < 0) { + printf("Couldn't open control file %s\n", tmp); + return fd; + } + + sprintf(tmp, "%u", val); + err = write(fd, tmp, strlen(tmp)); + if (err < 0) + printf("Couldn't write control file %s: errcode = %d: %s\n", + tmp, errno, strerror(errno)); + close(fd); + + return (err < 0 ? err : 0); +} + +static int destroy_trace(void) +{ + return control_write("destroy", 1); +} + +static int start_trace(struct trace_info *ti) +{ + int err; + + err = utt_add_channel(ti, "trace", buf_size, buf_nr); + if (err) + goto out; + + if (!ti->attach_only) { + err = control_write("buf_size", buf_size); + if (err) + goto out; + + err = control_write("buf_nr", buf_nr); + if (err) + goto out; + } else { + // read and set buf_size, buf_nr + } + + err = control_write("event_mask", ~0U); + if (err) + goto out; + + if (!ti->attach_only) { + err = control_write("create", 1); + if (err) + goto out; + } + + err = utt_start_threads(ti); + if (err) + goto out; + + if (!ti->attach_only) { + err = control_write("start", 1); + if (err) + goto out; + } + + set_tracing(1); +out: + return err; +} + +static void stop_trace(void) +{ + if (tracing() || kill_running_trace) { + set_tracing(0); + + /* + * should be stopped, just don't complain if it isn't + */ + if (!ti->attach_only) + control_write("stop", 1); + } +} + +static void handle_sigint(__attribute__((__unused__)) int sig) +{ + /* + * stop trace so we can reap currently produced data + */ + stop_trace(); + utt_set_tracing_done(ti); +} + +static char usage_str[] = \ + "[ -r debugfs path ] [ -o ] [-k ] [ -w time ]\n" \ + "-v ]\n\n" \ + "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \ + "\t-o File(s) to send output to\n" \ + "\t-D Directory to prepend to output file names\n" \ + "\t-k Kill a running trace\n" \ + "\t-w Stop after defined time, in seconds\n" \ + "\t-b Sub buffer size in KiB\n" \ + "\t-n Number of sub buffers\n" \ + "\t-l Run in network listen mode (blktrace server)\n" \ + "\t-h Run in network client mode, connecting to the given host\n" \ + "\t-p Network port to use (default 8462)\n" \ + "\t-s Make the network client NOT use sendfile() to transfer data\n" \ + "\t-t Attach and read buffers, don't start a new trace\n" \ + "\t-V Print program version info\n\n"; + +static void show_usage(char *program) +{ + fprintf(stderr, "Usage: %s %s %s", program, kmemtrace_version, usage_str); +} + +int main(int argc, char *argv[]) +{ + int i, c; + int stop_watch = 0; + + /* Channels will appear in /sys/debug/kernel/kmem */ + ti = utt_alloc_trace("kmem", 0, 0, NULL); + + while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) { + switch (c) { + case 'r': + ti->debugfs_path = optarg; + break; + + case 'o': + ti->output_name = optarg; + break; + case 'k': + kill_running_trace = 1; + break; + case 'w': + stop_watch = atoi(optarg); + if (stop_watch <= 0) { + fprintf(stderr, + "Invalid stopwatch value (%d secs)\n", + stop_watch); + return 1; + } + break; + case 'V': + printf("%s version %s\n", argv[0], kmemtrace_version); + return 0; + case 'b': + buf_size = strtoul(optarg, NULL, 10); + if (buf_size <= 0 || buf_size > 16 * 1024) { + fprintf(stderr, + "Invalid buffer size (%lu)\n", buf_size); + return 1; + } + buf_size <<= 10; + break; + case 'n': + buf_nr = strtoul(optarg, NULL, 10); + if (buf_nr <= 0) { + fprintf(stderr, + "Invalid buffer nr (%lu)\n", buf_nr); + return 1; + } + break; + case 'D': + ti->output_dir = optarg; + break; + case 'h': + ti->net_mode = Net_client; + strcpy(ti->hostname, optarg); + break; + case 'l': + ti->net_mode = Net_server; + break; + case 'p': + ti->net_port = atoi(optarg); + break; + case 's': + ti->net_use_sendfile = 0; + break; + case 't': + ti->attach_only = 1; + break; + default: + show_usage(argv[0]); + return 1; + } + } + + setlocale(LC_NUMERIC, "en_US"); + + if (ti->net_mode == Net_server) { + if (ti->output_name) { + fprintf(stderr, "-o ignored in server mode\n"); + ti->output_name = NULL; + } + + return utt_net_server(ti); + } + + if (utt_check_debugfs_path(ti)) + return 1; + + if (kill_running_trace) { + stop_trace(); + return 0; + } + + ti->ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ti->ncpus < 0) { + fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n"); + return 1; + } + + signal(SIGINT, handle_sigint); + signal(SIGHUP, handle_sigint); + signal(SIGTERM, handle_sigint); + signal(SIGALRM, handle_sigint); + signal(SIGPIPE, SIG_IGN); + + if (ti->net_mode == Net_client && + utt_net_setup_client(ti)) + return 1; + + if (start_trace(ti) != 0) + return 1; + + atexit(stop_trace); + + if (stop_watch) + alarm(stop_watch); + + utt_wait_for_threads(ti); + utt_exit_trace(ti); + stop_trace(); + utt_show_stats(ti); + utt_free_trace(ti); + destroy_trace(); + + return 0; +} +