diff options
Diffstat (limited to 'tracecmd/trace-read.c')
-rw-r--r-- | tracecmd/trace-read.c | 1984 |
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(®ex, 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(®ex, event->name, 0, NULL, 0) || + !regexec(®ex, 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; +} |