aboutsummaryrefslogtreecommitdiff
path: root/tracecmd/trace-split.c
diff options
context:
space:
mode:
Diffstat (limited to 'tracecmd/trace-split.c')
-rw-r--r--tracecmd/trace-split.c556
1 files changed, 556 insertions, 0 deletions
diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c
new file mode 100644
index 00000000..83c5402c
--- /dev/null
+++ b/tracecmd/trace-split.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+#define _LARGEFILE64_SOURCE
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "trace-local.h"
+
+static unsigned int page_size;
+static const char *default_input_file = DEFAULT_INPUT_FILE;
+static const char *input_file;
+
+enum split_types {
+ SPLIT_NONE,
+ /* The order of these must be reverse of the case statement in the options */
+ SPLIT_SECONDS,
+ SPLIT_MSECS,
+ SPLIT_USECS,
+ SPLIT_EVENTS,
+ SPLIT_PAGES,
+ SPLIT_NR_TYPES,
+};
+
+struct cpu_data {
+ unsigned long long ts;
+ unsigned long long offset;
+ struct tep_record *record;
+ int cpu;
+ int fd;
+ int index;
+ void *commit;
+ void *page;
+ char *file;
+};
+
+static int create_type_len(struct tep_handle *pevent, int time, int len)
+{
+ static int bigendian = -1;
+ char *ptr;
+ int test;
+
+ if (bigendian < 0) {
+ test = 0x4321;
+ ptr = (char *)&test;
+ if (*ptr == 0x21)
+ bigendian = 0;
+ else
+ bigendian = 1;
+ }
+
+ if (tep_is_file_bigendian(pevent))
+ time |= (len << 27);
+ else
+ time = (time << 5) | len;
+
+ return tep_read_number(pevent, &time, 4);
+}
+
+static int write_record(struct tracecmd_input *handle,
+ struct tep_record *record,
+ struct cpu_data *cpu_data,
+ enum split_types type)
+{
+ unsigned long long diff;
+ struct tep_handle *pevent;
+ void *page;
+ int len = 0;
+ char *ptr;
+ int index = 0;
+ int time;
+
+ page = cpu_data->page;
+
+ pevent = tracecmd_get_tep(handle);
+
+ ptr = page + cpu_data->index;
+
+ diff = record->ts - cpu_data->ts;
+ if (diff > (1 << 27)) {
+ /* Add a time stamp */
+ len = RINGBUF_TYPE_TIME_EXTEND;
+ time = (unsigned int)(diff & ((1ULL << 27) - 1));
+ time = create_type_len(pevent, time, len);
+ *(unsigned *)ptr = time;
+ ptr += 4;
+ time = (unsigned int)(diff >> 27);
+ *(unsigned *)ptr = tep_read_number(pevent, &time, 4);
+ cpu_data->ts = record->ts;
+ cpu_data->index += 8;
+ return 0;
+ }
+
+ if (record->size && (record->size <= 28 * 4))
+ len = record->size / 4;
+
+ time = (unsigned)diff;
+ time = create_type_len(pevent, time, len);
+
+ memcpy(ptr, &time, 4);
+ ptr += 4;
+ index = 4;
+
+ if (!len) {
+ len = record->size + 4;
+ if ((len + 4) > record->record_size)
+ die("Bad calculation of record len (expect:%d actual:%d)",
+ record->record_size, len + 4);
+ *(unsigned *)ptr = tep_read_number(pevent, &len, 4);
+ ptr += 4;
+ index += 4;
+ }
+
+ len = (record->size + 3) & ~3;
+ index += len;
+
+ memcpy(ptr, record->data, len);
+
+ cpu_data->index += index;
+ cpu_data->ts = record->ts;
+
+ return 1;
+}
+
+static void write_page(struct tep_handle *pevent,
+ struct cpu_data *cpu_data, int long_size)
+{
+ if (long_size == 8) {
+ unsigned long long index = cpu_data->index - 16;
+ *(unsigned long long *)cpu_data->commit =
+ tep_read_number(pevent, &index, 8);
+ } else {
+ unsigned int index = cpu_data->index - 12;
+ *(unsigned int *)cpu_data->commit =
+ tep_read_number(pevent, &index, 4);
+ }
+ write(cpu_data->fd, cpu_data->page, page_size);
+}
+
+static struct tep_record *read_record(struct tracecmd_input *handle,
+ int percpu, int *cpu)
+{
+ if (percpu)
+ return tracecmd_read_data(handle, *cpu);
+
+ return tracecmd_read_next_data(handle, cpu);
+}
+
+static void set_cpu_time(struct tracecmd_input *handle,
+ int percpu, unsigned long long start, int cpu, int cpus)
+{
+ if (percpu) {
+ tracecmd_set_cpu_to_timestamp(handle, cpu, start);
+ return;
+ }
+
+ for (cpu = 0; cpu < cpus; cpu++)
+ tracecmd_set_cpu_to_timestamp(handle, cpu, start);
+ return;
+}
+
+static int parse_cpu(struct tracecmd_input *handle,
+ struct cpu_data *cpu_data,
+ unsigned long long start,
+ unsigned long long end,
+ int count_limit, int percpu, int cpu,
+ enum split_types type)
+{
+ struct tep_record *record;
+ struct tep_handle *pevent;
+ void *ptr;
+ int page_size;
+ int long_size = 0;
+ int cpus;
+ int count = 0;
+ int pages = 0;
+
+ cpus = tracecmd_cpus(handle);
+
+ long_size = tracecmd_long_size(handle);
+ page_size = tracecmd_page_size(handle);
+ pevent = tracecmd_get_tep(handle);
+
+ /* Force new creation of first page */
+ if (percpu) {
+ cpu_data[cpu].index = page_size + 1;
+ cpu_data[cpu].page = NULL;
+ } else {
+ for (cpu = 0; cpu < cpus; cpu++) {
+ cpu_data[cpu].index = page_size + 1;
+ cpu_data[cpu].page = NULL;
+ }
+ }
+
+ /*
+ * Get the cpu pointers up to the start of the
+ * start time stamp.
+ */
+
+ record = read_record(handle, percpu, &cpu);
+
+ if (start) {
+ set_cpu_time(handle, percpu, start, cpu, cpus);
+ while (record && record->ts < start) {
+ tracecmd_free_record(record);
+ record = read_record(handle, percpu, &cpu);
+ }
+ } else if (record)
+ start = record->ts;
+
+ while (record && (!end || record->ts <= end)) {
+ if (cpu_data[cpu].index + record->record_size > page_size) {
+
+ if (type == SPLIT_PAGES && ++pages > count_limit)
+ break;
+
+ if (cpu_data[cpu].page)
+ write_page(pevent, &cpu_data[cpu], long_size);
+ else {
+ cpu_data[cpu].page = malloc(page_size);
+ if (!cpu_data[cpu].page)
+ die("Failed to allocate page");
+ }
+
+ memset(cpu_data[cpu].page, 0, page_size);
+ ptr = cpu_data[cpu].page;
+
+ *(unsigned long long*)ptr =
+ tep_read_number(pevent, &(record->ts), 8);
+ cpu_data[cpu].ts = record->ts;
+ ptr += 8;
+ cpu_data[cpu].commit = ptr;
+ ptr += long_size;
+ cpu_data[cpu].index = 8 + long_size;
+ }
+
+ cpu_data[cpu].offset = record->offset;
+
+ if (write_record(handle, record, &cpu_data[cpu], type)) {
+ tracecmd_free_record(record);
+ record = read_record(handle, percpu, &cpu);
+
+ /* if we hit the end of the cpu, clear the offset */
+ if (!record) {
+ if (percpu)
+ cpu_data[cpu].offset = 0;
+ else
+ for (cpu = 0; cpu < cpus; cpu++)
+ cpu_data[cpu].offset = 0;
+ }
+
+ switch (type) {
+ case SPLIT_NONE:
+ break;
+ case SPLIT_SECONDS:
+ if (record &&
+ record->ts >
+ (start + (unsigned long long)count_limit * 1000000000ULL)) {
+ tracecmd_free_record(record);
+ record = NULL;
+ }
+ break;
+ case SPLIT_MSECS:
+ if (record &&
+ record->ts >
+ (start + (unsigned long long)count_limit * 1000000ULL)) {
+ tracecmd_free_record(record);
+ record = NULL;
+ }
+ break;
+ case SPLIT_USECS:
+ if (record &&
+ record->ts >
+ (start + (unsigned long long)count_limit * 1000ULL)) {
+ tracecmd_free_record(record);
+ record = NULL;
+ }
+ break;
+ case SPLIT_EVENTS:
+ if (++count >= count_limit) {
+ tracecmd_free_record(record);
+ record = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (record)
+ tracecmd_free_record(record);
+
+ if (percpu) {
+ if (cpu_data[cpu].page) {
+ write_page(pevent, &cpu_data[cpu], long_size);
+ free(cpu_data[cpu].page);
+ cpu_data[cpu].page = NULL;
+ }
+ } else {
+ for (cpu = 0; cpu < cpus; cpu++) {
+ if (cpu_data[cpu].page) {
+ write_page(pevent, &cpu_data[cpu], long_size);
+ free(cpu_data[cpu].page);
+ cpu_data[cpu].page = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static double parse_file(struct tracecmd_input *handle,
+ const char *output_file,
+ unsigned long long start,
+ unsigned long long end, int percpu, int only_cpu,
+ int count, enum split_types type)
+{
+ unsigned long long current;
+ struct tracecmd_output *ohandle;
+ struct cpu_data *cpu_data;
+ struct tep_record *record;
+ char **cpu_list;
+ char *output;
+ char *base;
+ char *file;
+ char *dir;
+ int cpus;
+ int cpu;
+ int fd;
+
+ output = strdup(output_file);
+ dir = dirname(output);
+ base = basename(output);
+
+ ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);
+
+ cpus = tracecmd_cpus(handle);
+ cpu_data = malloc(sizeof(*cpu_data) * cpus);
+ if (!cpu_data)
+ die("Failed to allocate cpu_data for %d cpus", cpus);
+
+ for (cpu = 0; cpu < cpus; cpu++) {
+ int ret;
+
+ ret = asprintf(&file, "%s/.tmp.%s.%d", dir, base, cpu);
+ if (ret < 0)
+ die("Failed to allocate file for %s %s %d", dir, base, cpu);
+ fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+ cpu_data[cpu].cpu = cpu;
+ cpu_data[cpu].fd = fd;
+ cpu_data[cpu].file = file;
+ cpu_data[cpu].offset = 0;
+ if (start)
+ tracecmd_set_cpu_to_timestamp(handle, cpu, start);
+ }
+
+ if (only_cpu >= 0) {
+ parse_cpu(handle, cpu_data, start, end, count,
+ 1, only_cpu, type);
+ } else if (percpu) {
+ for (cpu = 0; cpu < cpus; cpu++)
+ parse_cpu(handle, cpu_data, start,
+ end, count, percpu, cpu, type);
+ } else
+ parse_cpu(handle, cpu_data, start,
+ end, count, percpu, -1, type);
+
+ cpu_list = malloc(sizeof(*cpu_list) * cpus);
+ if (!cpu_list)
+ die("Failed to allocate cpu_list for %d cpus", cpus);
+ for (cpu = 0; cpu < cpus; cpu ++)
+ cpu_list[cpu] = cpu_data[cpu].file;
+
+ tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));
+ if (tracecmd_append_cpu_data(ohandle, cpus, cpu_list) < 0)
+ die("Failed to append tracing data\n");
+
+ current = end;
+ for (cpu = 0; cpu < cpus; cpu++) {
+ /* Set the tracecmd cursor to the next set of records */
+ if (cpu_data[cpu].offset) {
+ record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL);
+ if (record && (!current || record->ts > current))
+ current = record->ts + 1;
+ tracecmd_free_record(record);
+ }
+ unlink(cpu_data[cpu].file);
+ free(cpu_data[cpu].file);
+ }
+ free(cpu_data);
+ free(cpu_list);
+ free(output);
+ tracecmd_output_close(ohandle);
+
+ return current;
+}
+
+void trace_split (int argc, char **argv)
+{
+ struct tracecmd_input *handle;
+ unsigned long long start_ns = 0, end_ns = 0;
+ unsigned long long current;
+ double start, end;
+ char *endptr;
+ char *output = NULL;
+ char *output_file;
+ enum split_types split_type = SPLIT_NONE;
+ enum split_types type = SPLIT_NONE;
+ int count;
+ int repeat = 0;
+ int percpu = 0;
+ int cpu = -1;
+ int ac;
+ int c;
+
+ if (strcmp(argv[1], "split") != 0)
+ usage(argv);
+
+ while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
+ switch (c) {
+ case 'h':
+ usage(argv);
+ break;
+ case 'p':
+ type++;
+ case 'e':
+ type++;
+ case 'u':
+ type++;
+ case 'm':
+ type++;
+ case 's':
+ type++;
+ if (split_type != SPLIT_NONE)
+ die("Only one type of split is allowed");
+ count = atoi(optarg);
+ if (count <= 0)
+ die("Units must be greater than 0");
+ split_type = type;
+
+ /* Spliting by pages only makes sense per cpu */
+ if (type == SPLIT_PAGES)
+ percpu = 1;
+ break;
+ case 'r':
+ repeat = 1;
+ break;
+ case 'c':
+ percpu = 1;
+ break;
+ case 'C':
+ cpu = atoi(optarg);
+ break;
+ case 'o':
+ if (output)
+ die("only one output file allowed");
+ output = strdup(optarg);
+ break;
+ case 'i':
+ input_file = optarg;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ ac = (argc - optind);
+
+ if (ac >= 2) {
+ optind++;
+ start = strtod(argv[optind], &endptr);
+ if (ac > 3)
+ usage(argv);
+
+ /* Make sure a true start value was entered */
+ if (*endptr != 0)
+ die("Start value not floating point: %s", argv[optind]);
+
+ start_ns = (unsigned long long)(start * 1000000000.0);
+ optind++;
+ if (ac == 3) {
+ end = strtod(argv[optind], &endptr);
+
+ /* Make sure a true end value was entered */
+ if (*endptr != 0)
+ die("End value not floating point: %s",
+ argv[optind]);
+
+ end_ns = (unsigned long long)(end * 1000000000.0);
+ if (end_ns < start_ns)
+ die("Error: end is less than start");
+ }
+ }
+
+ if (!input_file)
+ input_file = default_input_file;
+
+ handle = tracecmd_open(input_file, 0);
+ if (!handle)
+ die("error reading %s", input_file);
+
+ if (tracecmd_get_file_state(handle) == TRACECMD_FILE_CPU_LATENCY)
+ die("trace-cmd split does not work with latency traces\n");
+
+ page_size = tracecmd_page_size(handle);
+
+ if (!output)
+ output = strdup(input_file);
+
+ if (!repeat) {
+ output = realloc(output, strlen(output) + 3);
+ strcat(output, ".1");
+ }
+
+ current = start_ns;
+ output_file = malloc(strlen(output) + 50);
+ if (!output_file)
+ die("Failed to allocate for %s", output);
+ c = 1;
+
+ do {
+ if (repeat)
+ sprintf(output_file, "%s.%04d", output, c++);
+ else
+ strcpy(output_file, output);
+
+ current = parse_file(handle, output_file, start_ns, end_ns,
+ percpu, cpu, count, type);
+ if (!repeat)
+ break;
+ start_ns = 0;
+ } while (current && (!end_ns || current < end_ns));
+
+ free(output);
+ free(output_file);
+
+ tracecmd_close(handle);
+
+ return;
+}