aboutsummaryrefslogtreecommitdiff
path: root/tracecmd/trace-read.c
diff options
context:
space:
mode:
Diffstat (limited to 'tracecmd/trace-read.c')
-rw-r--r--tracecmd/trace-read.c1984
1 files changed, 1984 insertions, 0 deletions
diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
new file mode 100644
index 00000000..df559d2a
--- /dev/null
+++ b/tracecmd/trace-read.c
@@ -0,0 +1,1984 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2009, 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 <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 <signal.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "trace-local.h"
+#include "trace-hash.h"
+#include "trace-hash-local.h"
+#include "kbuffer.h"
+#include "list.h"
+
+/*
+ * tep_func_repeat_format is defined as a weak variable in the
+ * libtraceevent library function plugin, to allow applications
+ * to override the format of the timestamp it prints for the
+ * last function that repeated.
+ */
+const char *tep_func_repeat_format;
+
+static struct filter_str {
+ struct filter_str *next;
+ char *filter;
+ int neg;
+} *filter_strings;
+static struct filter_str **filter_next = &filter_strings;
+
+struct filter {
+ struct filter *next;
+ struct tep_event_filter *filter;
+};
+
+struct event_str {
+ struct event_str *next;
+ const char *event;
+};
+
+struct handle_list {
+ struct list_head list;
+ struct tracecmd_input *handle;
+ const char *file;
+ int cpus;
+ int done;
+ struct tep_record *record;
+ struct filter *event_filters;
+ struct filter *event_filter_out;
+ unsigned long long *last_timestamp;
+};
+static struct list_head handle_list;
+
+struct input_files {
+ struct list_head list;
+ const char *file;
+ long long tsoffset;
+ unsigned long long ts2secs;
+};
+static struct list_head input_files;
+static struct input_files *last_input_file;
+
+struct pid_list {
+ struct pid_list *next;
+ char *pid;
+ int free;
+} *pid_list;
+
+struct pid_list *comm_list;
+
+static unsigned int page_size;
+static int input_fd;
+static const char *default_input_file = DEFAULT_INPUT_FILE;
+static const char *input_file;
+static int multi_inputs;
+static int max_file_size;
+
+static int instances;
+
+static int *filter_cpus;
+static int nr_filter_cpus;
+static int test_filters_mode;
+
+static int show_wakeup;
+static int wakeup_id;
+static int wakeup_new_id;
+static int sched_id;
+static int stacktrace_id;
+
+static int profile;
+
+static int buffer_breaks = 0;
+
+static int no_irqs;
+static int no_softirqs;
+
+static int tsdiff;
+static int tscheck;
+
+static int latency_format;
+static bool raw_format;
+static const char *format_type = TEP_PRINT_INFO;
+
+static struct tep_format_field *wakeup_task;
+static struct tep_format_field *wakeup_success;
+static struct tep_format_field *wakeup_new_task;
+static struct tep_format_field *wakeup_new_success;
+static struct tep_format_field *sched_task;
+static struct tep_format_field *sched_prio;
+
+static unsigned long long total_wakeup_lat;
+static unsigned long wakeup_lat_count;
+
+static unsigned long long total_wakeup_rt_lat;
+static unsigned long wakeup_rt_lat_count;
+
+struct wakeup_info {
+ struct trace_hash_item hash;
+ unsigned long long start;
+ int pid;
+};
+
+static struct hook_list *hooks;
+static struct hook_list *last_hook;
+
+#define WAKEUP_HASH_SIZE 1024
+static struct trace_hash wakeup_hash;
+
+static void print_event_name(struct trace_seq *s, struct tep_event *event)
+{
+ static const char *spaces = " "; /* 20 spaces */
+ const char *name;
+ int len;
+
+ name = event ? event->name : "(NULL)";
+
+ trace_seq_printf(s, " %s: ", name);
+
+ /* Space out the event names evenly. */
+ len = strlen(name);
+ if (len < 20)
+ trace_seq_printf(s, "%.*s", 20 - len, spaces);
+}
+
+enum time_fmt {
+ TIME_FMT_LAT = 1,
+ TIME_FMT_NORMAL = 2,
+};
+
+static const char *time_format(struct tracecmd_input *handle, enum time_fmt tf)
+{
+ struct tep_handle *tep = tracecmd_get_tep(handle);
+
+ switch (tf) {
+ case TIME_FMT_LAT:
+ if (latency_format)
+ return "%8.8s-%-5d %3d";
+ return "%16s-%-5d [%03d]";
+ default:
+ if (tracecmd_get_flags(handle) & TRACECMD_FL_IN_USECS) {
+ if (tep_test_flag(tep, TEP_NSEC_OUTPUT))
+ return " %9.1d:";
+ else
+ return " %6.1000d:";
+ } else
+ return "%12d:";
+ }
+}
+
+static void print_event(struct trace_seq *s, struct tracecmd_input *handle,
+ struct tep_record *record)
+{
+ struct tep_handle *tep = tracecmd_get_tep(handle);
+ struct tep_event *event;
+ const char *lfmt = time_format(handle, TIME_FMT_LAT);
+ const char *tfmt = time_format(handle, TIME_FMT_NORMAL);
+
+ event = tep_find_event_by_record(tep, record);
+ tep_print_event(tep, s, record, lfmt, TEP_PRINT_COMM,
+ TEP_PRINT_PID, TEP_PRINT_CPU);
+ tep_print_event(tep, s, record, tfmt, TEP_PRINT_TIME);
+ print_event_name(s, event);
+ tep_print_event(tep, s, record, "%s", format_type);
+}
+
+/* Debug variables for testing tracecmd_read_at */
+#define TEST_READ_AT 0
+#if TEST_READ_AT
+#define DO_TEST
+static off64_t test_read_at_offset;
+static int test_read_at_copy = 100;
+static int test_read_at_index;
+static void show_test(struct tracecmd_input *handle)
+{
+ struct tep_record *record;
+ struct trace_seq s;
+
+ if (!test_read_at_offset) {
+ printf("\nNO RECORD COPIED\n");
+ return;
+ }
+
+ record = tracecmd_read_at(handle, test_read_at_offset, NULL);
+ printf("\nHERE'S THE COPY RECORD\n");
+ trace_seq_init(&s);
+ print_event(&s, handle, record);
+ trace_seq_do_printf(&s);
+ trace_seq_destroy(&s);
+ printf("\n");
+
+ tracecmd_free_record(record);
+}
+
+static void test_save(struct tep_record *record, int cpu)
+{
+ if (test_read_at_index++ == test_read_at_copy) {
+ test_read_at_offset = record->offset;
+ printf("\nUSING THIS RECORD\n");
+ }
+}
+#endif /* TEST_READ_AT */
+
+/* Debug variables for testing tracecmd_set_cpu_at_timestamp */
+#define TEST_AT_TIMESTAMP 0
+#if TEST_AT_TIMESTAMP
+#define DO_TEST
+static unsigned long long test_at_timestamp_ts;
+static int test_at_timestamp_copy = 100;
+static int test_at_timestamp_cpu = -1;
+static int test_at_timestamp_index;
+static void show_test(struct tracecmd_input *handle)
+{
+ struct tep_record *record;
+ struct trace_seq s;
+ int cpu = test_at_timestamp_cpu;
+
+ if (!test_at_timestamp_ts) {
+ printf("\nNO RECORD COPIED\n");
+ return;
+ }
+
+ if (tracecmd_set_cpu_to_timestamp(handle, cpu, test_at_timestamp_ts))
+ return;
+
+ record = tracecmd_read_data(handle, cpu);
+ printf("\nHERE'S THE COPY RECORD with page %p offset=%p\n",
+ (void *)(record->offset & ~(page_size - 1)),
+ (void *)record->offset);
+ trace_seq_init(&s);
+ print_event(&s, handle, record);
+ trace_seq_do_printf(&s);
+ trace_seq_destroy(&s);
+ printf("\n");
+
+ tracecmd_free_record(record);
+}
+
+static void test_save(struct tep_record *record, int cpu)
+{
+ if (test_at_timestamp_index++ == test_at_timestamp_copy) {
+ test_at_timestamp_ts = record->ts;
+ test_at_timestamp_cpu = cpu;
+ printf("\nUSING THIS RECORD page=%p offset=%p\n",
+ (void *)(record->offset & ~(page_size - 1)),
+ (void *)record->offset);
+ }
+}
+#endif /* TEST_AT_TIMESTAMP */
+
+#define TEST_FIRST_LAST 0
+#if TEST_FIRST_LAST
+#define DO_TEST
+static void show_test(struct tracecmd_input *handle)
+{
+ struct tep_record *record;
+ struct trace_seq s;
+ int cpu = 0;
+
+ record = tracecmd_read_cpu_first(handle, cpu);
+ if (!record) {
+ printf("No first record?\n");
+ return;
+ }
+
+ printf("\nHERE'S THE FIRST RECORD with offset %p\n",
+ (void *)record->offset);
+ trace_seq_init(&s);
+ print_event(&s, handle, record);
+ trace_seq_do_printf(&s);
+ trace_seq_destroy(&s);
+ printf("\n");
+
+ tracecmd_free_record(record);
+
+ record = tracecmd_read_cpu_last(handle, cpu);
+ if (!record) {
+ printf("No last record?\n");
+ return;
+ }
+
+ printf("\nHERE'S THE LAST RECORD with offset %p\n",
+ (void *)record->offset);
+ trace_seq_init(&s);
+ print_event(&s, handle, record);
+ trace_seq_do_printf(&s);
+ trace_seq_destroy(&s);
+ printf("\n");
+
+ tracecmd_free_record(record);
+}
+static void test_save(struct tep_record *record, int cpu)
+{
+}
+#endif /* TEST_FIRST_LAST */
+
+#ifndef DO_TEST
+static void show_test(struct tracecmd_input *handle)
+{
+ /* quiet the compiler */
+ if (0)
+ print_event(NULL, NULL, NULL);
+}
+static void test_save(struct tep_record *record, int cpu)
+{
+}
+#endif
+
+static void add_input(const char *file)
+{
+ struct input_files *item;
+
+ item = malloc(sizeof(*item));
+ if (!item)
+ die("Failed to allocate for %s", file);
+ memset(item, 0, sizeof(*item));
+ item->file = file;
+ list_add_tail(&item->list, &input_files);
+ last_input_file = item;
+}
+
+static void add_handle(struct tracecmd_input *handle, const char *file)
+{
+ struct handle_list *item;
+
+ item = malloc(sizeof(*item));
+ if (!item)
+ die("Failed ot allocate for %s", file);
+ memset(item, 0, sizeof(*item));
+ item->handle = handle;
+ if (file) {
+ item->file = file + strlen(file);
+ /* we want just the base name */
+ while (item->file >= file && *item->file != '/')
+ item->file--;
+ item->file++;
+ if (strlen(item->file) > max_file_size)
+ max_file_size = strlen(item->file);
+ }
+ list_add_tail(&item->list, &handle_list);
+}
+
+static void free_inputs(void)
+{
+ struct input_files *item;
+
+ while (!list_empty(&input_files)) {
+ item = container_of(input_files.next, struct input_files, list);
+ list_del(&item->list);
+ free(item);
+ }
+}
+
+static void free_handles(void)
+{
+ struct handle_list *item;
+
+ while (!list_empty(&handle_list)) {
+ item = container_of(handle_list.next, struct handle_list, list);
+ list_del(&item->list);
+ free(item);
+ }
+}
+
+static void add_filter(const char *filter, int neg)
+{
+ struct filter_str *ftr;
+
+ ftr = malloc(sizeof(*ftr));
+ if (!ftr)
+ die("Failed to allocate for filter %s", filter);
+ ftr->filter = strdup(filter);
+ if (!ftr->filter)
+ die("malloc");
+ ftr->next = NULL;
+ ftr->neg = neg;
+
+ /* must maintain order of command line */
+ *filter_next = ftr;
+ filter_next = &ftr->next;
+}
+
+static void __add_filter(struct pid_list **head, const char *arg)
+{
+ struct pid_list *list;
+ char *pids = strdup(arg);
+ char *pid;
+ char *sav;
+ int free = 1;
+
+ if (!pids)
+ die("malloc");
+
+ pid = strtok_r(pids, ",", &sav);
+ while (pid) {
+ list = malloc(sizeof(*list));
+ if (!list)
+ die("Failed to allocate for arg %s", arg);
+ list->pid = pid;
+ list->free = free;
+ list->next = *head;
+ *head = list;
+ /* The first pid needs to be freed */
+ free = 0;
+ pid = strtok_r(NULL, ",", &sav);
+ }
+}
+
+static void add_comm_filter(const char *arg)
+{
+ __add_filter(&comm_list, arg);
+}
+
+static void add_pid_filter(const char *arg)
+{
+ __add_filter(&pid_list, arg);
+}
+
+static char *append_pid_filter(char *curr_filter, char *pid)
+{
+ char *filter;
+ int len, curr_len;
+
+#define FILTER_FMT "(common_pid==" __STR ")||(pid==" __STR ")||(next_pid==" __STR ")"
+
+#undef __STR
+#define __STR ""
+
+ /* strlen(".*:") > strlen("||") */
+ len = strlen(".*:" FILTER_FMT) + strlen(pid) * 3 + 1;
+
+#undef __STR
+#define __STR "%s"
+
+ if (!curr_filter) {
+ filter = malloc(len);
+ if (!filter)
+ die("Failed to allocate for filter %s", curr_filter);
+ sprintf(filter, ".*:" FILTER_FMT, pid, pid, pid);
+ } else {
+ curr_len = strlen(curr_filter);
+ len += curr_len;
+
+ filter = realloc(curr_filter, len);
+ if (!filter)
+ die("realloc");
+ sprintf(filter + curr_len, "||" FILTER_FMT, pid, pid, pid);
+ }
+
+ return filter;
+}
+
+static void convert_comm_filter(struct tracecmd_input *handle)
+{
+ struct tep_cmdline *cmdline;
+ struct tep_handle *pevent;
+ struct pid_list *list;
+
+ char pidstr[100];
+
+ if (!comm_list)
+ return;
+
+ pevent = tracecmd_get_tep(handle);
+
+ /* Seach for comm names and get their pids */
+ for (list = comm_list; list; list = list->next) {
+ cmdline = tep_data_pid_from_comm(pevent, list->pid, NULL);
+ if (!cmdline) {
+ warning("comm: %s not in cmdline list", list->pid);
+ continue;
+ }
+ do {
+ sprintf(pidstr, "%d", tep_cmdline_pid(pevent, cmdline));
+ add_pid_filter(pidstr);
+ cmdline = tep_data_pid_from_comm(pevent, list->pid,
+ cmdline);
+ } while (cmdline);
+ }
+
+ while (comm_list) {
+ list = comm_list;
+ comm_list = comm_list->next;
+ if (list->free)
+ free(list->pid);
+ free(list);
+ }
+}
+
+static void make_pid_filter(struct tracecmd_input *handle)
+{
+ struct pid_list *list;
+ char *str = NULL;
+
+ convert_comm_filter(handle);
+
+ if (!pid_list)
+ return;
+
+ /* First do all common pids */
+ for (list = pid_list; list; list = list->next) {
+ str = append_pid_filter(str, list->pid);
+ }
+
+ add_filter(str, 0);
+ free(str);
+
+ while (pid_list) {
+ list = pid_list;
+ pid_list = pid_list->next;
+ if (list->free)
+ free(list->pid);
+ free(list);
+ }
+}
+
+static void process_filters(struct handle_list *handles)
+{
+ struct filter **filter_next = &handles->event_filters;
+ struct filter **filter_out_next = &handles->event_filter_out;
+ struct filter *event_filter;
+ struct filter_str *filter;
+ struct tep_handle *pevent;
+ char errstr[200];
+ int filters = 0;
+ int ret;
+
+ pevent = tracecmd_get_tep(handles->handle);
+
+ make_pid_filter(handles->handle);
+
+ while (filter_strings) {
+ filter = filter_strings;
+ filter_strings = filter->next;
+
+ event_filter = malloc(sizeof(*event_filter));
+ if (!event_filter)
+ die("Failed to allocate for event filter");
+ event_filter->next = NULL;
+ event_filter->filter = tep_filter_alloc(pevent);
+ if (!event_filter->filter)
+ die("malloc");
+
+ ret = tep_filter_add_filter_str(event_filter->filter,
+ filter->filter);
+ if (ret < 0) {
+ tep_strerror(pevent, ret, errstr, sizeof(errstr));
+ die("Error filtering: %s\n%s",
+ filter->filter, errstr);
+ }
+
+ if (filter->neg) {
+ *filter_out_next = event_filter;
+ filter_out_next = &event_filter->next;
+ } else {
+ *filter_next = event_filter;
+ filter_next = &event_filter->next;
+ }
+ filters++;
+ free(filter->filter);
+ free(filter);
+ }
+ if (filters && test_filters_mode)
+ exit(0);
+}
+
+static void init_wakeup(struct tracecmd_input *handle)
+{
+ struct tep_handle *pevent;
+ struct tep_event *event;
+
+ if (!show_wakeup)
+ return;
+
+ pevent = tracecmd_get_tep(handle);
+
+ trace_hash_init(&wakeup_hash, WAKEUP_HASH_SIZE);
+
+ event = tep_find_event_by_name(pevent, "sched", "sched_wakeup");
+ if (!event)
+ goto fail;
+ wakeup_id = event->id;
+ wakeup_task = tep_find_field(event, "pid");
+ if (!wakeup_task)
+ goto fail;
+ wakeup_success = tep_find_field(event, "success");
+
+ event = tep_find_event_by_name(pevent, "sched", "sched_switch");
+ if (!event)
+ goto fail;
+ sched_id = event->id;
+ sched_task = tep_find_field(event, "next_pid");
+ if (!sched_task)
+ goto fail;
+
+ sched_prio = tep_find_field(event, "next_prio");
+ if (!sched_prio)
+ goto fail;
+
+
+ wakeup_new_id = -1;
+
+ event = tep_find_event_by_name(pevent, "sched", "sched_wakeup_new");
+ if (!event)
+ goto skip;
+ wakeup_new_id = event->id;
+ wakeup_new_task = tep_find_field(event, "pid");
+ if (!wakeup_new_task)
+ goto fail;
+ wakeup_new_success = tep_find_field(event, "success");
+
+ skip:
+ return;
+
+ fail:
+ show_wakeup = 0;
+}
+
+static void add_wakeup(unsigned int val, unsigned long long start)
+{
+ unsigned int key = trace_hash(val);
+ struct wakeup_info *info;
+ struct trace_hash_item *item;
+
+ item = trace_hash_find(&wakeup_hash, key, NULL, NULL);
+ if (item) {
+ info = container_of(item, struct wakeup_info, hash);
+ /* Hmm, double wakeup? */
+ info->start = start;
+ return;
+ }
+
+ info = malloc(sizeof(*info));
+ if (!info)
+ die("Failed to allocate wakeup info");
+ info->hash.key = key;
+ info->start = start;
+ trace_hash_add(&wakeup_hash, &info->hash);
+}
+
+static unsigned long long max_lat = 0;
+static unsigned long long max_time;
+static unsigned long long min_lat = -1;
+static unsigned long long min_time;
+
+static unsigned long long max_rt_lat = 0;
+static unsigned long long max_rt_time;
+static unsigned long long min_rt_lat = -1;
+static unsigned long long min_rt_time;
+
+static void add_sched(unsigned int val, unsigned long long end, int rt)
+{
+ struct trace_hash_item *item;
+ unsigned int key = trace_hash(val);
+ struct wakeup_info *info;
+ unsigned long long cal;
+
+ item = trace_hash_find(&wakeup_hash, key, NULL, NULL);
+ if (!item)
+ return;
+
+ info = container_of(item, struct wakeup_info, hash);
+
+ cal = end - info->start;
+
+ if (cal > max_lat) {
+ max_lat = cal;
+ max_time = end;
+ }
+ if (cal < min_lat) {
+ min_lat = cal;
+ min_time = end;
+ }
+
+ if (rt) {
+ if (cal > max_rt_lat) {
+ max_rt_lat = cal;
+ max_rt_time = end;
+ }
+ if (cal < min_rt_lat) {
+ min_rt_lat = cal;
+ min_rt_time = end;
+ }
+ }
+
+ printf(" Latency: %llu.%03llu usecs", cal / 1000, cal % 1000);
+
+ total_wakeup_lat += cal;
+ wakeup_lat_count++;
+
+ if (rt) {
+ total_wakeup_rt_lat += cal;
+ wakeup_rt_lat_count++;
+ }
+
+ trace_hash_del(item);
+ free(info);
+}
+
+static void process_wakeup(struct tep_handle *pevent, struct tep_record *record)
+{
+ unsigned long long val;
+ int id;
+
+ if (!show_wakeup)
+ return;
+
+ id = tep_data_type(pevent, record);
+ if (id == wakeup_id) {
+ if (tep_read_number_field(wakeup_success, record->data, &val) == 0) {
+ if (!val)
+ return;
+ }
+ if (tep_read_number_field(wakeup_task, record->data, &val))
+ return;
+ add_wakeup(val, record->ts);
+ } else if (id == wakeup_new_id) {
+ if (tep_read_number_field(wakeup_new_success, record->data, &val) == 0) {
+ if (!val)
+ return;
+ }
+ if (tep_read_number_field(wakeup_new_task, record->data, &val))
+ return;
+ add_wakeup(val, record->ts);
+ } else if (id == sched_id) {
+ int rt = 1;
+ if (tep_read_number_field(sched_prio, record->data, &val))
+ return;
+ if (val > 99)
+ rt = 0;
+ if (tep_read_number_field(sched_task, record->data, &val))
+ return;
+ add_sched(val, record->ts, rt);
+ }
+}
+
+static void
+show_wakeup_timings(unsigned long long total, unsigned long count,
+ unsigned long long lat_max, unsigned long long time_max,
+ unsigned long long lat_min, unsigned long long time_min)
+{
+
+ total /= count;
+
+ printf("\nAverage wakeup latency: %llu.%03llu usecs\n",
+ total / 1000,
+ total % 1000);
+ printf("Maximum Latency: %llu.%03llu usecs at ", lat_max / 1000, lat_max % 1000);
+ printf("timestamp: %llu.%06llu\n",
+ time_max / 1000000000, ((time_max + 500) % 1000000000) / 1000);
+ printf("Minimum Latency: %llu.%03llu usecs at ", lat_min / 1000, lat_min % 1000);
+ printf("timestamp: %llu.%06llu\n\n", time_min / 1000000000,
+ ((time_min + 500) % 1000000000) / 1000);
+}
+
+static void finish_wakeup(void)
+{
+ struct wakeup_info *info;
+ struct trace_hash_item **bucket;
+ struct trace_hash_item *item;
+
+ if (!show_wakeup || !wakeup_lat_count)
+ return;
+
+ show_wakeup_timings(total_wakeup_lat, wakeup_lat_count,
+ max_lat, max_time,
+ min_lat, min_time);
+
+
+ if (wakeup_rt_lat_count) {
+ printf("RT task timings:\n");
+ show_wakeup_timings(total_wakeup_rt_lat, wakeup_rt_lat_count,
+ max_rt_lat, max_rt_time,
+ min_rt_lat, min_rt_time);
+ }
+
+ trace_hash_for_each_bucket(bucket, &wakeup_hash) {
+ trace_hash_while_item(item, bucket) {
+ trace_hash_del(item);
+ info = container_of(item, struct wakeup_info, hash);
+ free(info);
+ }
+ }
+
+ trace_hash_free(&wakeup_hash);
+}
+
+void trace_show_data(struct tracecmd_input *handle, struct tep_record *record)
+{
+ tracecmd_show_data_func func = tracecmd_get_show_data_func(handle);
+ const char *tfmt = time_format(handle, TIME_FMT_NORMAL);
+ const char *cfmt = latency_format ? "%8.8s-%-5d %3d" : "%16s-%-5d [%03d]";
+ struct tep_handle *pevent;
+ struct tep_event *event;
+ struct trace_seq s;
+ int cpu = record->cpu;
+ bool use_trace_clock;
+ static unsigned long long last_ts;
+ unsigned long long diff_ts;
+ unsigned long page_size;
+ char buf[50];
+
+ page_size = tracecmd_page_size(handle);
+
+ test_save(record, cpu);
+
+ if (func) {
+ func(handle, record);
+ return;
+ }
+
+ pevent = tracecmd_get_tep(handle);
+ event = tep_find_event_by_record(pevent, record);
+ use_trace_clock = tracecmd_get_use_trace_clock(handle);
+
+ trace_seq_init(&s);
+ if (record->missed_events > 0)
+ trace_seq_printf(&s, "CPU:%d [%lld EVENTS DROPPED]\n",
+ cpu, record->missed_events);
+ else if (record->missed_events < 0)
+ trace_seq_printf(&s, "CPU:%d [EVENTS DROPPED]\n", cpu);
+ if (buffer_breaks || tracecmd_get_debug()) {
+ if (tracecmd_record_at_buffer_start(handle, record)) {
+ trace_seq_printf(&s, "CPU:%d [SUBBUFFER START]", cpu);
+ if (tracecmd_get_debug())
+ trace_seq_printf(&s, " [%lld:0x%llx]",
+ tracecmd_page_ts(handle, record),
+ record->offset & ~(page_size - 1));
+ trace_seq_putc(&s, '\n');
+ }
+ }
+
+ tep_print_event(pevent, &s, record, cfmt,
+ TEP_PRINT_COMM,
+ TEP_PRINT_PID,
+ TEP_PRINT_CPU);
+
+ if (latency_format) {
+ if (raw_format)
+ trace_seq_printf(&s, "-0x%x",
+ tep_data_flags(pevent, record));
+ else
+ tep_print_event(pevent, &s, record, "%s",
+ TEP_PRINT_LATENCY);
+ }
+
+ tep_print_event(pevent, &s, record, tfmt, TEP_PRINT_TIME);
+
+ if (tsdiff) {
+ unsigned long long rec_ts = record->ts;
+
+ buf[0] = 0;
+ if (use_trace_clock && !tep_test_flag(pevent, TEP_NSEC_OUTPUT))
+ rec_ts = (rec_ts + 500) / 1000;
+ if (last_ts) {
+ diff_ts = rec_ts - last_ts;
+ snprintf(buf, 50, "(+%lld)", diff_ts);
+ buf[49] = 0;
+ }
+ last_ts = rec_ts;
+ trace_seq_printf(&s, " %-8s", buf);
+ }
+
+ print_event_name(&s, event);
+ tep_print_event(pevent, &s, record, "%s", format_type);
+
+ if (s.len && *(s.buffer + s.len - 1) == '\n')
+ s.len--;
+ if (tracecmd_get_debug()) {
+ struct kbuffer *kbuf;
+ struct kbuffer_raw_info info;
+ void *page;
+ void *offset;
+
+ trace_seq_printf(&s, " [%d:0x%llx:%d]",
+ tracecmd_record_ts_delta(handle, record),
+ record->offset & (page_size - 1), record->size);
+ kbuf = tracecmd_record_kbuf(handle, record);
+ page = tracecmd_record_page(handle, record);
+ offset = tracecmd_record_offset(handle, record);
+
+ if (kbuf && page && offset) {
+ struct kbuffer_raw_info *pi = &info;
+
+ /* We need to get the record raw data to get next */
+ pi->next = offset;
+ pi = kbuffer_raw_get(kbuf, page, pi);
+ while ((pi = kbuffer_raw_get(kbuf, page, pi))) {
+ if (pi->type < KBUFFER_TYPE_PADDING)
+ break;
+ switch (pi->type) {
+ case KBUFFER_TYPE_PADDING:
+ trace_seq_printf(&s, "\n PADDING: ");
+ break;
+ case KBUFFER_TYPE_TIME_EXTEND:
+ trace_seq_printf(&s, "\n TIME EXTEND: ");
+ break;
+ case KBUFFER_TYPE_TIME_STAMP:
+ trace_seq_printf(&s, "\n TIME STAMP: ");
+ break;
+ }
+ if (pi->type == KBUFFER_TYPE_TIME_STAMP)
+ trace_seq_printf(&s, "timestamp:%lld length:%d",
+ pi->delta,
+ pi->length);
+ else
+ trace_seq_printf(&s, "delta:%lld length:%d",
+ pi->delta,
+ pi->length);
+ }
+ }
+ }
+
+ trace_seq_do_printf(&s);
+ trace_seq_destroy(&s);
+
+ process_wakeup(pevent, record);
+
+ printf("\n");
+}
+
+static void read_latency(struct tracecmd_input *handle)
+{
+ char *buf = NULL;
+ size_t size = 0;
+ int r;
+
+ do {
+ r = tracecmd_latency_data_read(handle, &buf, &size);
+ if (r > 0)
+ printf("%.*s", r, buf);
+ } while (r > 0);
+
+ printf("\n");
+ free(buf);
+}
+
+static int
+test_filters(struct tep_handle *pevent, struct filter *event_filters,
+ struct tep_record *record, int neg)
+{
+ int found = 0;
+ int ret = FILTER_NONE;
+ int flags;
+
+ if (no_irqs || no_softirqs) {
+ flags = tep_data_flags(pevent, record);
+ if (no_irqs && (flags & TRACE_FLAG_HARDIRQ))
+ return FILTER_MISS;
+ if (no_softirqs && (flags & TRACE_FLAG_SOFTIRQ))
+ return FILTER_MISS;
+ }
+
+ while (event_filters) {
+ ret = tep_filter_match(event_filters->filter, record);
+ switch (ret) {
+ case FILTER_NONE:
+ case FILTER_MATCH:
+ found = 1;
+ }
+ /* We need to test all negative filters */
+ if (!neg && found)
+ break;
+ event_filters = event_filters->next;
+ }
+
+ return ret;
+}
+
+struct stack_info_cpu {
+ int cpu;
+ int last_printed;
+};
+
+struct stack_info {
+ struct stack_info *next;
+ struct handle_list *handles;
+ struct stack_info_cpu *cpus;
+ int stacktrace_id;
+ int nr_cpus;
+};
+
+static int
+test_stacktrace(struct handle_list *handles, struct tep_record *record,
+ int last_printed)
+{
+ static struct stack_info *infos;
+ struct stack_info *info;
+ struct stack_info_cpu *cpu_info;
+ struct handle_list *h;
+ struct tracecmd_input *handle;
+ struct tep_handle *pevent;
+ struct tep_event *event;
+ static int init;
+ int ret;
+ int id;
+
+ if (!init) {
+ init = 1;
+
+ list_for_each_entry(h, &handle_list, list) {
+ info = malloc(sizeof(*info));
+ if (!info)
+ die("Failed to allocate handle");
+ info->handles = h;
+ info->nr_cpus = tracecmd_cpus(h->handle);
+
+ info->cpus = malloc(sizeof(*info->cpus) * info->nr_cpus);
+ if (!info->cpus)
+ die("Failed to allocate for %d cpus", info->nr_cpus);
+ memset(info->cpus, 0, sizeof(*info->cpus));
+
+ pevent = tracecmd_get_tep(h->handle);
+ event = tep_find_event_by_name(pevent, "ftrace",
+ "kernel_stack");
+ if (event)
+ info->stacktrace_id = event->id;
+ else
+ info->stacktrace_id = 0;
+
+ info->next = infos;
+ infos = info;
+ }
+
+
+ }
+
+ handle = handles->handle;
+ pevent = tracecmd_get_tep(handle);
+
+ for (info = infos; info; info = info->next)
+ if (info->handles == handles)
+ break;
+
+ if (!info->stacktrace_id)
+ return 0;
+
+ cpu_info = &info->cpus[record->cpu];
+
+ id = tep_data_type(pevent, record);
+
+ /*
+ * Print the stack trace if the previous event was printed.
+ * But do not print the stack trace if it is explicitly
+ * being filtered out.
+ */
+ if (id == info->stacktrace_id) {
+ ret = test_filters(pevent, handles->event_filter_out, record, 1);
+ if (ret != FILTER_MATCH)
+ return cpu_info->last_printed;
+ return 0;
+ }
+
+ cpu_info->last_printed = last_printed;
+ return 0;
+}
+
+static struct tep_record *get_next_record(struct handle_list *handles)
+{
+ struct tep_record *record;
+ struct tep_handle *pevent;
+ int found = 0;
+ int cpu;
+ int ret;
+
+ if (handles->record)
+ return handles->record;
+
+ if (handles->done)
+ return NULL;
+
+ pevent = tracecmd_get_tep(handles->handle);
+
+ do {
+ if (filter_cpus) {
+ long long last_stamp = -1;
+ struct tep_record *precord;
+ int first_record = 1;
+ int next_cpu = -1;
+ int i;
+
+ for (i = 0; (cpu = filter_cpus[i]) >= 0; i++) {
+ precord = tracecmd_peek_data(handles->handle, cpu);
+ if (precord &&
+ (first_record || precord->ts < last_stamp)) {
+ next_cpu = cpu;
+ last_stamp = precord->ts;
+ first_record = 0;
+ }
+ }
+ if (!first_record)
+ record = tracecmd_read_data(handles->handle, next_cpu);
+ else
+ record = NULL;
+ } else
+ record = tracecmd_read_next_data(handles->handle, &cpu);
+
+ if (record) {
+ ret = test_filters(pevent, handles->event_filters, record, 0);
+ switch (ret) {
+ case FILTER_NOEXIST:
+ /* Stack traces may still filter this */
+ if (stacktrace_id &&
+ test_stacktrace(handles, record, 0))
+ found = 1;
+ else
+ tracecmd_free_record(record);
+ break;
+ case FILTER_NONE:
+ case FILTER_MATCH:
+ /* Test the negative filters (-v) */
+ ret = test_filters(pevent, handles->event_filter_out,
+ record, 1);
+ if (ret != FILTER_MATCH) {
+ found = 1;
+ break;
+ }
+ /* fall through */
+ default:
+ tracecmd_free_record(record);
+ }
+ }
+ } while (record && !found);
+
+ if (record && stacktrace_id)
+ test_stacktrace(handles, record, 1);
+
+ handles->record = record;
+ if (!record)
+ handles->done = 1;
+
+ return record;
+}
+
+static void free_handle_record(struct handle_list *handles)
+{
+ if (!handles->record)
+ return;
+
+ tracecmd_free_record(handles->record);
+ handles->record = NULL;
+}
+
+static void print_handle_file(struct handle_list *handles)
+{
+ /* Only print file names if more than one file is read */
+ if (!multi_inputs && !instances)
+ return;
+ if (handles->file && *handles->file != '\0')
+ printf("%*s: ", max_file_size, handles->file);
+ else
+ printf("%*s ", max_file_size, "");
+}
+
+static void free_filters(struct filter *event_filter)
+{
+ struct filter *filter;
+
+ while (event_filter) {
+ filter = event_filter;
+ event_filter = filter->next;
+
+ tep_filter_free(filter->filter);
+ free(filter);
+ }
+}
+
+enum output_type {
+ OUTPUT_NORMAL,
+ OUTPUT_STAT_ONLY,
+ OUTPUT_UNAME_ONLY,
+ OUTPUT_VERSION_ONLY,
+};
+
+static void read_data_info(struct list_head *handle_list, enum output_type otype,
+ int global, int align_ts)
+{
+ unsigned long long ts, first_ts;
+ struct handle_list *handles;
+ struct handle_list *last_handle;
+ struct tep_record *record;
+ struct tep_record *last_record;
+ struct tep_handle *pevent;
+ struct tep_event *event;
+ int first = 1;
+ int ret;
+
+ list_for_each_entry(handles, handle_list, list) {
+ int cpus;
+
+ if (!tracecmd_is_buffer_instance(handles->handle)) {
+ ret = tracecmd_init_data(handles->handle);
+ if (ret < 0)
+ die("failed to init data");
+ }
+ cpus = tracecmd_cpus(handles->handle);
+ handles->cpus = cpus;
+ handles->last_timestamp = calloc(cpus, sizeof(*handles->last_timestamp));
+ if (!handles->last_timestamp)
+ die("allocating timestamps");
+
+ /* Don't process instances that we added here */
+ if (tracecmd_is_buffer_instance(handles->handle))
+ continue;
+
+ if (align_ts) {
+ ts = tracecmd_get_first_ts(handles->handle);
+ if (first || first_ts > ts)
+ first_ts = ts;
+ first = 0;
+ }
+ print_handle_file(handles);
+ printf("cpus=%d\n", cpus);
+
+ /* Latency trace is just all ASCII */
+ if (ret > 0) {
+ if (multi_inputs)
+ die("latency traces do not work with multiple inputs");
+ read_latency(handles->handle);
+ return;
+ }
+
+ switch (otype) {
+ case OUTPUT_NORMAL:
+ break;
+ case OUTPUT_STAT_ONLY:
+ printf("\nKernel buffer statistics:\n"
+ " Note: \"entries\" are the entries left in the kernel ring buffer and are not\n"
+ " recorded in the trace data. They should all be zero.\n\n");
+ tracecmd_print_stats(handles->handle);
+ continue;
+ case OUTPUT_UNAME_ONLY:
+ tracecmd_print_uname(handles->handle);
+ case OUTPUT_VERSION_ONLY:
+ tracecmd_print_version(handles->handle);
+ continue;
+ }
+
+ /* Find the kernel_stacktrace if available */
+ pevent = tracecmd_get_tep(handles->handle);
+ event = tep_find_event_by_name(pevent, "ftrace", "kernel_stack");
+ if (event)
+ stacktrace_id = event->id;
+
+ init_wakeup(handles->handle);
+ if (last_hook)
+ last_hook->next = tracecmd_hooks(handles->handle);
+ else
+ hooks = tracecmd_hooks(handles->handle);
+ if (profile)
+ trace_init_profile(handles->handle, hooks, global);
+
+ process_filters(handles);
+
+ /* If this file has buffer instances, get the handles for them */
+ instances = tracecmd_buffer_instances(handles->handle);
+ if (instances) {
+ struct tracecmd_input *new_handle;
+ const char *name;
+ int i;
+
+ for (i = 0; i < instances; i++) {
+ name = tracecmd_buffer_instance_name(handles->handle, i);
+ if (!name)
+ die("error in reading buffer instance");
+ new_handle = tracecmd_buffer_instance_handle(handles->handle, i);
+ if (!new_handle) {
+ warning("could not retrieve handle %s", name);
+ continue;
+ }
+ add_handle(new_handle, name);
+ }
+ }
+ }
+
+ if (otype != OUTPUT_NORMAL)
+ return;
+
+ if (align_ts) {
+ list_for_each_entry(handles, handle_list, list) {
+ tracecmd_add_ts_offset(handles->handle, -first_ts);
+ }
+ }
+
+ do {
+ last_handle = NULL;
+ last_record = NULL;
+
+ list_for_each_entry(handles, handle_list, list) {
+ record = get_next_record(handles);
+ if (!record)
+ continue;
+ if (!last_record ||
+ (record && record->ts < last_record->ts)) {
+ last_record = record;
+ last_handle = handles;
+ }
+ }
+ if (last_record) {
+ int cpu = last_record->cpu;
+ if (cpu >= last_handle->cpus)
+ die("cpu %d greater than %d\n", cpu, last_handle->cpus);
+ if (tscheck &&
+ last_handle->last_timestamp[cpu] > last_record->ts) {
+ errno = 0;
+ warning("WARNING: Record on cpu %d went backwards: %lld to %lld delta: -%lld\n",
+ cpu, last_handle->last_timestamp[cpu],
+ last_record->ts,
+ last_handle->last_timestamp[cpu] - last_record->ts);
+ }
+ last_handle->last_timestamp[cpu] = last_record->ts;
+ print_handle_file(last_handle);
+ trace_show_data(last_handle->handle, last_record);
+ free_handle_record(last_handle);
+ }
+ } while (last_record);
+
+ if (profile)
+ do_trace_profile();
+
+ list_for_each_entry(handles, handle_list, list) {
+ free_filters(handles->event_filters);
+ free_filters(handles->event_filter_out);
+ free(handles->last_timestamp);
+
+ show_test(handles->handle);
+ }
+}
+
+struct tracecmd_input *read_trace_header(const char *file, int flags)
+{
+ input_fd = open(file, O_RDONLY);
+ if (input_fd < 0)
+ die("opening '%s'\n", file);
+
+ return tracecmd_alloc_fd(input_fd, flags);
+}
+
+static void sig_end(int sig)
+{
+ struct handle_list *handles;
+
+ fprintf(stderr, "trace-cmd: Received SIGINT\n");
+
+ list_for_each_entry(handles, &handle_list, list) {
+ tracecmd_close(handles->handle);
+ }
+
+ exit(0);
+}
+
+static const char *skip_space_and_test_digit(const char *p, const char *cpu_str)
+{
+ while (isspace(*p))
+ p++;
+ if (!isdigit(*p))
+ die("invalid character '%c' in cpu string '%s'",
+ *p, cpu_str);
+ return p;
+}
+
+static void __add_cpu(int cpu)
+{
+ filter_cpus = tracecmd_add_id(filter_cpus, cpu, nr_filter_cpus++);
+}
+
+static void parse_cpulist(const char *cpu_str)
+{
+ unsigned a, b;
+ const char *s = cpu_str;
+
+ do {
+ s = skip_space_and_test_digit(s, cpu_str);
+ b = a = strtoul(s, (char **)&s, 10);
+ if (*s == '-') {
+ s = skip_space_and_test_digit(s + 1, cpu_str);
+ b = strtoul(s, (char **)&s, 10);
+ }
+ if (!(a <= b))
+ die("range of cpu numbers must be lower to greater");
+ while (a <= b) {
+ __add_cpu(a);
+ a++;
+ }
+ if (*s == ',' || *s == ':')
+ s++;
+ } while (*s != '\0');
+}
+
+static void read_file_fd(int fd, char *dst, int len)
+{
+ size_t size = 0;
+ int r;
+
+ do {
+ r = read(fd, dst+size, len);
+ if (r > 0) {
+ size += r;
+ len -= r;
+ }
+ } while (r > 0);
+}
+
+static void add_functions(struct tep_handle *pevent, const char *file)
+{
+ struct stat st;
+ char *buf;
+ int ret;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ die("Can't read file %s", file);
+
+ ret = fstat(fd, &st);
+ if (ret < 0)
+ die("Can't stat file %s", file);
+
+ buf = malloc(st.st_size + 1);
+ if (!buf)
+ die("Failed to allocate for function buffer");
+ read_file_fd(fd, buf, st.st_size);
+ buf[st.st_size] = '\0';
+ close(fd);
+ tep_parse_kallsyms(pevent, buf);
+ free(buf);
+}
+
+static void process_plugin_option(char *option)
+{
+ char *name = option;
+ char *val = NULL;
+ char *p;
+
+ if ((p = strstr(name, "="))) {
+ *p = '\0';
+ val = p+1;
+ }
+ tep_plugin_add_option(name, val);
+}
+
+static void set_event_flags(struct tep_handle *pevent, struct event_str *list,
+ unsigned int flag)
+{
+ struct tep_event **events;
+ struct tep_event *event;
+ struct event_str *str;
+ regex_t regex;
+ int ret;
+ int i;
+
+ if (!list)
+ return;
+
+ events = tep_list_events(pevent, 0);
+
+ for (str = list; str; str = str->next) {
+ char *match;
+
+ match = malloc(strlen(str->event) + 3);
+ if (!match)
+ die("Failed to allocate for match string '%s'", str->event);
+ sprintf(match, "^%s$", str->event);
+
+ ret = regcomp(&regex, match, REG_ICASE|REG_NOSUB);
+ if (ret < 0)
+ die("Can't parse '%s'", str->event);
+ free(match);
+ for (i = 0; events[i]; i++) {
+ event = events[i];
+ if (!regexec(&regex, event->name, 0, NULL, 0) ||
+ !regexec(&regex, event->system, 0, NULL, 0))
+ event->flags |= flag;
+ }
+ }
+}
+
+static void add_hook(const char *arg)
+{
+ struct hook_list *hook;
+
+ hook = tracecmd_create_event_hook(arg);
+
+ hook->next = hooks;
+ hooks = hook;
+ if (!last_hook)
+ last_hook = hook;
+}
+
+enum {
+ OPT_verbose = 234,
+ OPT_align_ts = 235,
+ OPT_raw_ts = 236,
+ OPT_version = 237,
+ OPT_tscheck = 238,
+ OPT_tsdiff = 239,
+ OPT_ts2secs = 240,
+ OPT_tsoffset = 241,
+ OPT_bycomm = 242,
+ OPT_debug = 243,
+ OPT_uname = 244,
+ OPT_profile = 245,
+ OPT_event = 246,
+ OPT_comm = 247,
+ OPT_boundary = 248,
+ OPT_stat = 249,
+ OPT_pid = 250,
+ OPT_nodate = 251,
+ OPT_check_event_parsing = 252,
+ OPT_kallsyms = 253,
+ OPT_events = 254,
+ OPT_cpu = 255,
+ OPT_cpus = 256,
+};
+
+void trace_report (int argc, char **argv)
+{
+ struct tracecmd_input *handle;
+ struct tep_handle *pevent;
+ struct event_str *raw_events = NULL;
+ struct event_str *nohandler_events = NULL;
+ struct event_str **raw_ptr = &raw_events;
+ struct event_str **nohandler_ptr = &nohandler_events;
+ const char *functions = NULL;
+ const char *print_event = NULL;
+ struct input_files *inputs;
+ struct handle_list *handles;
+ enum output_type otype;
+ long long tsoffset = 0;
+ unsigned long long ts2secs = 0;
+ unsigned long long ts2sc;
+ int open_flags = 0;
+ int show_stat = 0;
+ int show_funcs = 0;
+ int show_endian = 0;
+ int show_page_size = 0;
+ int show_printk = 0;
+ int show_uname = 0;
+ int show_version = 0;
+ int show_events = 0;
+ int show_cpus = 0;
+ int print_events = 0;
+ int nanosec = 0;
+ int no_date = 0;
+ int raw_ts = 0;
+ int align_ts = 0;
+ int global = 0;
+ int neg = 0;
+ int ret = 0;
+ int check_event_parsing = 0;
+ int c;
+
+ list_head_init(&handle_list);
+ list_head_init(&input_files);
+
+ if (argc < 2)
+ usage(argv);
+
+ if (strcmp(argv[1], "report") != 0)
+ usage(argv);
+
+ signal(SIGINT, sig_end);
+
+ for (;;) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"cpu", required_argument, NULL, OPT_cpu},
+ {"cpus", no_argument, NULL, OPT_cpus},
+ {"events", no_argument, NULL, OPT_events},
+ {"event", required_argument, NULL, OPT_event},
+ {"filter-test", no_argument, NULL, 'T'},
+ {"kallsyms", required_argument, NULL, OPT_kallsyms},
+ {"pid", required_argument, NULL, OPT_pid},
+ {"comm", required_argument, NULL, OPT_comm},
+ {"check-events", no_argument, NULL,
+ OPT_check_event_parsing},
+ {"nodate", no_argument, NULL, OPT_nodate},
+ {"stat", no_argument, NULL, OPT_stat},
+ {"boundary", no_argument, NULL, OPT_boundary},
+ {"debug", no_argument, NULL, OPT_debug},
+ {"profile", no_argument, NULL, OPT_profile},
+ {"uname", no_argument, NULL, OPT_uname},
+ {"version", no_argument, NULL, OPT_version},
+ {"by-comm", no_argument, NULL, OPT_bycomm},
+ {"ts-offset", required_argument, NULL, OPT_tsoffset},
+ {"ts2secs", required_argument, NULL, OPT_ts2secs},
+ {"ts-diff", no_argument, NULL, OPT_tsdiff},
+ {"ts-check", no_argument, NULL, OPT_tscheck},
+ {"raw-ts", no_argument, NULL, OPT_raw_ts},
+ {"align-ts", no_argument, NULL, OPT_align_ts},
+ {"verbose", optional_argument, NULL, OPT_verbose},
+ {"help", no_argument, NULL, '?'},
+ {NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long (argc-1, argv+1, "+hSIi:H:feGpRr:tPNn:LlEwF:V::vTqO:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ usage(argv);
+ break;
+ case 'i':
+ if (input_file) {
+ if (!multi_inputs) {
+ add_input(input_file);
+ if (tsoffset)
+ last_input_file->tsoffset = tsoffset;
+ }
+ multi_inputs++;
+ add_input(optarg);
+ } else
+ input_file = optarg;
+ break;
+ case 'F':
+ add_filter(optarg, neg);
+ break;
+ case 'H':
+ add_hook(optarg);
+ break;
+ case 'T':
+ test_filters_mode = 1;
+ break;
+ case 'f':
+ show_funcs = 1;
+ break;
+ case 'I':
+ no_irqs = 1;
+ break;
+ case 'S':
+ no_softirqs = 1;
+ break;
+ case 'P':
+ show_printk = 1;
+ break;
+ case 'L':
+ open_flags |= TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS;
+ break;
+ case 'N':
+ open_flags |= TRACECMD_FL_LOAD_NO_PLUGINS;
+ break;
+ case 'n':
+ *nohandler_ptr = malloc(sizeof(struct event_str));
+ if (!*nohandler_ptr)
+ die("Failed to allocate for '-n %s'", optarg);
+ (*nohandler_ptr)->event = optarg;
+ (*nohandler_ptr)->next = NULL;
+ nohandler_ptr = &(*nohandler_ptr)->next;
+ break;
+ case 'e':
+ show_endian = 1;
+ break;
+ case 'p':
+ show_page_size = 1;
+ break;
+ case 'E':
+ show_events = 1;
+ break;
+ case 'G':
+ global = 1;
+ break;
+ case 'R':
+ raw_format = true;
+ break;
+ case 'r':
+ *raw_ptr = malloc(sizeof(struct event_str));
+ if (!*raw_ptr)
+ die("Failed to allocate '-r %s'", optarg);
+ (*raw_ptr)->event = optarg;
+ (*raw_ptr)->next = NULL;
+ raw_ptr = &(*raw_ptr)->next;
+ break;
+ case 't':
+ nanosec = 1;
+ break;
+ case 'w':
+ show_wakeup = 1;
+ break;
+ case 'l':
+ latency_format = 1;
+ break;
+ case 'O':
+ process_plugin_option(optarg);
+ break;
+ case 'v':
+ if (neg)
+ die("Only 1 -v can be used");
+ neg = 1;
+ break;
+ case 'q':
+ silence_warnings = 1;
+ tracecmd_set_loglevel(TEP_LOG_NONE);
+ break;
+ case OPT_cpu:
+ parse_cpulist(optarg);
+ break;
+ case OPT_cpus:
+ show_cpus = 1;
+ break;
+ case OPT_events:
+ print_events = 1;
+ break;
+ case OPT_event:
+ print_event = optarg;
+ break;
+ case OPT_kallsyms:
+ functions = optarg;
+ break;
+ case OPT_pid:
+ add_pid_filter(optarg);
+ break;
+ case OPT_comm:
+ add_comm_filter(optarg);
+ break;
+ case OPT_check_event_parsing:
+ check_event_parsing = 1;
+ break;
+ case OPT_nodate:
+ no_date = 1;
+ break;
+ case OPT_stat:
+ show_stat = 1;
+ break;
+ case OPT_boundary:
+ /* Debug to look at buffer breaks */
+ buffer_breaks = 1;
+ break;
+ case OPT_debug:
+ buffer_breaks = 1;
+ tracecmd_set_debug(true);
+ break;
+ case OPT_profile:
+ profile = 1;
+ break;
+ case OPT_uname:
+ show_uname = 1;
+ break;
+ case OPT_version:
+ show_version = 1;
+ break;
+ case OPT_bycomm:
+ trace_profile_set_merge_like_comms();
+ break;
+ case OPT_ts2secs:
+ ts2sc = atoll(optarg);
+ if (multi_inputs)
+ last_input_file->ts2secs = ts2sc;
+ else
+ ts2secs = ts2sc;
+ break;
+ case OPT_tsoffset:
+ tsoffset = atoll(optarg);
+ if (multi_inputs)
+ last_input_file->tsoffset = tsoffset;
+ if (!input_file)
+ die("--ts-offset must come after -i");
+ break;
+ case OPT_tsdiff:
+ tsdiff = 1;
+ break;
+ case OPT_tscheck:
+ tscheck = 1;
+ break;
+ case OPT_raw_ts:
+ raw_ts = 1;
+ break;
+ case OPT_align_ts:
+ align_ts = 1;
+ break;
+ case 'V':
+ case OPT_verbose:
+ show_status = 1;
+ if (trace_set_verbose(optarg) < 0)
+ die("invalid verbose level %s", optarg);
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ if ((argc - optind) >= 2) {
+ if (input_file)
+ usage(argv);
+ input_file = argv[optind + 1];
+ }
+
+ if (!input_file)
+ input_file = default_input_file;
+
+ if (!multi_inputs) {
+ add_input(input_file);
+ if (tsoffset)
+ last_input_file->tsoffset = tsoffset;
+ } else if (show_wakeup)
+ die("Wakeup tracing can only be done on a single input file");
+
+ list_for_each_entry(inputs, &input_files, list) {
+ handle = read_trace_header(inputs->file, open_flags);
+ if (!handle)
+ die("error reading header for %s", inputs->file);
+
+ /* If used with instances, top instance will have no tag */
+ add_handle(handle, multi_inputs ? inputs->file : NULL);
+
+ if (no_date)
+ tracecmd_set_flag(handle, TRACECMD_FL_IGNORE_DATE);
+ if (raw_ts)
+ tracecmd_set_flag(handle, TRACECMD_FL_RAW_TS);
+ page_size = tracecmd_page_size(handle);
+
+ if (show_page_size) {
+ printf("file page size is %d, and host page size is %d\n",
+ page_size,
+ getpagesize());
+ return;
+ }
+
+ if (inputs->tsoffset)
+ tracecmd_set_ts_offset(handle, inputs->tsoffset);
+
+ if (inputs->ts2secs)
+ tracecmd_set_ts2secs(handle, inputs->ts2secs);
+ else if (ts2secs)
+ tracecmd_set_ts2secs(handle, ts2secs);
+
+ pevent = tracecmd_get_tep(handle);
+
+ if (nanosec)
+ tep_set_flag(pevent, TEP_NSEC_OUTPUT);
+
+ if (raw_format)
+ format_type = TEP_PRINT_INFO_RAW;
+
+ if (test_filters_mode)
+ tep_set_test_filters(pevent, 1);
+
+ if (functions)
+ add_functions(pevent, functions);
+
+ if (show_endian) {
+ printf("file is %s endian and host is %s endian\n",
+ tep_is_file_bigendian(pevent) ? "big" : "little",
+ tep_is_local_bigendian(pevent) ? "big" : "little");
+ return;
+ }
+
+ if (print_events) {
+ tracecmd_print_events(handle, NULL);
+ return;
+ }
+
+ if (print_event) {
+ tracecmd_print_events(handle, print_event);
+ return;
+ }
+
+ ret = tracecmd_read_headers(handle, 0);
+ if (check_event_parsing) {
+ if (ret || tracecmd_get_parsing_failures(handle))
+ exit(EINVAL);
+ else
+ exit(0);
+ } else {
+ if (ret)
+ return;
+ }
+
+ if (show_funcs) {
+ tep_print_funcs(pevent);
+ return;
+ }
+ if (show_printk) {
+ tep_print_printk(pevent);
+ return;
+ }
+
+ if (show_events) {
+ struct tep_event **events;
+ struct tep_event *event;
+ int i;
+
+ events = tep_list_events(pevent, TEP_EVENT_SORT_SYSTEM);
+ for (i = 0; events[i]; i++) {
+ event = events[i];
+ if (event->system)
+ printf("%s:", event->system);
+ printf("%s\n", event->name);
+ }
+ return;
+ }
+
+ if (show_cpus) {
+ int cpus;
+ int ret;
+ int i;
+
+ if (!tracecmd_is_buffer_instance(handle)) {
+ ret = tracecmd_init_data(handle);
+ if (ret < 0)
+ die("failed to init data");
+ }
+ cpus = tracecmd_cpus(handle);
+ printf("List of CPUs in %s with data:\n", inputs->file);
+ for (i = 0; i < cpus; i++) {
+ if (tracecmd_read_cpu_first(handle, i))
+ printf(" %d\n", i);
+ }
+ continue;
+ }
+
+ set_event_flags(pevent, nohandler_events, TEP_EVENT_FL_NOHANDLE);
+ set_event_flags(pevent, raw_events, TEP_EVENT_FL_PRINTRAW);
+ }
+
+ if (show_cpus)
+ return;
+
+ otype = OUTPUT_NORMAL;
+
+ if (tracecmd_get_flags(handle) & TRACECMD_FL_RAW_TS) {
+ tep_func_repeat_format = "%d";
+ } else if (tracecmd_get_flags(handle) & TRACECMD_FL_IN_USECS) {
+ if (tep_test_flag(tracecmd_get_tep(handle), TEP_NSEC_OUTPUT))
+ tep_func_repeat_format = "%9.1d";
+ else
+ tep_func_repeat_format = "%6.1000d";
+ } else {
+ tep_func_repeat_format = "%12d";
+ }
+
+
+ if (show_stat)
+ otype = OUTPUT_STAT_ONLY;
+ /* yeah yeah, uname overrides stat */
+ if (show_uname)
+ otype = OUTPUT_UNAME_ONLY;
+ /* and version overrides uname! */
+ if (show_version)
+ otype = OUTPUT_VERSION_ONLY;
+ read_data_info(&handle_list, otype, global, align_ts);
+
+ list_for_each_entry(handles, &handle_list, list) {
+ tracecmd_close(handles->handle);
+ }
+ free_handles();
+ free_inputs();
+
+ finish_wakeup();
+
+ return;
+}