aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2016-02-11 10:14:06 -0500
committerSteven Rostedt <rostedt@goodmis.org>2016-02-18 20:11:11 -0500
commit853819899f74ac712cb30f31063a5d2d02106239 (patch)
tree848a0117bf41b46c6438cf4048e3ea9299cbbb4a
parentac21d5ebe38d8e1872a7159af8503ed55019c690 (diff)
downloadtrace-cmd-853819899f74ac712cb30f31063a5d2d02106239.tar.gz
kerenshark: Have task plots show when they are in interrupt context
Have the task plots display when they are in interrupt context. If two events together have their HARDIRQ flags set, then the box that fills them will be hollow. When the cursor is over that hollow box, it will display in the info box "(in interrupt)" to let users know why its hollow. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--trace-graph.c106
-rw-r--r--trace-graph.h14
-rw-r--r--trace-plot-task.c70
3 files changed, 187 insertions, 3 deletions
diff --git a/trace-graph.c b/trace-graph.c
index 736800bb..0711efdb 100644
--- a/trace-graph.c
+++ b/trace-graph.c
@@ -197,6 +197,11 @@ static void init_event_cache(struct graph_info *ginfo)
ginfo->event_wakeup_id = -1;
ginfo->event_wakeup_new_id = -1;
+ ginfo->hard_irq_entry_ids = NULL;
+ ginfo->hard_irq_exit_ids = NULL;
+ ginfo->soft_irq_entry_ids = NULL;
+ ginfo->soft_irq_exit_ids = NULL;
+
ginfo->event_pid_field = NULL;
ginfo->event_comm_field = NULL;
ginfo->ftrace_pid_field = NULL;
@@ -1144,6 +1149,97 @@ int trace_graph_check_sched_switch(struct graph_info *ginfo,
return ret;
}
+static void enter_id(gint **ids, gint id, gint *len)
+{
+ *ids = realloc(*ids, sizeof(gint) * (*len + 2));
+ (*ids)[*len] = id;
+ (*len)++;
+ (*ids)[*len] = -1;
+}
+
+enum graph_irq_type
+trace_graph_check_irq(struct graph_info *ginfo,
+ struct pevent_record *record)
+{
+ struct event_format *event;
+ struct event_format **events;
+ gint id;
+ int i;
+
+ if (!ginfo->hard_irq_entry_ids) {
+ gint hard_irq_entry_len = 0;
+ gint hard_irq_exit_len = 0;
+ gint soft_irq_entry_len = 0;
+ gint soft_irq_exit_len = 0;
+
+ event = pevent_find_event_by_name(ginfo->pevent,
+ NULL, "irq_handler_exit");
+ if (event)
+ enter_id(&ginfo->hard_irq_exit_ids, event->id,
+ &hard_irq_exit_len);
+
+ event = pevent_find_event_by_name(ginfo->pevent,
+ NULL, "irq_handler_entry");
+ if (event)
+ enter_id(&ginfo->hard_irq_entry_ids, event->id,
+ &hard_irq_entry_len);
+
+ event = pevent_find_event_by_name(ginfo->pevent,
+ NULL, "softirq_exit");
+ if (event)
+ enter_id(&ginfo->soft_irq_exit_ids, event->id,
+ &soft_irq_exit_len);
+
+ event = pevent_find_event_by_name(ginfo->pevent,
+ NULL, "softirq_entry");
+ if (event)
+ enter_id(&ginfo->soft_irq_entry_ids, event->id,
+ &soft_irq_entry_len);
+
+ events = pevent_list_events(ginfo->pevent, EVENT_SORT_SYSTEM);
+
+ for (i = 0; events[i]; i++) {
+ event = events[i];
+ if (strcmp(event->system, "irq_vectors") == 0)
+ break;
+ }
+
+ for (; events[i]; i++) {
+ event = events[i];
+ if (strcmp(event->system, "irq_vectors") != 0)
+ break;
+ if (strcmp(event->name + strlen(event->name) - 5,
+ "_exit") == 0)
+ enter_id(&ginfo->hard_irq_exit_ids, event->id,
+ &hard_irq_exit_len);
+ else if (strcmp(event->name + strlen(event->name) - 6,
+ "_entry") == 0)
+ enter_id(&ginfo->hard_irq_entry_ids, event->id,
+ &hard_irq_entry_len);
+ }
+ }
+
+ id = pevent_data_type(ginfo->pevent, record);
+
+ for (i = 0; ginfo->hard_irq_exit_ids[i] != -1; i++)
+ if (id == ginfo->hard_irq_exit_ids[i])
+ return GRAPH_HARDIRQ_EXIT;
+
+ for (i = 0; ginfo->hard_irq_entry_ids[i] != -1; i++)
+ if (id == ginfo->hard_irq_entry_ids[i])
+ return GRAPH_HARDIRQ_ENTRY;
+
+ for (i = 0; ginfo->soft_irq_exit_ids[i] != -1; i++)
+ if (id == ginfo->soft_irq_exit_ids[i])
+ return GRAPH_SOFTIRQ_EXIT;
+
+ for (i = 0; ginfo->soft_irq_entry_ids[i] != -1; i++)
+ if (id == ginfo->soft_irq_entry_ids[i])
+ return GRAPH_SOFTIRQ_ENTRY;
+
+ return GRAPH_IRQ_NONE;
+}
+
static void draw_info_box(struct graph_info *ginfo, const gchar *buffer,
gint x, gint y)
{
@@ -2418,6 +2514,16 @@ void trace_graph_free_info(struct graph_info *ginfo)
ginfo->cursor = 0;
}
ginfo->handle = NULL;
+
+ free(ginfo->hard_irq_entry_ids);
+ free(ginfo->hard_irq_exit_ids);
+ free(ginfo->soft_irq_entry_ids);
+ free(ginfo->soft_irq_exit_ids);
+
+ ginfo->hard_irq_entry_ids = NULL;
+ ginfo->hard_irq_exit_ids = NULL;
+ ginfo->soft_irq_entry_ids = NULL;
+ ginfo->soft_irq_exit_ids = NULL;
}
static int load_handle(struct graph_info *ginfo,
diff --git a/trace-graph.h b/trace-graph.h
index 8ea0514e..bc76e50f 100644
--- a/trace-graph.h
+++ b/trace-graph.h
@@ -39,6 +39,14 @@ enum graph_plot_type {
PLOT_TYPE_TASK,
};
+enum graph_irq_type {
+ GRAPH_IRQ_NONE,
+ GRAPH_HARDIRQ_ENTRY,
+ GRAPH_HARDIRQ_EXIT,
+ GRAPH_SOFTIRQ_ENTRY,
+ GRAPH_SOFTIRQ_EXIT
+};
+
struct graph_plot;
struct plot_info {
@@ -222,6 +230,10 @@ struct graph_info {
gint event_sched_switch_id;
gint event_wakeup_id;
gint event_wakeup_new_id;
+ gint *hard_irq_entry_ids;
+ gint *hard_irq_exit_ids;
+ gint *soft_irq_entry_ids;
+ gint *soft_irq_exit_ids;
struct format_field *event_prev_state;
struct format_field *event_pid_field;
struct format_field *event_comm_field;
@@ -302,6 +314,8 @@ int trace_graph_check_sched_switch(struct graph_info *ginfo,
int trace_graph_check_sched_wakeup(struct graph_info *ginfo,
struct pevent_record *record,
gint *pid);
+enum graph_irq_type trace_graph_check_irq(struct graph_info *ginfo,
+ struct pevent_record *record);
gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid);
gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct pevent_record *record);
diff --git a/trace-plot-task.c b/trace-plot-task.c
index b6c6fadf..4343a4c1 100644
--- a/trace-plot-task.c
+++ b/trace-plot-task.c
@@ -34,6 +34,7 @@ struct task_plot_info {
unsigned long long display_wake_time;
int wake_color;
int last_cpu;
+ gboolean in_irq;
};
static void convert_nano(unsigned long long time, unsigned long *sec,
@@ -328,6 +329,34 @@ static void task_plot_start(struct graph_info *ginfo, struct graph_plot *plot,
task_info->last_cpu = -1;
task_info->wake_time = 0ULL;
task_info->display_wake_time = 0ULL;
+ task_info->in_irq = FALSE;
+}
+
+static gboolean record_is_interrupt(struct graph_info *ginfo,
+ struct pevent_record *record,
+ gboolean check_type)
+{
+ gboolean in_irq;
+
+ in_irq = !!(pevent_data_flags(ginfo->pevent, record) &
+ (TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ));
+
+ /*
+ * An irq exit event can also cause us to exit irq
+ * even if the next event is an irq.
+ * Treat exiting irqs (hard and soft) as non interrupts.
+ */
+ if (check_type && in_irq) {
+ switch (trace_graph_check_irq(ginfo, record)) {
+ case GRAPH_HARDIRQ_EXIT:
+ case GRAPH_SOFTIRQ_EXIT:
+ in_irq = FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+ return in_irq;
}
static void update_last_record(struct graph_info *ginfo,
@@ -404,6 +433,7 @@ static void update_last_record(struct graph_info *ginfo,
task_info->last_records[cpu] = trecord;
task_info->last_cpu = trecord->cpu;
task_info->last_time = trecord->ts;
+ task_info->in_irq = record_is_interrupt(ginfo, trecord, TRUE);
break;
}
@@ -417,7 +447,9 @@ static int task_plot_event(struct graph_info *ginfo,
{
struct task_plot_info *task_info = plot->private;
struct plot_info *info = &plot->info;
+ struct pevent_record *next_record;
gboolean match;
+ gboolean in_irq;
int sched_pid;
int rec_pid;
int is_wakeup;
@@ -435,6 +467,8 @@ static int task_plot_event(struct graph_info *ginfo,
info->bstart = task_info->last_time;
info->bend = ginfo->view_end_time;
info->bcolor = hash_cpu(task_info->last_cpu);
+ info->bfill = !task_info->in_irq;
+ task_info->in_irq = FALSE;
}
for (cpu = 0; cpu < ginfo->cpus; cpu++) {
free_record(task_info->last_records[cpu]);
@@ -446,7 +480,6 @@ static int task_plot_event(struct graph_info *ginfo,
match = record_matches_pid(ginfo, record, pid, &rec_pid,
&sched_pid, &is_sched, &is_wakeup);
-
if (!match && record->cpu != task_info->last_cpu) {
if (!task_info->last_records[record->cpu]) {
task_info->last_records[record->cpu] = record;
@@ -489,6 +522,14 @@ static int task_plot_event(struct graph_info *ginfo,
return 1;
}
+ in_irq = record_is_interrupt(ginfo, record, TRUE);
+
+ /* It takes two events to be in an irq */
+ if (in_irq) {
+ next_record = tracecmd_peek_data(ginfo->handle, record->cpu);
+ in_irq = record_is_interrupt(ginfo, next_record, FALSE);
+ }
+
if (task_info->last_cpu != record->cpu) {
if (task_info->last_cpu >= 0) {
/* Switched CPUs */
@@ -496,6 +537,7 @@ static int task_plot_event(struct graph_info *ginfo,
info->bcolor = hash_cpu(task_info->last_cpu);
info->bstart = task_info->last_time;
info->bend = record->ts;
+ info->bfill = !task_info->in_irq;
}
task_info->last_time = record->ts;
}
@@ -521,16 +563,30 @@ static int task_plot_event(struct graph_info *ginfo,
info->bcolor = hash_cpu(task_info->last_cpu);
info->bstart = task_info->last_time;
info->bend = record->ts;
+ info->bfill = !task_info->in_irq;
task_info->last_cpu = -1;
if (is_running(ginfo, record)) {
task_info->wake_time = record->ts;
task_info->wake_color = RED;
} else
task_info->wake_time = 0;
- } else
+ } else {
task_info->wake_time = 0;
- } else
+ task_info->in_irq = in_irq;
+ }
+ } else {
+ /* Hollow out when we are in an irq */
+ if (task_info->in_irq != in_irq) {
+ info->box = TRUE;
+ info->bcolor = hash_cpu(task_info->last_cpu);
+ info->bstart = task_info->last_time;
+ info->bend = record->ts;
+ info->bfill = !task_info->in_irq;
+ task_info->last_time = record->ts;
+ }
task_info->wake_time = 0;
+ task_info->in_irq = in_irq;
+ }
return 1;
}
@@ -726,6 +782,14 @@ int task_plot_display_info(struct graph_info *ginfo,
if (pid == task_info->pid || sched_pid == task_info->pid)
trace_seq_printf(s, " CPU: %03d", cpu);
+ if (record_is_interrupt(ginfo, record, TRUE)) {
+ struct pevent_record *next_record;
+
+ next_record = tracecmd_peek_data(ginfo->handle, record->cpu);
+ if (record_is_interrupt(ginfo, next_record, FALSE))
+ trace_seq_puts(s, "\n(in interrupt)");
+ }
+
free_record(record);
return 1;