aboutsummaryrefslogblamecommitdiffstats
path: root/trace-graph.c
blob: 1f5e4cf2170d4407f9da20b3b7a1be43c96a5341 (plain) (tree)



























                                                                             
                         








                           
                             




                                              
                                          
 


                                        






                                                                     







                                               






















                                                                                   

                                                                 
 

                                                              








                                                                           

                               
 
                                  
                          
 
                                           
 
                                  
 
                    



                                                                        
               

                          

                    
 
                                                                         

 























                                                                                 



















































                                                                                  














                                                                             
                           






















                                                                                         



                                                



                                                                         










                                                                               

                                                        
                                                                           

                                                    

                                                              



























































































                                                                                     



                                                       
                                                         



                                            








                                                                


























                                                                          



                                                                          
                             
                           


                                           



                              



                                                                       
                                                               

                                            
 
                                     

                                                   
                                         









                                                                            











                                                                     

                                              



















































                                                                              



                                                                   




                                                                               


                                                                                        


















                                                                                   




                                                                           
                        
                           
                          



                                           




                                                               



                                                                
 
                              

                                                   
                                         
 

                                         
                                                            


                                      























                                                                                      

                                                    




                                                                               

                                                                                        





                                                                  

 




                                                                             


                                               
                                               

                                               
                                                
                                                
                                                           
                                            


                                                                         

         


























                                                                    

















































                                                                         
                                                        
                                    
 

                                          


                                      
                             
                   
                           
                        

                 

















                                                                             











                                                                       
 






                                                            



                                                                     

                                                                   
                                      



                                               











                                                                      




                                                                                      
















                                                                                


                             


                                                                    











                                                      












                                                               
                        







                                                                          
                        

















                                                                         
                                        
                                                          






                                                                         
                                              



                                      
                                                        






                                                                         
                                                            

                               
 

                                                               
 


                                                                        
 













                                                                                 

 

                                               









                                                                                   
                                                







                                                                        
 
                                                                   

 

                                     



                                                             
                                                           

                                                                      








                                                      
                                                   












                                                                                           


                               
                                                      
                                                                 


                   


                                                                  
















                                                   
                                                                                















                                                    


                                                                     


                                                                    



























































































































































                                                                                             
                                                                           


























                                                                             
/*
 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License (not later!)
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <gnome.h>
#include <gtk/gtk.h>

#include "trace-compat.h"
#include "trace-cmd.h"
#include "trace-local.h"
#include "trace-graph.h"

#define version "0.1.1"

#define TRACE_WIDTH	800
#define TRACE_HEIGHT	600

#define MAX_WIDTH	10000
#define input_file "trace.dat"

#define CPU_MIDDLE(cpu) (80 * (cpu) + 80)
#define CPU_TOP(cpu) (CPU_MIDDLE(cpu) - 10)
#define CPU_BOTTOM(cpu) (CPU_MIDDLE(cpu) + 10)
#define CPU_SPACE(cpus) (80 * (cpus) + 80)

static gint ftrace_sched_switch_id = -1;
static gint event_sched_switch_id = -1;

static void convert_nano(unsigned long long time, unsigned long *sec,
			 unsigned long *usec)
{
	*sec = time / 1000000000ULL;
	*usec = (time / 1000) % 1000000;
}

static void print_time(unsigned long long time)
{
	unsigned long sec, usec;

	convert_nano(time, &sec, &usec);
	printf("%lu.%06lu", sec, usec);
}

static void update_with_backend(struct graph_info *ginfo,
				gint x, gint y,
				gint width, gint height)
{
	gdk_draw_drawable(ginfo->draw->window,
			  ginfo->draw->style->fg_gc[GTK_WIDGET_STATE(ginfo->draw)],
			  ginfo->curr_pixmap,
			  x, y, x, y,
			  width, height);
}

static gboolean
expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
	struct graph_info *ginfo = data;

	update_with_backend(ginfo,
			    event->area.x, event->area.y,
			    event->area.width, event->area.height);

	return FALSE;
}

static void
draw_line(GtkWidget *widget, gdouble x, struct graph_info *ginfo)
{
	gdk_draw_line(widget->window, widget->style->black_gc,
		      x, 0, x, widget->allocation.width);
}

static gboolean
button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
	struct graph_info *ginfo = data;

	printf("button = %d\n", event->button);

	if (event->button != 1)
		return TRUE;

	ginfo->press_x = event->x;
	ginfo->last_x = 0;

	draw_line(widget, event->x, ginfo);

	ginfo->line_active = TRUE;

	return TRUE;
}

static void clear_last_line(GtkWidget *widget, struct graph_info *ginfo)
{
	gint x;

	x = ginfo->last_x;
	if (x)
		x--;

	update_with_backend(ginfo, x, 0, x+2, widget->allocation.height);
}

static void print_rec_info(struct record *record, struct pevent *pevent, int cpu)
{
	struct trace_seq s;
	struct event *event;
	unsigned long sec, usec;
	gint type;

	trace_seq_init(&s);

	convert_nano(record->ts, &sec, &usec);
	trace_seq_printf(&s, "%lu.%06lu", sec, usec);

	type = pevent_data_type(pevent, record);
	event = pevent_data_event_from_type(pevent, type);
	trace_seq_puts(&s, event->name);
	trace_seq_putc(&s, ':');
	pevent_event_info(&s, event, cpu, record->data, record->size,
			  record->ts);
	trace_seq_putc(&s, '\n');
	trace_seq_do_printf(&s);
}

#define CPU_BOARDER 5

static int check_sched_switch(struct graph_info *ginfo,
			      struct record *record,
			      gint *pid, const char **comm)
{
	static struct format_field *event_pid_field;
	static struct format_field *event_comm_field;
	static struct format_field *ftrace_pid_field;
	static struct format_field *ftrace_comm_field;
	unsigned long long val;
	struct event *event;
	gint id;

	if (event_sched_switch_id < 0) {
		event = pevent_find_event_by_name(ginfo->pevent,
						  "ftrace", "context_switch");
		if (event) {
			ftrace_sched_switch_id = event->id;
			ftrace_pid_field = pevent_find_field(event, "next_pid");
			ftrace_comm_field = pevent_find_field(event, "next_comm");
		}

		event = pevent_find_event_by_name(ginfo->pevent,
						  "sched", "sched_switch");
		if (!event)
			die("can't find event sched_switch!");
		event_sched_switch_id = event->id;
		event_pid_field = pevent_find_field(event, "next_pid");
		event_comm_field = pevent_find_field(event, "next_comm");
	}

	id = pevent_data_type(ginfo->pevent, record);
	if (id == event_sched_switch_id) {
		pevent_read_number_field(event_pid_field, record->data, &val);
		if (comm)
			*comm = record->data + event_comm_field->offset;
		if (pid)
			*pid = val;
		return 1;
	}

	if (id == ftrace_sched_switch_id) {
		pevent_read_number_field(ftrace_pid_field, record->data, &val);
		if (comm)
			*comm = record->data + ftrace_comm_field->offset;
		if (pid)
			*pid = val;
		return 1;
	}

	return 0;
}

static void draw_cpu_info(struct graph_info *ginfo, gint cpu, gint x, gint y)
{
	PangoLayout *layout;
	struct record *record = NULL;
	struct pevent *pevent;
	struct event *event;
	guint64 time;
	const char *comm;
	gint pid = -1;
	gint type;
	unsigned long sec, usec;
	struct trace_seq s;
	gint width, height;
	GdkPixmap *pix;
	static GdkGC *pix_bg;
	guint64 offset = 0;

	if (!pix_bg) {
		GdkColor color;

		pix_bg = gdk_gc_new(ginfo->draw->window);
		color.red = (0xff) *(65535/255);
		color.green = (0xfa) *(65535/255);
		color.blue = (0xcd) *(65535/255);
		gdk_color_alloc(gtk_widget_get_colormap(ginfo->draw), &color);
		gdk_gc_set_foreground(pix_bg, &color);
	}

	printf("res=%f\n", ginfo->resolution);
	time =  (x / ginfo->resolution) + ginfo->view_start_time;
	convert_nano(time, &sec, &usec);

	pevent = ginfo->pevent;

	trace_seq_init(&s);

	printf("start=%zu end=%zu time=%lu\n", ginfo->start_time, ginfo->end_time, time);
	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
	do {
		if (record) {
			offset = record->offset;
			free_record(record);
		}
		record = tracecmd_read_data(ginfo->handle, cpu);
	} while (record && record->ts <= (time - 1 / ginfo->resolution));

	if (record) {

		if (record->ts > (time + 1 / ginfo->resolution) && offset) {
			printf("old ts = %llu!\n", record->ts);
			free_record(record);
			record = tracecmd_read_at(ginfo->handle, offset, NULL);
		}

		if (!check_sched_switch(ginfo, record, &pid, &comm)) {
			pid = pevent_data_pid(ginfo->pevent, record);
			comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
		}

		printf("record->ts=%llu time=%zu-%zu\n",
		       record->ts, time, time-(gint)(1/ginfo->resolution));
		print_rec_info(record, pevent, cpu);

		if (record->ts > time - 2/ginfo->resolution &&
		    record->ts < time + 2/ginfo->resolution) {
			convert_nano(record->ts, &sec, &usec);

			type = pevent_data_type(pevent, record);
			event = pevent_data_event_from_type(pevent, type);
			trace_seq_puts(&s, event->name);
			trace_seq_putc(&s, '\n');
			pevent_data_lat_fmt(pevent, &s, record->data, record->size);
			trace_seq_putc(&s, '\n');
			pevent_event_info(&s, event, cpu, record->data, record->size,
					  record->ts);
			trace_seq_putc(&s, '\n');
		}

		trace_seq_printf(&s, "%lu.%06lu", sec, usec);
		if (pid)
			trace_seq_printf(&s, " %s-%d", comm, pid);
		else
			trace_seq_puts(&s, " <idle>");

		free(record);

	} else
		trace_seq_printf(&s, "%lu.%06lu", sec, usec);

	trace_seq_putc(&s, 0);

	layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
	pango_layout_get_pixel_size(layout, &width, &height);

	width += CPU_BOARDER * 2;
	height += CPU_BOARDER * 2;

	if (y > height)
		y -= height;


	if (x + width > ginfo->draw->allocation.width)
		x -= ((x + width) - ginfo->draw->allocation.width);

	ginfo->cpu_data_x = x;
	ginfo->cpu_data_y = y;
	ginfo->cpu_data_w = width;
	ginfo->cpu_data_h = height;

	pix = gdk_pixmap_new(ginfo->draw->window,
			     width,
			     height,
			     -1);

	gdk_draw_rectangle(pix,
			   pix_bg,
			   TRUE,
			   0, 0,
			   width, height);
	
	gdk_draw_rectangle(pix,
			   ginfo->draw->style->black_gc,
			   FALSE,
			   0, 0,
			   width-1, height-1);

	gdk_draw_layout(pix, ginfo->draw->style->black_gc,
			CPU_BOARDER, CPU_BOARDER, layout);
	gdk_draw_drawable(ginfo->draw->window,
			  ginfo->draw->style->fg_gc[GTK_WIDGET_STATE(ginfo->draw)],
			  pix, 0, 0, x, y, width, height);

	g_object_unref(layout);
	g_object_unref(pix);
}

static gboolean
motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
	struct graph_info *ginfo = data;
	GdkModifierType state;
	gint x, y;
	gint cpu;

	update_with_backend(ginfo, ginfo->cpu_data_x, ginfo->cpu_data_y,
			    ginfo->cpu_data_w, ginfo->cpu_data_h);
	if (event->is_hint)
		gdk_window_get_pointer(event->window, &x, &y, &state);
	else {
		x = event->x;
		y = event->y;
		state = event->state;
	}

	if (!ginfo->curr_pixmap)
		return TRUE;

	if (ginfo->line_active) {
		if (ginfo->last_x)
			clear_last_line(widget, ginfo);
		ginfo->last_x = x;
		draw_line(widget, ginfo->press_x, ginfo);
		draw_line(widget, x, ginfo);
		return TRUE;
	}

	for (cpu = 0; cpu < ginfo->cpus; cpu++) {
		if (y >= CPU_TOP(cpu) && y <= CPU_BOTTOM(cpu)) {
			draw_cpu_info(ginfo, cpu, x, y);
		}
	}

	return TRUE;
}

static void update_graph(struct graph_info *ginfo, gdouble percent)
{
	ginfo->full_width *= percent;
	ginfo->resolution =
		(gdouble)ginfo->full_width / (gdouble)(ginfo->end_time -
						       ginfo->start_time);
	ginfo->start_x *= percent;
}

static void update_graph_to_start_x(struct graph_info *ginfo)
{
	ginfo->view_start_time = ginfo->start_x / ginfo->resolution +
		ginfo->start_time;

	ginfo->view_end_time = ginfo->draw_width / ginfo->resolution +
		ginfo->view_start_time;
}

static void reset_graph(struct graph_info *ginfo, gdouble view_width)
{
	ginfo->full_width = view_width;
	ginfo->draw_width = 0;
	ginfo->view_start_time = ginfo->start_time;
	ginfo->view_end_time = ginfo->end_time;
	ginfo->start_x = 0;
}

static void zoom_in_window(struct graph_info *ginfo, gint start, gint end)
{
	gdouble view_width;
	gdouble new_width;
	gdouble select_width;
	gdouble curr_width;
	gdouble mid;
	gdouble percent;
	gint old_width = ginfo->draw_width;

	g_assert(start < end);
	g_assert(ginfo->vadj);

	printf("*** started with ");
	print_time(start / ginfo->resolution + ginfo->view_start_time);
	printf("\n");

	view_width = gtk_adjustment_get_page_size(ginfo->vadj);
	select_width = end - start;
	percent = view_width / select_width;

	update_graph(ginfo, percent);

	curr_width = ginfo->draw->allocation.width;
	new_width = curr_width * percent;

	printf("width=%d\n", ginfo->draw->allocation.width);
	if (ginfo->vadj) {
		printf("adj:%f-%f\n", gtk_adjustment_get_upper(ginfo->vadj),
		       gtk_adjustment_get_lower(ginfo->vadj));
	} else
		printf("no adjustment\n");

	ginfo->draw_width = new_width;

	if (ginfo->draw_width > MAX_WIDTH) {
		gint new_start;
		gint new_end;

		/*
		 * The drawing is now greater than our max. We must
		 * limit the maximum size of the drawing area or
		 * we risk running out of X resources.
		 *
		 * We will now shorten the trace to that of what will
		 * fit in this zoomed area.
		 */
		ginfo->draw_width = MAX_WIDTH;

		mid = start + (end - start) / 2;
		mid *= percent;

		/*
		 * mid now points to the center of the viewable area
		 * if the draw area was of new_width.
		 *
		 *       new_start           new_end
		 * +------------------------------------------------+
		 * |        |                 |                     |
		 * |        |                 |                     |
		 * +------------------------------------------------+
		 * ^                ^
		 * |               mid
		 * old view start
		 *
		 */

		new_start = mid - MAX_WIDTH / 2;
		new_end = new_start + MAX_WIDTH;
		if (new_start < 0) {
			/* First check if there's a start available in full */
			if (ginfo->start_x) {
				ginfo->start_x += new_start;
				if (ginfo->start_x < 0) {
					new_start = ginfo->start_x;
					ginfo->start_x = 0;
				} else
					new_start = 0;
			}
			new_end += -new_start;
			new_start = 0;
		} else if (new_end > ginfo->full_width) {
			new_start -= new_end - ginfo->full_width;
			new_end = ginfo->full_width;
			g_assert(new_start >= 0);
		}

		ginfo->start_x += new_start;

		update_graph_to_start_x(ginfo);

		printf("new start/end =%d/%d full:%d  start_time:",
		       new_start, new_end, ginfo->full_width);
		print_time(ginfo->view_start_time);
		printf("\n");

		/* Adjust start to be the location for the vadj */
		start = (mid - new_start) / percent - (end - start) / 2;
	}

	ginfo->vadj_value = (gdouble)start * view_width / select_width;
	if (ginfo->vadj_value > (ginfo->draw_width - view_width))
		ginfo->vadj_value = ginfo->draw_width - view_width;

	printf("new width=%d\n", ginfo->draw_width);

	/* make sure the width is sent */
	if (ginfo->draw_width == old_width)
		gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width - 1,
					    ginfo->draw_height);
	gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height);

	printf("set val %f\n", ginfo->vadj_value);


	printf("*** ended with with ");
	print_time(ginfo->vadj_value / ginfo->resolution + ginfo->view_start_time);
	printf("\n");

}

static gboolean
value_changed(GtkWidget *widget, gpointer data)
{
	struct graph_info *ginfo = data;
	GtkAdjustment *adj = GTK_ADJUSTMENT(widget);

	printf("value = %f\n",
	       gtk_adjustment_get_value(adj));

	return TRUE;

}

static void zoom_out_window(struct graph_info *ginfo, gint start, gint end)
{
	gdouble view_width;
	gdouble divider;
	gdouble curr_width;
	gdouble new_width;
	gdouble mid;
	gdouble start_x;
	unsigned long long time;
	gint old_width = ginfo->draw_width;

	g_assert(start > end);
	g_assert(ginfo->vadj);

	view_width = gtk_adjustment_get_page_size(ginfo->vadj);
	start_x = gtk_adjustment_get_value(ginfo->vadj);
	mid = start_x + view_width / 2;

	time = mid / ginfo->resolution + ginfo->view_start_time;

	divider = start - end;

	curr_width = ginfo->draw->allocation.width;
	new_width = curr_width / divider;

	update_graph(ginfo, 1 / divider);

	printf("width=%d\n", ginfo->draw->allocation.width);

	ginfo->draw_width = new_width;

	printf("draw_width=%d full_width=%d\n", ginfo->draw_width, ginfo->full_width);
	if (ginfo->full_width < view_width) {
		reset_graph(ginfo, view_width);
		time = ginfo->view_start_time;

	} else if (ginfo->draw_width < ginfo->full_width) {
		if (ginfo->full_width < MAX_WIDTH) {
			ginfo->draw_width = ginfo->full_width;
			ginfo->view_start_time = ginfo->start_time;
			ginfo->view_end_time = ginfo->end_time;
			ginfo->start_x = 0;
		} else {
			ginfo->draw_width = MAX_WIDTH;
			mid /= divider;
			mid += ginfo->start_x;

			/* mid now is the current mid with full_width */
			ginfo->start_x = mid - MAX_WIDTH / 2;
			if (ginfo->start_x < 0)
				ginfo->start_x = 0;

			update_graph_to_start_x(ginfo);
		}
	}

	printf("new width=%d\n", ginfo->draw_width);

	/* make sure the width is sent */
	if (ginfo->draw_width == old_width)
		gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width - 1,
					    ginfo->draw_height);
	gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height);

	mid = (time - ginfo->view_start_time) * ginfo->resolution;
	start_x = mid - view_width / 2;
	if (start_x < 0)
		start_x = 0;

	ginfo->vadj_value = start_x;
}

static gboolean
button_release_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
	struct graph_info *ginfo = data;

	if (ginfo->line_active) {
		ginfo->line_active = FALSE;
		clear_last_line(widget, ginfo);
		ginfo->last_x = ginfo->press_x;
		clear_last_line(widget, ginfo);

		if (event->x > ginfo->press_x) {
			/* make a decent zoom */
			if (event->x - ginfo->press_x < 10)
				return TRUE;
			zoom_in_window(ginfo, ginfo->press_x, event->x);
		} else if (event->x < ginfo->press_x)
			zoom_out_window(ginfo, ginfo->press_x, event->x);
	}

	return TRUE;
}

static gint do_hash(gint val)
{
	return (val + (val << 4) + (val << 8) + (val << 12) + 
		(val << 16) + (val << 20) + (val << 24) +
		(val << 28)) * 3;
}

static void set_color_by_pid(GtkWidget *widget, GdkGC *gc, gint pid)
{
	GdkColor color;
	gint hash = do_hash(pid);
	static gint last_pid = -1;

	if (!(hash & 0xffffff) && last_pid != pid) {
		last_pid = pid;
		printf("pid=%d is black\n", pid);
	}
	color.red = (hash & 0xff)*(65535/255);
	color.blue = ((hash >> 8) & 0xff)*(65535/255);
	color.green = ((hash >> 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,
			    gint p1, gint p2, gint p3,
			    gint width_16, PangoFontDescription *font)
{
	struct event *event;
	PangoLayout *layout;
	struct trace_seq s;
	gint text_width;
	gint text_height;
	gint x, y;


	/* No room to print */
	if (((p3 - p2) < width_16 / 2 ||
	     (p2 - p1) < width_16 / 2))
		return;

	/* 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);

	layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
	pango_layout_set_font_description(layout, font);

	pango_layout_get_pixel_size(layout, &text_width, &text_height);

	if ((p3 - p2) < text_width / 2 ||
	    (p2 - p1) < text_width / 2) {
		g_object_unref(layout);
		return;
	}

	x = p2 - text_width / 2;
	y = (CPU_TOP(cpu) - 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, CPU_TOP(cpu) - 5, p2, CPU_TOP(cpu) - 1);

	g_object_unref(layout);
}

static void draw_cpu(struct graph_info *ginfo, gint cpu,
		     gint new_width)
{
	static PangoFontDescription *font;
	PangoLayout *layout;
	gint height = CPU_MIDDLE(cpu);
	struct record *record;
	static GdkGC *gc;
	static gint width_16;
	guint64 ts;
	gint last_pid = -1;
	gint last_x = 0;
	gint pid;
	gint x;
	gint p1 = 0, p2 = 0, p3 = 0;
	gint last_event_id = 0;
	gint event_id;

	/* Calculate the size of 16 characters */
	if (!width_16) {
		gchar buf[17];
		gint text_height;

		memset(buf, 'a', 16);
		buf[16] = 0;

		font = pango_font_description_from_string("Sans 8");
		layout = gtk_widget_create_pango_layout(ginfo->draw, buf);
		pango_layout_set_font_description(layout, font);
		pango_layout_get_pixel_size(layout, &width_16, &text_height);
		g_object_unref(layout);
	}

	if (!gc)
		gc = gdk_gc_new(ginfo->draw->window);

	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
		      0, height, new_width, height);

	ts = ginfo->view_start_time;

	tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, ts);

	while ((record = tracecmd_read_data(ginfo->handle, cpu))) {

		if (record->ts > ginfo->view_end_time)
			break;

		ts = record->ts - ginfo->view_start_time;

		x = (gint)((gdouble)ts * ginfo->resolution);


		if (!check_sched_switch(ginfo, record, &pid, NULL))
			pid = pevent_data_pid(ginfo->pevent, record);

		event_id = pevent_data_type(ginfo->pevent, record);

		if (last_pid != pid) {

			if (last_pid < 0)
				last_pid = pid;

			if (last_pid) {
				gdk_draw_line(ginfo->curr_pixmap, gc,
					      last_x, CPU_TOP(cpu),
					      x, CPU_TOP(cpu));
				gdk_draw_line(ginfo->curr_pixmap, gc,
					      last_x, CPU_BOTTOM(cpu),
					      x, CPU_BOTTOM(cpu));
			}

			last_x = x;
			last_pid = pid;
		}

		set_color_by_pid(ginfo->draw, gc, pid);

		gdk_draw_line(ginfo->curr_pixmap, gc, // ginfo->draw->style->black_gc,
			      x, CPU_TOP(cpu), x, CPU_BOTTOM(cpu));

		/* Figure out if we can show the text for the previous record */

		p3 = x;

		/* Make sure p2 will be non-zero the next iteration */
		if (!p3)
			p3 = 1;

		/* first record, continue */
		if (p2)
			draw_event_label(ginfo, cpu, last_event_id, last_pid,
					 p1, p2, p3, width_16, font);

		p1 = p2;
		p2 = p3;
		last_event_id = event_id;
		free(record);
	}

	draw_event_label(ginfo, cpu, last_event_id, last_pid,
			 p1, p2, ginfo->draw_width, width_16, font);


	if (last_pid > 0) {
		x = ginfo->draw_width;

		gdk_draw_line(ginfo->curr_pixmap, gc,
			      last_x, CPU_TOP(cpu),
			      x, CPU_TOP(cpu));
		gdk_draw_line(ginfo->curr_pixmap, gc,
			      last_x, CPU_BOTTOM(cpu),
			      x, CPU_BOTTOM(cpu));
	}

	if (record)
		free(record);
}


static void draw_timeline(struct graph_info *ginfo, gint width)
{
	PangoLayout *layout;
	struct trace_seq s;
	unsigned long sec, usec;
	unsigned long long time;
	gint mid;
	gint w, h, height;
	gint view_width;

	/* --- draw timeline text --- */

	layout = gtk_widget_create_pango_layout(ginfo->draw, "Time Line");
	pango_layout_get_pixel_size(layout, &w, &h);

	height = 10 + h;

	mid = width / 2;
	gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
			mid - w / 2, 5, layout);
	g_object_unref(layout);


	/* --- draw time line lines --- */
	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
		      0, height, width, height);

	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
		      0, height, 0, height + 5);

	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
		      width-1, height, width-1, height);

	gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
		      width-1, height, width-1, height + 5);

	/* --- draw starting time --- */
	convert_nano(ginfo->view_start_time, &sec, &usec);
	trace_seq_init(&s);
	trace_seq_printf(&s, "%lu.%06lu", sec, usec);

	layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
	pango_layout_get_pixel_size(layout, &w, &h);

	gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
			1, height+10, layout);
	g_object_unref(layout);


	/* --- draw ending time --- */
	convert_nano(ginfo->view_end_time, &sec, &usec);
	trace_seq_init(&s);
	trace_seq_printf(&s, "%lu.%06lu", sec, usec);

	layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
	pango_layout_get_pixel_size(layout, &w, &h);

	gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
			width - (w + 2), height+10, layout);
	g_object_unref(layout);


	/* --- draw time at intervals --- */
	view_width = gtk_adjustment_get_page_size(ginfo->vadj);

	for (mid = view_width / 2; mid < (width - view_width / 2 + 10);
	     mid += view_width / 2) {
		time = mid / ginfo->resolution + ginfo->view_start_time;

		convert_nano(time, &sec, &usec);
		trace_seq_init(&s);
		trace_seq_printf(&s, "%lu.%06lu", sec, usec);

		gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
			      mid, height, mid, height + 5);

		layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer);
		pango_layout_get_pixel_size(layout, &w, &h);

		gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc,
				mid - (w / 2), height+10, layout);
		g_object_unref(layout);
	}
}

static void draw_info(struct graph_info *ginfo,
		      gint new_width)
{
	gint cpu;

	ginfo->resolution = (gdouble)new_width / (gdouble)(ginfo->view_end_time -
							   ginfo->view_start_time);

	draw_timeline(ginfo, new_width);

	
	for (cpu = 0; cpu < ginfo->cpus; cpu++)
		draw_cpu(ginfo, cpu, new_width);

}

static gboolean
configure_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
	struct graph_info *ginfo = data;
	GdkPixmap *old_pix;

//	gtk_widget_set_size_request(widget, 0, ginfo->draw_height);


	old_pix = ginfo->curr_pixmap;

	/* initialize full width if needed */
	if (!ginfo->full_width)
		ginfo->full_width = widget->allocation.width;

	ginfo->curr_pixmap = gdk_pixmap_new(widget->window,
					    widget->allocation.width,
					    widget->allocation.height,
					    -1);

	gdk_draw_rectangle(ginfo->curr_pixmap,
			   widget->style->white_gc,
			   TRUE,
			   0, 0,
			   widget->allocation.width,
			   widget->allocation.height);

	draw_info(ginfo, widget->allocation.width);

	if (old_pix) {
#if 0
		gdk_draw_drawable(ginfo->curr_pixmap,
				  ginfo->draw->style->fg_gc[GTK_WIDGET_STATE(ginfo->draw)],
				  old_pix,
				  0, 0, 0, 0,
				  old_w, old_h);
#endif

		g_object_unref(old_pix);
	}

	if (!ginfo->vadj_value)
		return TRUE;

//	gtk_adjustment_set_lower(ginfo->vadj, -100.0);
	gtk_adjustment_set_value(ginfo->vadj, ginfo->vadj_value);


	/* debug */
	ginfo->vadj_value = gtk_adjustment_get_value(ginfo->vadj);
	printf("get val %f\n", ginfo->vadj_value);
	ginfo->vadj_value = 0.0;
	
	return TRUE;
}

static gboolean
destroy_event(GtkWidget *widget, gpointer data)
{
	struct graph_info *ginfo = data;

	if (ginfo->test)
		printf("test = %s\n", ginfo->test);

	return TRUE;
}


static GtkWidget *
create_drawing_area(struct tracecmd_input *handle, GtkScrolledWindow *scrollwin)
{
	struct graph_info *ginfo;
	unsigned long sec, usec;
	gint cpu;

	ginfo = g_new0(typeof(*ginfo), 1);
	g_assert(ginfo != NULL);
	ginfo->test = "hello!";

	ginfo->handle = handle;
	ginfo->pevent = tracecmd_get_pevent(handle);
	ginfo->cpus = tracecmd_cpus(handle);

	ginfo->start_time = -1ULL;
	ginfo->end_time = 0;

	ginfo->draw_height = CPU_SPACE(ginfo->cpus);
	ginfo->vadj = gtk_scrolled_window_get_hadjustment(scrollwin);

	gtk_signal_connect(GTK_OBJECT(ginfo->vadj), "value_changed",
			   (GtkSignalFunc) value_changed, ginfo);

	for (cpu = 0; cpu < ginfo->cpus; cpu++) {
		struct record *record;

		record = tracecmd_read_cpu_first(handle, cpu);
		if (!record)
			continue;

		if (record->ts < ginfo->start_time)
			ginfo->start_time = record->ts;

		record = tracecmd_read_cpu_last(handle, cpu);

		if (record->ts > ginfo->end_time)
			ginfo->end_time = record->ts;
	}

	convert_nano(ginfo->start_time, &sec, &usec);
	printf("start=%lu.%06lu ", sec, usec);

	convert_nano(ginfo->end_time, &sec, &usec);
	printf("end=%lu.%06lu\n", sec, usec);

	ginfo->view_start_time = ginfo->start_time;
	ginfo->view_end_time = ginfo->end_time;

	ginfo->draw = gtk_drawing_area_new();

	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "expose_event",
			   (GtkSignalFunc) expose_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "button_press_event",
			   (GtkSignalFunc) button_press_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "configure_event",
			   (GtkSignalFunc) configure_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "motion_notify_event",
			   (GtkSignalFunc) motion_notify_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "button_release_event",
			   (GtkSignalFunc) button_release_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(ginfo->draw), "destroy",
			   (GtkSignalFunc) destroy_event, ginfo);

	gtk_widget_set_events(ginfo->draw, GDK_EXPOSURE_MASK
			      | GDK_LEAVE_NOTIFY_MASK
			      | GDK_BUTTON_PRESS_MASK
			      | GDK_BUTTON_RELEASE_MASK
			      | GDK_POINTER_MOTION_MASK
			      | GDK_POINTER_MOTION_HINT_MASK);


	return ginfo->draw;
}

/* Callback for the clicked signal of the Exit button */
static void
exit_clicked (GtkWidget *widget, gpointer data)
{
	gtk_widget_destroy (GTK_WIDGET (data)); /* the user data points to the main window */
	gtk_main_quit ();
}

/* Callback for the delete_event signal of the main application window */
static gint
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
	gtk_widget_destroy (widget); /* destroy the main window */
	gtk_main_quit ();
	return TRUE;
}

void trace_graph(int argc, char **argv)
{
	struct tracecmd_input *handle;
	GtkWidget *window;
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *menu_bar;
	GtkWidget *menu;
	GtkWidget *menu_item;
	GtkWidget *sub_item;
	GtkWidget *scrollwin;
	GtkWidget *draw;

	handle = tracecmd_open(input_file);

	if (!handle)
		die("error reading header");

	if (tracecmd_read_headers(handle) < 0)
		return;

	if (tracecmd_init_data(handle) < 0)
		die("failed to init data");

	gnome_init("trace-cmd", version, argc, argv);

	/* --- Main window --- */

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

	/* --- Top Level Vbox --- */

	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER (window), vbox);
	gtk_widget_show(vbox);

	/* --- Menu Bar --- */

	menu_bar = gtk_menu_bar_new();
	gtk_box_pack_start(GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0);
	gtk_widget_show(menu_bar);

	/* --- File Option --- */

	menu_item = gtk_menu_item_new_with_label("File");
	gtk_widget_show(menu_item);

	gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item);

	menu = gtk_menu_new();    /* Don't need to show menus */


	/* --- File - Quit Option --- */

	sub_item = gtk_menu_item_new_with_label("Quit");

	/* Add them to the menu */
	gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);

	/* We can attach the Quit menu item to our exit function */
	g_signal_connect_swapped (G_OBJECT (sub_item), "activate",
				  G_CALLBACK (exit_clicked),
				  (gpointer) window);

	/* We do need to show menu items */
	gtk_widget_show(sub_item);

	gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu);

	/* --- end File options --- */


	/* --- Top Level Hbox --- */

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
	gtk_widget_show(hbox);

	/* --- Scroll Window --- */
	scrollwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
	gtk_box_pack_start(GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0);
	gtk_widget_show(scrollwin);

	/* --- Set up Drawing --- */

	draw = create_drawing_area(handle, GTK_SCROLLED_WINDOW(scrollwin));

	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollwin),
					      draw);
	gtk_widget_show(draw);


	/**********************************************
	 *   Main Window
	 **********************************************/

	/* Connect to the delete_event signal and Run the application */

	gtk_signal_connect (GTK_OBJECT (window), "delete_event",
			    (GtkSignalFunc) delete_event,
			    NULL);

	gtk_widget_set_size_request(window, TRACE_WIDTH, TRACE_HEIGHT);

	gtk_widget_show (window);
	gtk_main ();
}

int main(int argc, char **argv)
{
	trace_graph(argc, argv);
	return 0;
}