trace-graph: Move what to plot into cpu specific file

Move the decisions to what to plot into the cpu specific file.
Now the trace-graph.c can just manange the drawing.

The code to show the fly-over windows still needs to be made
generic.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
diff --git a/trace-graph.c b/trace-graph.c
index 7eaf506..6f31862 100644
--- a/trace-graph.c
+++ b/trace-graph.c
@@ -60,9 +60,6 @@
 static gint largest_plot_label = 0;
 
 static void redraw_pixmap_backend(struct graph_info *ginfo);
-static int check_sched_switch(struct graph_info *ginfo,
-			      struct record *record,
-			      gint *pid, const char **comm);
 
 static void convert_nano(unsigned long long time, unsigned long *sec,
 			 unsigned long *usec)
@@ -210,7 +207,7 @@
 	return event != NULL;
 }
 
-gboolean graph_filter_on_event(struct graph_info *ginfo, struct record *record)
+gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct record *record)
 {
 	struct event_format *event;
 	gint event_id;
@@ -235,7 +232,7 @@
 	return TRUE;
 }
 
-gboolean graph_filter_on_task(struct graph_info *ginfo, gint pid)
+gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid)
 {
 	gboolean filter;
 
@@ -536,7 +533,7 @@
 
 	if (record) {
 
-		if (!check_sched_switch(ginfo, record, &pid, &comm)) {
+		if (!trace_graph_check_sched_switch(ginfo, record, &pid, &comm)) {
 			pid = pevent_data_pid(ginfo->pevent, record);
 			comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
 		}
@@ -748,9 +745,9 @@
 
 #define PLOT_BOARDER 5
 
-static int check_sched_wakeup(struct graph_info *ginfo,
-			      struct record *record,
-			      gint *pid)
+int trace_graph_check_sched_wakeup(struct graph_info *ginfo,
+				   struct record *record,
+				   gint *pid)
 {
 	struct event_format *event;
 	unsigned long long val;
@@ -810,9 +807,9 @@
 	return 0;
 }
 
-static int check_sched_switch(struct graph_info *ginfo,
-			      struct record *record,
-			      gint *pid, const char **comm)
+int trace_graph_check_sched_switch(struct graph_info *ginfo,
+				   struct record *record,
+				   gint *pid, const char **comm)
 {
 	unsigned long long val;
 	struct event_format *event;
@@ -939,7 +936,7 @@
 				trace_seq_printf(&s, "UNKNOW EVENT %d\n", type);
 		} else {
 			if (record->ts < time)
-				check_sched_switch(ginfo, record, &pid, &comm);
+				trace_graph_check_sched_switch(ginfo, record, &pid, &comm);
 		}
 
 		trace_seq_printf(&s, "%lu.%06lu", sec, usec);
@@ -953,7 +950,7 @@
 	} else {
 		record = tracecmd_read_cpu_last(ginfo->handle, cpu);
 		if (record && record->ts < time) {
-			if (!check_sched_switch(ginfo, record, &pid, &comm)) {
+			if (!trace_graph_check_sched_switch(ginfo, record, &pid, &comm)) {
 				pid = pevent_data_pid(ginfo->pevent, record);
 				comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
 			}
@@ -1356,38 +1353,28 @@
 	return FALSE;
 }
 
-static gint hash_pid(gint val)
-{
-	/* idle always gets black */
-	if (!val)
-		return 0;
-
-	return trace_hash(val);
-}
-
-static void set_color_by_pid(GtkWidget *widget, GdkGC *gc, gint pid)
+static void set_color(GtkWidget *widget, GdkGC *gc, gint c)
 {
 	GdkColor color;
-	gint hash = hash_pid(pid);
 
-	color.red = (hash & 0xff)*(65535/255);
-	color.blue = ((hash >> 8) & 0xff)*(65535/255);
-	color.green = ((hash >> 16) & 0xff)*(65535/255);
+	color.red = (c & 0xff)*(65535/255);
+	color.blue = ((c >> 8) & 0xff)*(65535/255);
+	color.green = ((c >> 16) & 0xff)*(65535/255);
 	gdk_color_alloc(gtk_widget_get_colormap(widget), &color);
 	gdk_gc_set_foreground(gc, &color);
 }
 
-static void draw_event_label(struct graph_info *ginfo, gint cpu,
-			    gint event_id, gint pid,
+static void draw_event_label(struct graph_info *ginfo, gint i,
 			    gint p1, gint p2, gint p3,
 			    gint width_16, PangoFontDescription *font)
 {
-	struct event_format *event;
+	struct graph_plot *plot = ginfo->plot_array[i];
 	PangoLayout *layout;
 	struct trace_seq s;
 	gint text_width;
 	gint text_height;
 	gint x, y;
+	gint ret;
 
 
 	/* No room to print */
@@ -1398,12 +1385,18 @@
 
 	/* Check if we can show some data */
 
-	event = pevent_data_event_from_type(ginfo->pevent, event_id);
-
 	trace_seq_init(&s);
-	trace_seq_printf(&s, "%s-%d\n%s\n",
-			 pevent_data_comm_from_pid(ginfo->pevent, pid),
-			 pid, event->name);
+
+	/*
+	 * Display the event after p2 - 1. We use "-1" because we need to
+	 * find the event at this pixel, and due to rounding, p2 time can
+	 * be after the time of the event. Since tracecmd finds the next event
+	 * after the time, we use this to find our next event.
+	 */
+	ret = trace_graph_plot_display_last_event(ginfo, plot, &s,
+						  convert_x_to_time(ginfo, p2-1));
+	if (!ret)
+		return;
 
 	layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
 	pango_layout_set_font_description(layout, font);
@@ -1422,43 +1415,66 @@
 	if (x < 0)
 		x = 1;
 
-	y = (PLOT_TOP(cpu) - text_height + 5);
+	y = (PLOT_TOP(i) - text_height + 5);
 	gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
 			x, y, layout);
 
 
 	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
-		      p2, PLOT_TOP(cpu) - 5, p2, PLOT_TOP(cpu) - 1);
+		      p2, PLOT_TOP(i) - 5, p2, PLOT_TOP(i) - 1);
 
 	g_object_unref(layout);
 }
 
-static void draw_cpu(struct graph_info *ginfo, gint cpu,
-		     gint new_width, gboolean read_comms)
+static gint draw_plot_line(struct graph_info *ginfo, int i,
+			   unsigned long long time, GdkGC *gc)
+{
+	gint x;
+
+	x = convert_time_to_x(ginfo, time);
+
+	gdk_draw_line(ginfo->curr_pixmap, gc,
+		      x, PLOT_TOP(i), x, PLOT_BOTTOM(i));
+
+	return x;
+}
+
+static void draw_plot_box(struct graph_info *ginfo, int i,
+			  unsigned long long start,
+			  unsigned long long end, GdkGC *gc)
+{
+	gint x1;
+	gint x2;
+
+	x1 = convert_time_to_x(ginfo, start);
+	x2 = convert_time_to_x(ginfo, end);
+
+	gdk_draw_rectangle(ginfo->curr_pixmap, gc,
+			   TRUE,
+			   x1, PLOT_BOX_TOP(i),
+			   x2 - x1, PLOT_BOX_SIZE);
+}
+
+static void draw_plot(struct graph_info *ginfo, gint i,
+		      gint new_width, gboolean read_comms)
 {
 	static PangoFontDescription *font;
 	PangoLayout *layout;
-	gint height = PLOT_LINE(cpu);
-	struct record *record;
+	gint height = PLOT_LINE(i);
+	struct graph_plot *plot = ginfo->plot_array[i];
 	static GdkGC *gc;
 	static gint width_16;
 	guint64 ts;
-	gint last_pid = -1;
-	gint last_x = 0;
-	gint pid;
-	gint x;
+	gint lcolor;
+	gint bcolor;
+	gint last_color = -1;
+	gboolean box;
+	gboolean line;
+	unsigned long long ltime;
+	unsigned long long bstart;
+	unsigned long long bend;
 	gint p1 = 0, p2 = 0, p3 = 0;
-	gint last_event_id = 0;
-	gint wake_pid;
-	gint last_wake_pid;
-	gint event_id;
-	gint display_pid = -1;
-	gint next_display_pid = -1;
-	gboolean filter;
-	gboolean is_sched_switch;
-	gboolean is_wakeup;
-	gboolean last_is_wakeup = FALSE;
-	const char *comm;
+	gint x;
 
 	/* Calculate the size of 16 characters */
 	if (!width_16) {
@@ -1483,85 +1499,36 @@
 
 	ts = ginfo->view_start_time;
 
-	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, ts);
+	trace_graph_plot_start(ginfo, plot, ts);
 
-	while ((record = tracecmd_read_data(ginfo->handle, cpu))) {
+	set_color(ginfo->draw, gc, last_color);
 
-		if (record->ts < ginfo->view_start_time) {
-			free_record(record);
+	while (trace_graph_plot_event(ginfo, plot,
+				      &line, &lcolor, &ltime,
+				      &box, &bcolor, &bstart, &bend)) {
+
+		/* really should not happen */
+		if (!line && !box)
 			continue;
-		}
-		if (record->ts > ginfo->view_end_time)
-			break;
 
-		ts = record->ts - ginfo->view_start_time;
-
-		x = (gint)((gdouble)ts * ginfo->resolution);
-
-		is_sched_switch = FALSE;
-
-		if (check_sched_switch(ginfo, record, &pid, &comm)) {
-			is_sched_switch = TRUE;
-			if (ginfo->read_comms) {
-				/*
-				 * First time through, register any missing
-				 *  comm / pid mappings.
-				 */
-				if (!pevent_pid_is_registered(ginfo->pevent, pid))
-					pevent_register_comm(ginfo->pevent,
-							     strdup(comm), pid);
+		if (box) {
+			if (bcolor != last_color) {
+				last_color = bcolor;
+				set_color(ginfo->draw, gc, last_color);
 			}
-		} else
-			pid = pevent_data_pid(ginfo->pevent, record);
 
-		event_id = pevent_data_type(ginfo->pevent, record);
+			draw_plot_box(ginfo, i, bstart, bend, gc);
+		}
 
-		if (last_pid != pid) {
+		if (line) {
 
-			if (last_pid < 0) {
-				/* if we hit a sched switch, use the original pid */
-				if (is_sched_switch)
-					last_pid = pevent_data_pid(ginfo->pevent, record);
-				else
-					last_pid = pid;
-				set_color_by_pid(ginfo->draw, gc, last_pid);
+			if (lcolor != last_color) {
+				last_color = lcolor;
+				set_color(ginfo->draw, gc, last_color);
 			}
-				
-			filter = graph_filter_on_task(ginfo, last_pid);
 
-			if (!filter && last_pid)
+			x = draw_plot_line(ginfo, i, ltime, gc);
 
-				gdk_draw_rectangle(ginfo->curr_pixmap, gc,
-						   TRUE,
-						   last_x, PLOT_BOX_TOP(cpu),
-						   x - last_x, PLOT_BOX_SIZE);
-
-			last_x = x;
-
-			set_color_by_pid(ginfo->draw, gc, pid);
-		}
-
-		filter = graph_filter_on_task(ginfo, pid);
-
-		/* Also show the task switching out */
-		if (filter && is_sched_switch)
-			filter = graph_filter_on_task(ginfo, last_pid);
-
-		/* Lets see if a filtered task is waking up */
-		is_wakeup = check_sched_wakeup(ginfo, record, &wake_pid);
-		if (filter && is_wakeup)
-			filter = graph_filter_on_task(ginfo, wake_pid);
-
-		if (!filter) {
-			filter = graph_filter_on_event(ginfo, record);
-			if (!filter)
-				gdk_draw_line(ginfo->curr_pixmap, gc,
-					      x, PLOT_TOP(cpu), x, PLOT_BOTTOM(cpu));
-		}
-
-		last_pid = pid;
-
-		if (!filter) {
 			/* Figure out if we can show the text for the previous record */
 
 			p3 = x;
@@ -1570,45 +1537,41 @@
 			if (!p3)
 				p3 = 1;
 
-			display_pid = next_display_pid;
-
 			/* first record, continue */
 			if (p2)
-				draw_event_label(ginfo, cpu, last_event_id, display_pid,
+				draw_event_label(ginfo, i,
 						 p1, p2, p3, width_16, font);
 
 			p1 = p2;
 			p2 = p3;
+		}
+	}
 
-			last_event_id = event_id;
-			last_is_wakeup = is_wakeup;
-			last_wake_pid = wake_pid;
-
-			if (last_is_wakeup)
-				next_display_pid = last_wake_pid;
-			else
-				next_display_pid = pid;
+	if (box) {
+		if (bcolor != last_color) {
+			last_color = bcolor;
+			set_color(ginfo->draw, gc, last_color);
 		}
 
-		free_record(record);
+		draw_plot_box(ginfo, i, bstart, bend, gc);
 	}
 
-	if (p2 && next_display_pid >= 0)
-		draw_event_label(ginfo, cpu, last_event_id, next_display_pid,
+	if (line) {
+		if (lcolor != last_color) {
+			last_color = lcolor;
+			set_color(ginfo->draw, gc, last_color);
+		}
+
+		draw_plot_line(ginfo, i, ltime, gc);
+	}
+
+	if (p2)
+		draw_event_label(ginfo, i,
 				 p1, p2, ginfo->draw_width, width_16, font);
 
-	if (last_pid > 0 &&
-	    !graph_filter_on_task(ginfo, last_pid)) {
+	trace_graph_plot_end(ginfo, plot);
 
-		x = ginfo->draw_width;
-
-		gdk_draw_rectangle(ginfo->curr_pixmap, gc,
-				   TRUE,
-				   last_x, PLOT_BOX_TOP(cpu),
-				   x - last_x, PLOT_BOX_SIZE);
-	}
-
-	free_record(record);
+	return;
 }
 
 
@@ -1700,7 +1663,7 @@
 static void draw_info(struct graph_info *ginfo,
 		      gint new_width)
 {
-	gint cpu;
+	gint i;
 
 	if (!ginfo->handle)
 		return;
@@ -1713,8 +1676,8 @@
 	draw_timeline(ginfo, new_width);
 
 	
-	for (cpu = 0; cpu < ginfo->cpus; cpu++)
-		draw_cpu(ginfo, cpu, new_width, ginfo->read_comms);
+	for (i = 0; i < ginfo->plots; i++)
+		draw_plot(ginfo, i, new_width, ginfo->read_comms);
 
 	ginfo->read_comms = FALSE;
 }
diff --git a/trace-graph.h b/trace-graph.h
index 6035045..343a769 100644
--- a/trace-graph.h
+++ b/trace-graph.h
@@ -19,10 +19,49 @@
  *   Return true if a selected time should expose plot.
  *   Should only return true if an event has the exact time that
  *   is passed in.
+ *
+ * start:
+ *   Initialize for plotting. This is called with the start time
+ *   to start plotting.
+ *
+ * plot_event:
+ *   This is called by the plotter. Return 1 to plot an event or
+ *   0 to stop the plotting.
+ *   color returns the color that should be printed.
+ *   line returns 1 or 0 if a line should be drawn.
+ *   ltime returns the time that the line should be drawn at
+ *    (ignored if line is 0)
+ *   box returns 1 or 0 if a box should be drawn
+ *    bstart is the time the box starts at
+ *    bend is the time the box ends at
+ *     (bstart and bend are ignored if box is 0)
+ *   time is the time of the current event
+ *   (box and line can be true and processed even if the func returns 0)
+ *
+ * end:
+ *   called at the end of the plotting in case the plotter needs to
+ *   release any resourses.
+ * display_last_event:
+ *   If enough space between the event before and the event after
+ *   a event, the plot may ask to display that event.
+ *   The time will be given to find the event, the time may be before
+ *   the given event.
  */
 struct plot_callbacks {
 	int (*match_time)(struct graph_info *, struct graph_plot *,
 			  unsigned long long time);
+	void (*start)(struct graph_info *, struct graph_plot *,
+		      unsigned long long time);
+	int (*plot_event)(struct graph_info *ginfo,
+			  struct graph_plot *plot,
+			  gboolean *line, int *lcolor,
+			  unsigned long long *ltime,
+			  gboolean *box, int *bcolor,
+			  unsigned long long *bstart,
+			  unsigned long long *bend);
+	void (*end)(struct graph_info *, struct graph_plot *);
+	int (*display_last_event)(struct graph_info *ginfo, struct graph_plot *plot,
+				  struct trace_seq *s, unsigned long long time);
 };
 
 struct graph_plot {
@@ -160,6 +199,15 @@
 int trace_graph_load_handle(struct graph_info *ginfo,
 			    struct tracecmd_input *handle);
 
+int trace_graph_check_sched_switch(struct graph_info *ginfo,
+				   struct record *record,
+				   gint *pid, const char **comm);
+int trace_graph_check_sched_wakeup(struct graph_info *ginfo,
+				   struct record *record,
+				   gint *pid);
+gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid);
+gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct record *record);
+
 /* plots */
 void trace_graph_plot_free(struct graph_info *ginfo);
 void trace_graph_plot_init(struct graph_info *ginfo);
@@ -171,6 +219,25 @@
 				struct graph_plot *plot,
 				unsigned long long time);
 
+int trace_graph_plot_display_last_event(struct graph_info *ginfo,
+					struct graph_plot *plot,
+					struct trace_seq *s,
+					unsigned long long time);
+
+void trace_graph_plot_start(struct graph_info *ginfo,
+			    struct graph_plot *plot,
+			    unsigned long long time);
+
+int trace_graph_plot_event(struct graph_info *ginfo,
+			   struct graph_plot *plot,
+			   gboolean *line, int *lcolor,
+			   unsigned long long *ltime,
+			   gboolean *box, int *bcolor,
+			   unsigned long long *bstart,
+			   unsigned long long *bend);
+
+void trace_graph_plot_end(struct graph_info *ginfo,
+			  struct graph_plot *plot);
 
 /* cpu plot */
 void graph_plot_init_cpus(struct graph_info *ginfo, int cpus);
diff --git a/trace-plot-cpu.c b/trace-plot-cpu.c
index d855c2c..b1adb91 100644
--- a/trace-plot-cpu.c
+++ b/trace-plot-cpu.c
@@ -1,13 +1,32 @@
+#include <string.h>
+
 #include "trace-graph.h"
 
+struct cpu_plot_info {
+	int			cpu;
+	struct record		*record;
+	unsigned long long	last_time;
+	int			last_pid;
+};
+
+static gint hash_pid(gint val)
+{
+	/* idle always gets black */
+	if (!val)
+		return 0;
+
+	return trace_hash(val);
+}
+
 static int cpu_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot,
 			       unsigned long long time)
 {
+	struct cpu_plot_info *cpu_info = plot->private;
 	struct record *record;
 	long cpu;
 	int ret = 0;
 
-	cpu = (long)plot->private;
+	cpu = cpu_info->cpu;
 
 	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
 	record = tracecmd_read_data(ginfo->handle, cpu);
@@ -22,17 +41,260 @@
 	return ret;
 }
 
+/*
+ * Return 1 if we should skip record, otherwise 0
+ *
+ * @orig_pid gets the pid of the record
+ * @sched_pid gets the pid of the record or if the record is
+ *   a sched_switch, it gets the next task
+ *   If it is a wakeup, then sched_pid gets the task being woken
+ * @is_sched_switch returns 1 on context switch, otherwise 0
+ */
+static int filter_record(struct graph_info *ginfo,
+			 struct record *record,
+			 int *orig_pid, int *sched_pid,
+			 gboolean *sched_switch)
+{
+	gboolean is_sched_switch;
+	gboolean is_wakeup;
+	const char *comm;
+	int wake_pid;
+	int filter;
+
+	*orig_pid = pevent_data_pid(ginfo->pevent, record);
+
+	filter = trace_graph_filter_on_task(ginfo, *orig_pid);
+
+	if (trace_graph_check_sched_switch(ginfo, record, sched_pid, &comm)) {
+		is_sched_switch = TRUE;
+
+		/* Also show the task switching out */
+		if (filter)
+			filter = trace_graph_filter_on_task(ginfo, *sched_pid);
+
+		if (ginfo->read_comms) {
+			/*
+			 * First time through, register any missing
+			 *  comm / pid mappings.
+			 */
+			if (!pevent_pid_is_registered(ginfo->pevent, *sched_pid))
+				pevent_register_comm(ginfo->pevent,
+						     strdup(comm), *sched_pid);
+		}
+	} else
+		*sched_pid = *orig_pid;
+
+	if (filter) {
+		/* Lets see if a filtered task is waking up */
+		is_wakeup = trace_graph_check_sched_wakeup(ginfo, record, &wake_pid);
+		if (is_wakeup) {
+			filter = trace_graph_filter_on_task(ginfo, wake_pid);
+			if (!filter)
+				*sched_pid = wake_pid;
+		}
+	}
+
+	*sched_switch = is_sched_switch;
+	return filter;
+}
+
+static int cpu_plot_display_last_event(struct graph_info *ginfo,
+				       struct graph_plot *plot,
+				       struct trace_seq *s,
+				       unsigned long long time)
+{
+	struct cpu_plot_info *cpu_info = plot->private;
+	struct event_format *event;
+	struct record *record;
+	int cpu = cpu_info->cpu;
+	unsigned long long offset = 0;
+	gboolean is_sched_switch;
+	int sched_pid;
+	int pid;
+	int type;
+
+	/*
+	 * Get the next record so we know can save its offset and
+	 * reset the cursor, not to mess up the plotting
+	 */
+	record = tracecmd_peek_data(ginfo->handle, cpu);
+	if (record)
+		offset = record->offset;
+	/* Don't need to free a peek */
+
+	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
+
+	/* find the non filtered event */
+	while ((record = tracecmd_read_data(ginfo->handle, cpu))) {
+		if (!filter_record(ginfo, record, &pid, &sched_pid, &is_sched_switch) &&
+		    !trace_graph_filter_on_event(ginfo, record) &&
+		    record->ts >= time)
+			break;
+		free_record(record);
+	}
+
+	if (offset)
+		tracecmd_set_cursor(ginfo->handle, cpu, offset);
+
+	if (!record)
+		return 0;
+
+	/* Must have the record we want */
+	type = pevent_data_type(ginfo->pevent, record);
+	event = pevent_data_event_from_type(ginfo->pevent, type);
+	if (is_sched_switch)
+		pid = sched_pid;
+	trace_seq_printf(s, "%s-%d\n%s\n",
+			 pevent_data_comm_from_pid(ginfo->pevent, pid),
+			 pid, event->name);
+	free_record(record);
+
+	if (offset)
+		return 1;
+
+	/*
+	 * We need to stop the iterator, read last record.
+	 */
+	record = tracecmd_read_cpu_last(ginfo->handle, cpu);
+	free_record(record);
+
+	return 1;
+}
+
+static void cpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot,
+			   unsigned long long time)
+{
+	struct cpu_plot_info *cpu_info = plot->private;
+	struct record *last_record = NULL;
+	struct record *record;
+	int cpu;
+
+	cpu = cpu_info->cpu;
+	cpu_info->record = NULL;
+	cpu_info->last_time = 0ULL;
+	cpu_info->last_pid = -1;
+
+	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
+
+	while ((record = tracecmd_read_data(ginfo->handle, cpu))) {
+		if (record->ts >= time)
+			break;
+
+		free_record(last_record);
+		last_record = record;
+	}
+
+	free_record(record);
+	/* reset so the next record read is the first record */
+	if (last_record) {
+		record = tracecmd_read_at(ginfo->handle,
+					  last_record->offset,
+					  NULL);
+		free_record(record);
+		free_record(last_record);
+	} else
+		tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
+
+}
+
+static int cpu_plot_event(struct graph_info *ginfo,
+			  struct graph_plot *plot,
+			  gboolean *line, int *lcolor,
+			  unsigned long long *ltime,
+			  gboolean *box, int *bcolor,
+			  unsigned long long *bstart,
+			  unsigned long long *bend)
+{
+	struct cpu_plot_info *cpu_info = plot->private;
+	struct record *record;
+	int sched_pid;
+	int orig_pid;
+	int is_sched_switch;
+	int filter;
+	int box_filter;
+	int pid;
+	int cpu;
+	int ret = 1;
+
+	cpu = cpu_info->cpu;
+	record = tracecmd_read_data(ginfo->handle, cpu);
+
+	if (!record) {
+		/* Finish a box if the last record was not idle */
+		if (cpu_info->last_pid > 0) {
+			*box = TRUE;
+			*bstart = cpu_info->last_time;
+			*bend = ginfo->view_end_time;
+		}
+		return 0;
+	}
+
+	cpu = cpu_info->cpu;
+
+	filter = filter_record(ginfo, record, &orig_pid, &sched_pid, &is_sched_switch);
+
+	/* set pid to record, or next task on sched_switch */
+	pid = is_sched_switch ? sched_pid : orig_pid;
+
+	if (cpu_info->last_pid != pid) {
+
+		if (cpu_info->last_pid < 0) {
+			/* if we hit a sched switch, use the original pid for box*/
+			if (is_sched_switch)
+				cpu_info->last_pid = orig_pid;
+			else
+				cpu_info->last_pid = pid;
+		}
+
+		/* Box should always use the original pid (prev in sched_switch) */
+		box_filter = trace_graph_filter_on_task(ginfo, orig_pid);
+
+		if (!box_filter && cpu_info->last_pid) {
+			*bcolor = hash_pid(cpu_info->last_pid);
+			*box = TRUE;
+			*bstart = cpu_info->last_time;
+			*bend = record->ts;
+		}
+
+		cpu_info->last_time = record->ts;
+	}
+
+	if (!filter && !trace_graph_filter_on_event(ginfo, record)) {
+		*line = TRUE;
+		*ltime = record->ts;
+		*lcolor = hash_pid(pid);
+	}
+
+	cpu_info->last_pid = pid;
+
+	if (record->ts > ginfo->view_end_time)
+		ret = 0;
+
+	free_record(record);
+
+	return ret;
+}
+
+
 static const struct plot_callbacks cpu_plot_cb = {
-	.match_time		 = cpu_plot_match_time
+	.match_time		= cpu_plot_match_time,
+	.plot_event		= cpu_plot_event,
+	.start			= cpu_plot_start,
+	.display_last_event	= cpu_plot_display_last_event
 };
 
 void graph_plot_init_cpus(struct graph_info *ginfo, int cpus)
 {
+	struct cpu_plot_info *cpu_info;
 	char label[100];
 	long cpu;
 
 	for (cpu = 0; cpu < cpus; cpu++) {
+		cpu_info = malloc_or_die(sizeof(*cpu_info));
+		cpu_info->cpu = cpu;
+
 		snprintf(label, 100, "CPU %ld", cpu);
-		trace_graph_plot_append(ginfo, label, &cpu_plot_cb, (void *)cpu);
+
+		trace_graph_plot_append(ginfo, label, &cpu_plot_cb, cpu_info);
 	}
 }
diff --git a/trace-plot.c b/trace-plot.c
index 152ff05..7a3e5dd 100644
--- a/trace-plot.c
+++ b/trace-plot.c
@@ -73,3 +73,51 @@
 
 	return plot->cb->match_time(ginfo, plot, time);
 }
+
+void trace_graph_plot_start(struct graph_info *ginfo,
+			    struct graph_plot *plot,
+			    unsigned long long time)
+{
+	if (!plot->cb->start)
+		return;
+
+	return plot->cb->start(ginfo, plot, time);
+}
+
+int trace_graph_plot_event(struct graph_info *ginfo,
+			   struct graph_plot *plot,
+			   gboolean *line, int *lcolor,
+			   unsigned long long *ltime,
+			   gboolean *box, int *bcolor,
+			   unsigned long long *bstart,
+			   unsigned long long *bend)
+{
+	*line = FALSE;
+	*box = FALSE;
+
+	if (!plot->cb->plot_event)
+		return 0;
+
+	return plot->cb->plot_event(ginfo, plot, line, lcolor, ltime,
+				    box, bcolor, bstart, bend);
+}
+
+void trace_graph_plot_end(struct graph_info *ginfo,
+			  struct graph_plot *plot)
+{
+	if (!plot->cb->end)
+		return;
+
+	return plot->cb->end(ginfo, plot);
+}
+
+int trace_graph_plot_display_last_event(struct graph_info *ginfo,
+					struct graph_plot *plot,
+					struct trace_seq *s,
+					unsigned long long time)
+{
+	if (!plot->cb->display_last_event)
+		return 0;
+
+	return plot->cb->display_last_event(ginfo, plot, s, time);
+}