From 11c9b6cac9c5cfb72ecaa88d6f5ab0c6cd0034ae Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 8 Mar 2012 20:23:53 -0500 Subject: rt-graph: rt cpus have all basic features implemented --- rt-plot-cpu.c | 461 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rt-plot-cpu.h | 20 +++ rt-plot-task.c | 16 +- trace-graph.h | 6 + 4 files changed, 491 insertions(+), 12 deletions(-) create mode 100644 rt-plot-cpu.c create mode 100644 rt-plot-cpu.h diff --git a/rt-plot-cpu.c b/rt-plot-cpu.c new file mode 100644 index 0000000..6884286 --- /dev/null +++ b/rt-plot-cpu.c @@ -0,0 +1,461 @@ +#include "trace-graph.h" +#include "cpu.h" + +#define DEBUG_LEVEL 0 +#if DEBUG_LEVEL > 0 +#define dprintf(l, x...) \ + do { \ + if (l <= DEBUG_LEVEL) \ + printf(x); \ + } while (0) +#else +#define dprintf(l, x...) do { if (0) printf(x); } while (0) +#endif + +static struct record* +next_sa_record(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, + unsigned long long time, int *out_pid) +{ + struct pevent *pevent; + struct record *ret = NULL, *record; + struct rt_graph_info *rtg_info = &ginfo->rtg_info; + unsigned long long max_ts, dull; + int pid, dint, match; + + max_ts = time + SEARCH_PERIODS * rtg_info->max_period; + pevent = ginfo->pevent; + + set_cpu_to_rts(ginfo, time, rtc_info->cpu); + + while ((record = tracecmd_read_data(ginfo->handle, rtc_info->cpu))) { + if (get_rts(ginfo, record) > max_ts) { + free_record(record); + break; + } + match = rt_graph_check_switch_away(rtg_info, pevent, record, + &pid, &dint, &dull); + if (match) { + ret = record; + *out_pid = pid; + break; + } + free_record(record); + } + return ret; +} + +static struct record* +find_record(struct graph_info *ginfo, int cpu, unsigned long long time) +{ + struct record *record = NULL; + set_cpu_to_rts(ginfo, time, cpu); + + while ((record = tracecmd_read_data(ginfo->handle, cpu))) { + if (get_rts(ginfo, record) >= time) + break; + free_record(record); + } + return record; +} + + +static void update_pid(struct rt_cpu_info *rtc_info, int pid) +{ + rtc_info->fresh = FALSE; + if (pid != rtc_info->run_pid) { + rtc_info->run_pid = pid; + snprintf(rtc_info->label, LLABEL, "%d", rtc_info->run_pid); + } +} + +static int +try_switch_away(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, + struct record *record, struct plot_info *info) +{ + int job, pid, match; + unsigned long long ts; + + match = rt_graph_check_switch_away(&ginfo->rtg_info, ginfo->pevent, + record, &pid, &job, &ts); + if (match) { + update_pid(rtc_info, pid); + if (rtc_info->run_time && rtc_info->run_time < ts) { + info->box = TRUE; + info->bcolor = hash_pid(rtc_info->run_pid); + info->bfill = TRUE; + info->bstart = rtc_info->run_time; + info->bend = ts; + info->blabel = rtc_info->label; + } + rtc_info->run_pid = 0; + rtc_info->run_time = 0Ull; + } + return match; +} + +static int +try_switch_to(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, + struct record *record, struct plot_info *info) +{ + int job, pid, match; + unsigned long long ts; + + match = rt_graph_check_switch_to(&ginfo->rtg_info, ginfo->pevent, + record, &pid, &job, &ts); + if (match) { + update_pid(rtc_info, pid); + rtc_info->run_time = ts; + } + return match; +} + +static int +try_completion(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, + struct record *record, struct plot_info *info) +{ + int pid, job, match; + unsigned long long ts; + + match = rt_graph_check_task_completion(&ginfo->rtg_info, ginfo->pevent, + record, &pid, &job, &ts); + if (match) { + info->completion = TRUE; + info->ctime = ts; + } + return match; +} + +static void do_plot_end(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, + struct plot_info *info) +{ + int pid; + struct record *record; + + if (rtc_info->run_time && rtc_info->run_pid) { + info->box = TRUE; + info->bcolor = hash_pid(rtc_info->run_pid); + info->bfill = TRUE; + info->bstart = rtc_info->run_time; + info->bend = ginfo->view_end_time; + info->blabel = rtc_info->label; + rtc_info->fresh = FALSE; + } else if (rtc_info->fresh) { + record = next_sa_record(ginfo, rtc_info, + ginfo->view_end_time, + &pid); + if (record) { + update_pid(rtc_info, pid); + info->box = TRUE; + info->bcolor = hash_pid(pid); + info->bfill = TRUE; + info->blabel = rtc_info->label; + info->bstart = ginfo->view_start_time; + info->bend = ginfo->view_end_time; + rtc_info->fresh = FALSE; + } + free_record(record); + } +} + +static int rt_cpu_plot_match_time(struct graph_info *ginfo, + struct graph_plot *plot, + unsigned long long time) +{ + int ret = 0; + struct rt_cpu_info *rtc_info = plot->private; + struct record = find_record(ginfo, rtc_info->cpu, time); + + if (record && get_rts(ginfo, record) == time) + ret = 1; + free_record(record); + + return ret; +} + +static void rt_cpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot, + unsigned long long time) +{ + struct rt_cpu_info *rtc_info = plot->private; + + rtc_info->run_time = time; + rtc_info->run_pid = 0; + rtc_info->fresh = TRUE; +} + +static int rt_cpu_plot_event(struct graph_info *ginfo, struct graph_plot *plot, + struct record *record, struct plot_info *info) +{ + int pid, eid, match; + unsigned long long ts; + struct rt_cpu_info *rtc_info = plot->private; + struct rt_graph_info *rtg_info = &ginfo->rtg_info; + const char *comm; + + if (!record) { + do_plot_end(ginfo, rtc_info, info); + return 0; + } + + if (record->cpu != rtc_info->cpu) + return 0; + + match = try_switch_away(ginfo, rtc_info, record, info) || + try_switch_to(ginfo, rtc_info, record, info) || + try_completion(ginfo, rtc_info, record, info) || + trace_graph_check_sched_switch(ginfo, record, + &pid, &comm); + if (!match) { + rt_graph_check_any(rtg_info, ginfo->pevent, record, + &pid, &eid, &ts); + info->line = TRUE; + info->lcolor = hash_pid(pid); + info->ltime = ts; + } + return 1; +} + +static int +rt_cpu_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, + struct trace_seq *s, unsigned long long time) +{ + struct rt_cpu_info *rtc_info = plot->private; + struct event_format *event; + struct record *record; + unsigned long long offset; + int eid, cpu; + + cpu = rtc_info->cpu; + record = tracecmd_peek_data(ginfo->handle, cpu); + if (record) + offset = record->offset; + + record = find_record(ginfo, cpu, time); + + if (offset) + tracecmd_set_cursor(ginfo->handle, cpu, offset); + if (!record) + return 0; + + eid = pevent_data_type(ginfo->pevent, record); + event = pevent_data_event_from_type(ginfo->pevent, eid); + if (event) { + trace_seq_puts(s, event->name); + trace_seq_printf(s, "\n"); /* Doesn't work otherwise */ + } else + trace_seq_printf(s, "UNKNOWN EVENT %d\n", eid); + free_record(record); + + return 1; +} + + +struct record* +rt_cpu_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, + unsigned long long time) +{ + struct rt_cpu_info *rtc_info = plot->private; + return find_record(ginfo, rtc_info->cpu, time); +} + +static int get_time_info(struct graph_info *ginfo, + struct rt_cpu_info *rtc_info, + unsigned long long time, + int *out_pid, int *out_job, + struct record **out_record) +{ + struct record *record; + struct rt_graph_info *rtg_info = &ginfo->rtg_info; + unsigned long long dull, max_ts; + int cpu, is_running, pid, job; + + cpu = rtc_info->cpu; + *out_pid = *out_job = is_running = 0; + + record = find_record(ginfo, cpu, time); + *out_record = record; + if (!record) + goto out; + + max_ts = time + SEARCH_PERIODS * rtg_info->max_period; + do { + if (get_rts(ginfo, record) > max_ts) + break; + +#define ARG rtg_info, ginfo->pevent, record, &pid, &job, &dull + if (rt_graph_check_switch_to(ARG)) { + /* Nothing is running */ + goto out; + } else if (rt_graph_check_switch_away(ARG)) { + is_running = 1; + *out_pid = pid; + *out_job = job; + goto out; + } + if (*out_record != record) + free_record(record); +#undef ARG + } while ((record = tracecmd_read_data(ginfo->handle, cpu))); + out: + if (*out_record != record) + free_record(record); + return is_running; +} + +static int +rt_cpu_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, + struct trace_seq *s, unsigned long long time) +{ + struct rt_cpu_info *rtc_info = plot->private; + unsigned long long msec, nsec, rts; + int pid, eid, job, is_running; + struct record *record; + struct event_format *event; + const char *comm; + + is_running = get_time_info(ginfo, rtc_info, time, + &pid, &job, &record); + + if (is_running) { + comm = pevent_data_comm_from_pid(ginfo->pevent, pid); + trace_seq_printf(s, "%s-%d:%d\n", comm, pid, job); + } + + if (record) { + rts = get_rts(ginfo, record); + if (in_res(ginfo, rts, time)) { + eid = pevent_data_type(ginfo->pevent, record); + event = pevent_data_event_from_type(ginfo->pevent, eid); + if (event) { + trace_seq_putc(s, '\n'); + trace_seq_puts(s, event->name); + trace_seq_putc(s, '\n'); + pevent_event_info(s, event, record); + } else + trace_seq_printf(s, "\nUNKNOWN EVENT %d\n", eid); + } + free_record(record); + } + trace_seq_putc(s, '\n'); + nano_to_milli(time, &msec, &nsec); + trace_seq_printf(s, "%llu.%06llu ms CPU: %03d", + msec, nsec, rtc_info->cpu); + + return 1; +} + +static void rt_cpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) +{ + struct rt_cpu_info *rtc_info = plot->private; + + trace_graph_plot_remove_all_recs(ginfo, plot); + free(rtc_info->label); + free(rtc_info); +} + + +static const struct plot_callbacks rt_cpu_cb = { + .start = rt_cpu_plot_start, + .destroy = rt_cpu_plot_destroy, + .plot_event = rt_cpu_plot_event, + .display_last_event = rt_cpu_plot_display_last_event, + .display_info = rt_cpu_plot_display_info, + .match_time = rt_cpu_plot_match_time, + .find_record = rt_cpu_plot_find_record, +}; + +void rt_plot_cpus_update_callback(gboolean accept, + gboolean all_cpus, + guint64 *selected_cpu_mask, + gpointer data) +{ + struct graph_info *ginfo = data; + struct rt_cpu_info *rtc_info; + struct graph_plot *plot; + gboolean old_all_cpus; + guint64 *old_cpu_mask; + int i; + + if (!accept) + return; + + /* Get the current status */ + rt_plot_cpus_plotted(ginfo, &old_all_cpus, &old_cpu_mask); + + if (old_all_cpus == all_cpus || + (selected_cpu_mask && + cpus_equal(old_cpu_mask, selected_cpu_mask, ginfo->cpus))) { + /* Nothing to do */ + g_free(old_cpu_mask); + return; + } + + if (!all_cpus) { + /* + * Remove any plots not selected. + * Go backwards, since removing a plot shifts the + * array from current position back. + */ + for (i = ginfo->plots - 1; i >= 0; i--) { + plot = ginfo->plot_array[i]; + if (plot->type != PLOT_TYPE_CPU) + continue; + rtc_info = plot->private; + if (!cpu_isset(selected_cpu_mask, rtc_info->cpu)) + trace_graph_plot_remove(ginfo, plot); + } + } + + /* Now add any plots not set */ + for (i = 0; i < ginfo->cpus; i++) { + if (!all_cpus && !cpu_isset(selected_cpu_mask, i)) + continue; + if (cpu_isset(old_cpu_mask, i)) + continue; + rt_plot_cpu(ginfo, i); + } + + g_free(old_cpu_mask); + + trace_graph_refresh(ginfo); +} + +void rt_plot_cpus_plotted(struct graph_info *ginfo, + gboolean *all_cpus, guint64 **cpu_mask) +{ + struct rt_cpu_info *rtc_info; + struct graph_plot *plot; + int i; + + *cpu_mask = g_new0(guint64, (ginfo->cpus >> 6) + 1); + g_assert(*cpu_mask); + + for (i = 0; i < ginfo->plots; i++) { + plot = ginfo->plot_array[i]; + if (plot->type != PLOT_TYPE_RT_CPU) + continue; + rtc_info = plot->private; + cpu_set(*cpu_mask, rtc_info->cpu); + } + + *all_cpus = cpu_weight(*cpu_mask, ginfo->cpus) == ginfo->cpus ? + TRUE : FALSE; +} + + +void rt_plot_cpu(struct graph_info *ginfo, int cpu) +{ + struct rt_cpu_info *rtc_info; + struct graph_plot *plot; + char label[100]; + + rtc_info = malloc_or_die(sizeof(*rtc_info)); + memset(rtc_info, 0, sizeof(*rtc_info)); + rtc_info->cpu = cpu; + rtc_info->label = malloc_or_die(LLABEL); + + snprintf(label, 100, "*CPU %d", cpu); + + plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_RT_CPU, + TIME_TYPE_RT, &rt_cpu_cb, rtc_info); + trace_graph_plot_add_all_recs(ginfo, plot); +} diff --git a/rt-plot-cpu.h b/rt-plot-cpu.h new file mode 100644 index 0000000..320bf53 --- /dev/null +++ b/rt-plot-cpu.h @@ -0,0 +1,20 @@ +#ifndef __RT_PLOT_CPU_H +#define __RT_PLOT_CPU_H + +struct rt_cpu_info { + int cpu; + unsigned long long run_time; + int run_pid; + gboolean fresh; + char *label; +}; + +void rt_plot_cpu(struct graph_info *ginfo, int cpu); +void rt_plot_cpus_plotted(struct graph_info *ginfo, + gboolean *all_cpus, guint64 **cpu_mask); +void rt_plot_cpus_update_callback(gboolean accept, + gboolean all_cpus, + guint64 *selected_cpu_mask, + gpointer data); + +#endif diff --git a/rt-plot-task.c b/rt-plot-task.c index 48bcdde..f8d29ed 100644 --- a/rt-plot-task.c +++ b/rt-plot-task.c @@ -93,11 +93,8 @@ __find_record(struct graph_info *ginfo, gint pid, guint64 time, int display) struct rt_graph_info *rtg_info = &ginfo->rtg_info; set_cpus_to_rts(ginfo, time); - do { + while ((record = tracecmd_read_next_data(ginfo->handle, &next_cpu))) { free_record(record); - record = tracecmd_read_next_data(ginfo->handle, &next_cpu); - if (!record) - return NULL; match = record_matches_pid(ginfo, record, pid); if (display) { eid = pevent_data_type(ginfo->pevent, record); @@ -109,7 +106,9 @@ __find_record(struct graph_info *ginfo, gint pid, guint64 time, int display) eid == rtg_info->task_release_id); } ignored = ignored && eid == ginfo->event_sched_switch_id; - } while (!(get_rts(ginfo, record) > time && match && !ignored)); + if (get_rts(ginfo, record) >= time && match && !ignored) + break; + }; return record; } @@ -593,13 +592,6 @@ static int rt_task_plot_display_last_event(struct graph_info *ginfo, return 1; } -static inline int in_res(struct graph_info *ginfo, unsigned long long time, - unsigned long target) -{ - return time > target - 2/ginfo->resolution && - time < target + 2/ginfo->resolution; -} - static int rt_task_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, diff --git a/trace-graph.h b/trace-graph.h index ffd7cf5..33bdde2 100644 --- a/trace-graph.h +++ b/trace-graph.h @@ -433,5 +433,11 @@ static inline int hash_cpu(int cpu) return trace_hash(cpu); } +static inline int in_res(struct graph_info *ginfo, unsigned long long time, + unsigned long target) +{ + return time > target - 2/ginfo->resolution && + time < target + 2/ginfo->resolution; +} #endif /* _TRACE_GRAPH_H */ -- cgit v1.2.2