#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 /* * Return the next switch_away record after @time. */ 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; } /* * Return 1 if the name of @eid is displayed in the plot. */ static inline int is_displayed(struct graph_info *ginfo, int eid) { struct rt_graph_info *rtg_info = &ginfo->rtg_info; return !(eid == rtg_info->switch_away_id || eid == rtg_info->switch_to_id || eid == rtg_info->task_completion_id || eid == rtg_info->task_block_id || eid == rtg_info->task_resume_id || eid == rtg_info->task_release_id || eid == ginfo->event_sched_switch_id); } static struct record* __find_record(struct graph_info *ginfo, int cpu, unsigned long long time, int display) { struct record *record; int eid, ignored; set_cpu_to_rts(ginfo, time, cpu); while ((record = tracecmd_read_data(ginfo->handle, cpu))) { ignored = 0; eid = pevent_data_type(ginfo->pevent, record); if (display) ignored = !is_displayed(ginfo, eid); else /* Without this ignore, the info popup is going * to be sched_switchs almost always. */ ignored = (eid == ginfo->event_sched_switch_id); if (get_rts(ginfo, record) >= time && !ignored) break; free_record(record); } return record; } /* * Return the first record after @time on @cpu. */ static inline struct record* find_record(struct graph_info *ginfo, int cpu, guint64 time) { return __find_record(ginfo, cpu, time, 0); } /* * Return the first _displayed_ record after @time on @cpu. */ static inline struct record* find_display_record(struct graph_info *ginfo, int cpu, guint64 time) { return __find_record(ginfo, cpu, time, 1); } /* * Update fields in @rtc_info for the new @pid. */ 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); } } /* * Get information about the given @time. * @out_pid: The running pid at @time * @out_job: The running job at @time * @out_record: The record at @time * * Return 1, @out_pid, and @out_job if the CPU is running at @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 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->rt_run_time && rtc_info->rt_run_time < ts && job != 1) { info->box = TRUE; info->bcolor = hash_pid(rtc_info->run_pid); info->bfill = TRUE; info->bstart = rtc_info->rt_run_time; info->bend = ts; info->blabel = rtc_info->label; } rtc_info->run_pid = 0; rtc_info->rt_run_time = 0Ull; rtc_info->reg_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->rt_run_time = ts; rtc_info->reg_run_time = 0ULL; } 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 int try_sched_switch(struct graph_info *ginfo, struct rt_cpu_info *rtc_info, struct record *record, struct plot_info *info) { const char *comm; int from_pid, to_pid, match; match = trace_graph_check_sched_switch(ginfo, record, &to_pid, &comm); if (match) { from_pid = pevent_data_pid(ginfo->pevent, record); /* Only draw if no real-time task is running */ if (!rtc_info->rt_run_time) { if (rtc_info->reg_run_time && rtc_info->reg_run_time < get_rts(ginfo, record)) { /* A non-rt task was running */ info->box = TRUE; info->bthin = TRUE; info->bcolor = 0x0; info->bstart = rtc_info->reg_run_time; info->bend = get_rts(ginfo, record); } if (to_pid) rtc_info->reg_run_time = get_rts(ginfo, record); else rtc_info->reg_run_time = 0ULL; } update_pid(rtc_info, to_pid); } 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->rt_run_time && rtc_info->run_pid) { info->box = TRUE; info->bcolor = hash_pid(rtc_info->run_pid); info->bfill = TRUE; info->bstart = rtc_info->rt_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 *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->rt_run_time = time; rtc_info->reg_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, dint; unsigned long long ts, dull; struct rt_cpu_info *rtc_info = plot->private; struct rt_graph_info *rtg_info = &ginfo->rtg_info; 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) || try_sched_switch(ginfo, rtc_info, record, info); if (!match) { /* Have to call checks to ensure ids are loaded. Otherwise, * is_displayed will not work here or in any other methods. */ #define ARG rtg_info, ginfo->pevent, record, &pid rt_graph_check_task_param(ARG, &dull, &dull); rt_graph_check_task_release(ARG, &dint, &dull, &dull); rt_graph_check_task_block(ARG, &dull); rt_graph_check_task_resume(ARG, &dull); rt_graph_check_any(ARG, &eid, &ts); #undef ARG if (is_displayed(ginfo, eid)) { 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_display_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 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\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_puts(s, event->name); trace_seq_putc(s, '\n'); pevent_event_info(s, event, record); trace_seq_putc(s, '\n'); if (!is_running) trace_seq_putc(s, '\n'); } else trace_seq_printf(s, "UNKNOWN EVENT %d\n", eid); } free_record(record); } 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); } /** * rt_plot_cpus_plotted - return the cpus plotted. */ 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; } /** * rt_plot_cpu - create a plot for @cpu. */ 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, "RT-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); } void rt_plot_init_cpus(struct graph_info *ginfo, int cpus) { long cpu; for (cpu = 0; cpu < cpus; cpu++) rt_plot_cpu(ginfo, cpu); }