aboutsummaryrefslogblamecommitdiffstats
path: root/trace-graph.c
blob: b53031460274157a2958730e0e4db09d072a15be (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 <gtk/gtk.h>

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

#define DEBUG_LEVEL	2
#if DEBUG_LEVEL > 0
# define dprintf(l, x...)			\
	do {					\
		if (l <= DEBUG_LEVEL)		\
			printf(x);		\
	} while (0)
#else
# define dprintf(x...)	do { } while (0)
#endif

#define MAX_WIDTH	10000

#define CPU_SIZE	10
#define CPU_BOX_SIZE	CPU_SIZE
#define CPU_GIVE	2
#define CPU_LINE(cpu) (80 * (cpu) + 80 + CPU_SIZE)
#define CPU_TOP(cpu) (CPU_LINE(cpu) - CPU_SIZE * 2)
#define CPU_BOX_TOP(cpu) (CPU_LINE(cpu) - CPU_SIZE)
#define CPU_BOTTOM(cpu) (CPU_LINE(cpu)-1)
#define CPU_BOX_BOTTOM(cpu) (CPU_LINE(cpu))
#define CPU_SPACE(cpus) (80 * (cpus) + 80)
#define CPU_LABEL(cpu) (CPU_TOP(cpu))
#define CPU_X		5

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

static gint largest_cpu_label = 0;

static void redraw_pixmap_backend(struct graph_info *ginfo);

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;

	if (!DEBUG_LEVEL)
		return;

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

struct filter_task_item *
trace_graph_filter_task_find_pid(struct graph_info *ginfo, gint pid)
{
	return filter_task_find_pid(ginfo->task_filter, pid);
}

struct filter_task_item *
trace_graph_hide_task_find_pid(struct graph_info *ginfo, gint pid)
{
	return filter_task_find_pid(ginfo->hide_tasks, pid);
}

static void graph_filter_task_add_pid(struct graph_info *ginfo, gint pid)
{
	filter_task_add_pid(ginfo->task_filter, pid);

	ginfo->filter_available = 1;
}

static void graph_filter_task_remove_pid(struct graph_info *ginfo, gint pid)
{
	filter_task_remove_pid(ginfo->task_filter, pid);

	if (!filter_task_count(ginfo->task_filter) &&
	    !filter_task_count(ginfo->hide_tasks)) {
		ginfo->filter_available = 0;
		ginfo->filter_enabled = 0;
	}
}

static void graph_hide_task_add_pid(struct graph_info *ginfo, gint pid)
{
	filter_task_add_pid(ginfo->hide_tasks, pid);

	ginfo->filter_available = 1;
}

static void graph_hide_task_remove_pid(struct graph_info *ginfo, gint pid)
{
	filter_task_remove_pid(ginfo->hide_tasks, pid);

	if (!filter_task_count(ginfo->task_filter) &&
	    !filter_task_count(ginfo->hide_tasks)) {
		ginfo->filter_available = 0;
		ginfo->filter_enabled = 0;
	}
}

static void graph_filter_task_clear(struct graph_info *ginfo)
{
	filter_task_clear(ginfo->task_filter);
	filter_task_clear(ginfo->hide_tasks);

	ginfo->filter_available = 0;
	ginfo->filter_enabled = 0;
}

gboolean graph_filter_system(struct graph_info *ginfo, const gchar *system)
{
	const gchar **sys = &system;

	if (ginfo->all_events)
		return TRUE;

	if (!ginfo->systems)
		return FALSE;

	sys = bsearch(sys, ginfo->systems, ginfo->systems_size,
		      sizeof(system), str_cmp);

	return sys != NULL;
}

gboolean graph_filter_event(struct graph_info *ginfo, gint event_id)
{
	gint *event = &event_id;

	if (ginfo->all_events)
		return TRUE;

	if (!ginfo->event_ids)
		return FALSE;

	event = bsearch(event, ginfo->event_ids, ginfo->event_ids_size,
			sizeof(event_id), id_cmp);

	return event != NULL;
}

gboolean graph_filter_on_event(struct graph_info *ginfo, struct record *record)
{
	struct event_format *event;
	gint event_id;

	if (!record)
		return TRUE;

	if (ginfo->all_events)
		return FALSE;

	event_id = pevent_data_type(ginfo->pevent, record);
	event = pevent_data_event_from_type(ginfo->pevent, event_id);
	if (!event)
		return TRUE;

	if (graph_filter_system(ginfo, event->system))
		return FALSE;

	if (graph_filter_event(ginfo, event_id))
		return FALSE;

	return TRUE;
}

gboolean graph_filter_on_task(struct graph_info *ginfo, gint pid)
{
	gboolean filter;

	filter = FALSE;

	if (ginfo->filter_enabled &&
	    ((filter_task_count(ginfo->task_filter) &&
	      !trace_graph_filter_task_find_pid(ginfo, pid)) ||
	     (filter_task_count(ginfo->hide_tasks) &&
	      trace_graph_hide_task_find_pid(ginfo, pid))))
		filter = TRUE;

	return filter;
}

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 void draw_cursor(struct graph_info *ginfo)
{
	gint x;

	if (ginfo->cursor < ginfo->view_start_time ||
	    ginfo->cursor > ginfo->view_end_time)
		return;

	x = (ginfo->cursor - ginfo->view_start_time)
		* ginfo->resolution;

	gdk_draw_line(ginfo->draw->window, ginfo->draw->style->mid_gc[3],
		      x, 0, x, ginfo->draw->allocation.width);
}

static void update_with_backend(struct graph_info *ginfo,
				gint x, gint y,
				gint width, gint height)
{
	__update_with_backend(ginfo, x, y, width, height);

	if (ginfo->cursor)
		draw_cursor(ginfo);
}

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 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 redraw_graph(struct graph_info *ginfo)
{
	gdouble height;
	gdouble width;

	redraw_pixmap_backend(ginfo);
	width = ginfo->draw->allocation.width;
	height = ginfo->draw->allocation.height;
	update_with_backend(ginfo, 0, 0, width, height);
}

static struct record *
find_record_on_cpu(struct graph_info *ginfo, gint cpu, guint64 time)
{
	struct record *record = NULL;
	guint64 offset = 0;

	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) {
			dprintf(3, "old ts = %llu!\n", record->ts);
			free_record(record);
			record = tracecmd_read_at(ginfo->handle, offset, NULL);
		}
	}

	return record;
}

void trace_graph_filter_toggle(struct graph_info *ginfo)
{
	ginfo->filter_enabled ^= 1;

	redraw_graph(ginfo);
}

static void
filter_enable_clicked (gpointer data)
{
	struct graph_info *ginfo = data;

	trace_graph_filter_toggle(ginfo);
}

void trace_graph_filter_add_remove_task(struct graph_info *ginfo,
					gint pid)
{
	gint filter_enabled = ginfo->filter_enabled;
	struct filter_task_item *task;

	task = trace_graph_filter_task_find_pid(ginfo, pid);

	if (task)
		graph_filter_task_remove_pid(ginfo, task->pid);
	else
		graph_filter_task_add_pid(ginfo, pid);

	if (filter_enabled)
		redraw_graph(ginfo);
}

void trace_graph_filter_hide_show_task(struct graph_info *ginfo,
				       gint pid)
{
	gint filter_enabled = ginfo->filter_enabled;
	struct filter_task_item *task;

	task = trace_graph_hide_task_find_pid(ginfo, pid);

	if (task)
		graph_hide_task_remove_pid(ginfo, task->pid);
	else
		graph_hide_task_add_pid(ginfo, pid);

	if (filter_enabled)
		redraw_graph(ginfo);
}

static void
filter_add_task_clicked (gpointer data)
{
	struct graph_info *ginfo = data;

	trace_graph_filter_add_remove_task(ginfo, ginfo->filter_task_selected);
}

static void
filter_hide_task_clicked (gpointer data)
{
	struct graph_info *ginfo = data;

	trace_graph_filter_hide_show_task(ginfo, ginfo->filter_task_selected);
}

void trace_graph_clear_tasks(struct graph_info *ginfo)
{
	gint filter_enabled = ginfo->filter_enabled;

	graph_filter_task_clear(ginfo);

	if (filter_enabled)
		redraw_graph(ginfo);
}

static void
filter_clear_tasks_clicked (gpointer data)
{
	struct graph_info *ginfo = data;

	trace_graph_clear_tasks(ginfo);
}

static gboolean
do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
	struct graph_info *ginfo = data;
	static GtkWidget *menu;
	static GtkWidget *menu_filter_enable;
	static GtkWidget *menu_filter_add_task;
	static GtkWidget *menu_filter_hide_task;
	static GtkWidget *menu_filter_clear_tasks;
	struct record *record = NULL;
	const char *comm;
	guint64 time;
	gchar *text;
	gint pid;
	gint len;
	gint x, y;
	gint cpu;

	x = event->x;
	y = event->y;

	if (!menu) {
		menu = gtk_menu_new();
		menu_filter_enable = gtk_menu_item_new_with_label("Enable Filter");
		gtk_widget_show(menu_filter_enable);
		gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_enable);

		g_signal_connect_swapped (G_OBJECT (menu_filter_enable), "activate",
					  G_CALLBACK (filter_enable_clicked),
					  data);

		menu_filter_add_task = gtk_menu_item_new_with_label("Add Task");
		gtk_widget_show(menu_filter_add_task);
		gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task);

		g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate",
					  G_CALLBACK (filter_add_task_clicked),
					  data);

		menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task");
		gtk_widget_show(menu_filter_hide_task);
		gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task);

		g_signal_connect_swapped (G_OBJECT (menu_filter_hide_task), "activate",
					  G_CALLBACK (filter_hide_task_clicked),
					  data);

		menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter");
		gtk_widget_show(menu_filter_clear_tasks);
		gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks);

		g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate",
					  G_CALLBACK (filter_clear_tasks_clicked),
					  data);

	}

	if (ginfo->filter_enabled)
		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable),
					"Disable Filter");
	else
		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable),
					"Enable Filter");

	if (ginfo->filter_available)
		gtk_widget_set_sensitive(menu_filter_enable, TRUE);
	else
		gtk_widget_set_sensitive(menu_filter_enable, FALSE);

	if (filter_task_count(ginfo->task_filter) ||
	    filter_task_count(ginfo->hide_tasks))
		gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE);
	else
		gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE);

	time =  (x / ginfo->resolution) + ginfo->view_start_time;

	for (cpu = 0; cpu < ginfo->cpus; cpu++) {
		if (y >= (CPU_TOP(cpu) - CPU_GIVE) &&
		    y <= (CPU_BOTTOM(cpu) + CPU_GIVE)) {
			record = find_record_on_cpu(ginfo, cpu, time);
			break;
		}
	}

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

		len = strlen(comm) + 50;

		text = g_malloc(len);
		g_assert(text);

		if (trace_graph_filter_task_find_pid(ginfo, pid))
			snprintf(text, len, "Remove %s-%d to filter", comm, pid);
		else
			snprintf(text, len, "Add %s-%d to filter", comm, pid);

		ginfo->filter_task_selected = pid;

		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task),
					text);

		if (trace_graph_hide_task_find_pid(ginfo, pid))
			snprintf(text, len, "Show %s-%d to filter", comm, pid);
		else
			snprintf(text, len, "Hide %s-%d to filter", comm, pid);

		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task),
					text);

		g_free(text);

		gtk_widget_set_sensitive(menu_filter_add_task, TRUE);
		gtk_widget_set_sensitive(menu_filter_hide_task, TRUE);

		free_record(record);
	} else {
		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task),
					"Add task to filter");
		gtk_widget_set_sensitive(menu_filter_add_task, FALSE);

		gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task),
					"Hide task to filter");
		gtk_widget_set_sensitive(menu_filter_hide_task, FALSE);
	}

		
	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3,
		       gtk_get_current_event_time());


	return TRUE;
}

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

	if (event->button == 3)
		return do_pop_up(widget, event, data);

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

	/* check for double click */
	if (event->type == GDK_2BUTTON_PRESS) {
		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 (ginfo->cursor >= ginfo->view_start_time &&
		    ginfo->cursor <= ginfo->view_end_time) {
			ginfo->last_x = (ginfo->cursor - ginfo->view_start_time)
				* ginfo->resolution;
			ginfo->cursor = 0;
			clear_last_line(widget, ginfo);
		}

		ginfo->cursor = event->x / ginfo->resolution +
			ginfo->view_start_time;
		draw_cursor(ginfo);
		if (ginfo->callbacks && ginfo->callbacks->select)
			ginfo->callbacks->select(ginfo, ginfo->cursor);
		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 print_rec_info(struct record *record, struct pevent *pevent, int cpu)
{
	struct trace_seq s;
	struct event_format *event;
	unsigned long sec, usec;
	gint type;

	if (DEBUG_LEVEL < 3)
		return;

	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);
	if (!event) {
		printf("No event found for id %d!\n", type);
		return;
	}
	trace_seq_puts(&s, event->name);
	trace_seq_putc(&s, ':');
	pevent_event_info(&s, event, record);
	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_format *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_format *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;
	gint view_width;
	gint view_start;

	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);
	}

	time =  (x / ginfo->resolution) + ginfo->view_start_time;
	convert_nano(time, &sec, &usec);

	pevent = ginfo->pevent;

	trace_seq_init(&s);

	dprintf(3, "start=%zu end=%zu time=%lu\n", ginfo->start_time, ginfo->end_time, time);

	record = find_record_on_cpu(ginfo, cpu, time);

	if (record) {

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

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

		/*
		 * The function graph trace reads the next record, which may
		 * unmap the record data. We need to reread the record to
		 * make sure it still exists.
		 */
		offset = record->offset;
		free_record(record);
		record = tracecmd_read_at(ginfo->handle, offset, NULL);		

		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);
			if (event) {
				trace_seq_puts(&s, event->name);
				trace_seq_putc(&s, '\n');
				pevent_data_lat_fmt(pevent, &s, record);
				trace_seq_putc(&s, '\n');
				pevent_event_info(&s, event, record);
				trace_seq_putc(&s, '\n');
			} else
				trace_seq_printf(&s, "UNKNOW EVENT %d\n", type);
		}

		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(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;

	view_start = gtk_adjustment_get_value(ginfo->vadj);
	view_width = gtk_adjustment_get_page_size(ginfo->vadj);

	if (x + width > view_start + view_width)
		x -= (x + width) - (view_start + view_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) - CPU_GIVE) &&
		    y <= (CPU_BOTTOM(cpu) + CPU_GIVE))
			draw_cpu_info(ginfo, cpu, x, y);
	}

	return TRUE;
}

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

	/* Check if we are too big */
	if (!resolution || full_width <= 0)
		return -1;

	ginfo->full_width = full_width;
	ginfo->resolution = resolution;
	ginfo->start_x = (ginfo->view_start_time - ginfo->start_time) *
		ginfo->resolution;

	dprintf(1, "new resolution = %f\n", resolution);
	return 0;
}

static void update_graph_to_start_x(struct graph_info *ginfo)
{
	gint width = ginfo->draw_width;;

	if (!width) {
		ginfo->view_start_time = ginfo->start_time;
		ginfo->view_end_time = ginfo->end_time;
		return;
	}

	ginfo->view_start_time = (gdouble)ginfo->start_x / ginfo->resolution +
		ginfo->start_time;

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

	g_assert (ginfo->view_start_time < ginfo->end_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)
{
	guint64 start_time;
	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);

	start_time = ginfo->start_time +
		(ginfo->start_x + start) / ginfo->resolution;

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

	dprintf(1, "view width=%f select width=%f percent=%f\n",
		view_width, select_width, percent);

	if (update_graph(ginfo, percent) < 0)
		return;

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

	ginfo->draw_width = new_width;
	dprintf(1, "zoom in draw_width=%d full_width=%d\n",
	       ginfo->draw_width, ginfo->full_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 += ginfo->start_x;

		/*
		 * 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) {
			mid += new_start;
			new_start = 0;
		} else if (new_end > ginfo->full_width) {
			new_start -= new_end - ginfo->full_width;
			mid += new_end - ginfo->full_width;
			new_end = ginfo->full_width;
			g_assert(new_start >= 0);
		}

		ginfo->start_x = new_start;

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

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

	update_graph_to_start_x(ginfo);

	ginfo->vadj_value = start;
	ginfo->vadj_value = (start_time - ginfo->view_start_time) * ginfo->resolution;

	if (ginfo->vadj_value > (ginfo->draw_width - view_width))
		ginfo->vadj_value = ginfo->draw_width - view_width;

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

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

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


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

}

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

	dprintf(2, "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;

	if (update_graph(ginfo, 1 / divider) < 0)
		return;

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

	ginfo->draw_width = new_width;

	dprintf(1, "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);
		}
	}

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

	/* make sure the width is sent */
	if (ginfo->draw_width == old_width)
		redraw_graph(ginfo);
	else
		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 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)
{
	GdkColor color;
	gint hash = hash_pid(pid);
	static gint last_pid = -1;

	if (!(hash & 0xffffff) && last_pid != pid) {
		last_pid = pid;
		dprintf(2, "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_format *event;
	PangoLayout *layout;
	struct trace_seq s;
	gint text_width;
	gint text_height;
	gint x, y;


	/* No room to print */
	if ((p2 > width_16 && ((p3 - p2) < width_16 / 2 ||
			       (p2 - p1) < width_16 / 2)) ||
	    (p2 <= width_16 && (p1 || (p3 - p2) < width_16)))
		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 ((p2 > text_width && ((p3 - p2) < text_width ||
				 (p2 - p1) < text_width)) ||
	    (p2 < text_width && (p1 || (p3 - p2 < (text_width +
						   text_width / 2))))) {
		g_object_unref(layout);
		return;
	}

	x = p2 - text_width / 2;
	if (x < 0)
		x = 1;

	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, int read_comms)
{
	static PangoFontDescription *font;
	PangoLayout *layout;
	gint height = CPU_LINE(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;
	gboolean filter;
	gboolean is_sched_switch;
	const char *comm;

	/* 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_start_time) {
			free_record(record);
			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 (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);
			}
		} else
			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;
				set_color_by_pid(ginfo->draw, gc, pid);
			}
				
			filter = graph_filter_on_task(ginfo, last_pid);

			if (!filter && last_pid)

				gdk_draw_rectangle(ginfo->curr_pixmap, gc,
						   TRUE,
						   last_x, CPU_BOX_TOP(cpu),
						   x - last_x, CPU_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);

		last_pid = pid;

		if (!filter) {
			filter = graph_filter_on_event(ginfo, record);
			if (!filter)
				gdk_draw_line(ginfo->curr_pixmap, gc,
					      x, CPU_TOP(cpu), x, CPU_BOTTOM(cpu));
		}

		if (!filter) {
			/* 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(record);
	}

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

	if (last_pid > 0 &&
	    !graph_filter_on_task(ginfo, last_pid)) {

		x = ginfo->draw_width;

		gdk_draw_rectangle(ginfo->curr_pixmap, gc,
				   TRUE,
				   last_x, CPU_BOX_TOP(cpu),
				   x - last_x, CPU_BOX_SIZE);
	}

	free_record(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)
{
	static int read_comms = 1;
	gint cpu;

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

	ginfo->full_width = (ginfo->end_time - ginfo->start_time) * ginfo->resolution;

	draw_timeline(ginfo, new_width);

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

	read_comms = 0;
}

void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time)
{
	gint view_width;
	gint width;
	gint mid;
	gint start;
	gint end;
	guint64 old_start_time = ginfo->view_start_time;

	view_width = gtk_adjustment_get_page_size(ginfo->vadj);
	width = ginfo->draw_width ? : ginfo->full_width;

	mid = (time - ginfo->start_time) * ginfo->resolution;
	start = mid - width / 2;
	if (start < 0)
		start = 0;
	end = start + width;

	/*
	 * Readjust the drawing to be centered on the selection.
	 */

	if (end > ginfo->full_width) {
		start -= end - ginfo->full_width;
		g_assert(start >= 0);
		end = ginfo->full_width;
	}

	ginfo->start_x = start;

	update_graph_to_start_x(ginfo);

	/* force redraw if we changed the time*/
	if (old_start_time != ginfo->view_start_time)
		redraw_pixmap_backend(ginfo);

	/* Adjust start to be the location for the vadj */
	mid = (time - ginfo->view_start_time) * ginfo->resolution;
	start = mid - view_width / 2;
	if (start < 0)
		start = 0;

	if (start > (width - view_width))
		start = width - view_width;
	gtk_adjustment_set_value(ginfo->vadj, start);

	ginfo->last_x = (ginfo->cursor - ginfo->view_start_time)
		* ginfo->resolution;
	ginfo->cursor = 0;
	clear_last_line(ginfo->draw, ginfo);
	ginfo->cursor = time;

	update_with_backend(ginfo, 0, 0, width, ginfo->draw_height);
}

static void graph_free_systems(struct graph_info *ginfo)
{
	gint i;

	if (!ginfo->systems)
		return;

	for (i = 0; ginfo->systems[i]; i++)
		g_free(ginfo->systems[i]);

	g_free(ginfo->systems);
	ginfo->systems = NULL;
	ginfo->systems_size = 0;
}

static void graph_free_events(struct graph_info *ginfo)
{
	g_free(ginfo->event_ids);
	ginfo->event_ids = NULL;
	ginfo->event_ids_size = 0;
}

void trace_graph_event_filter_callback(gboolean accept,
				       gboolean all_events,
				       gchar **systems,
				       gint *events,
				       gpointer data)
{
	struct graph_info *ginfo = data;
	gint i;

	if (!accept)
		return;

	graph_free_systems(ginfo);
	graph_free_events(ginfo);

	if (all_events) {
		ginfo->all_events = TRUE;
		redraw_graph(ginfo);
		return;
	}

	ginfo->all_events = FALSE;

	if (systems) {
		for (ginfo->systems_size = 0;
		     systems[ginfo->systems_size];
		     ginfo->systems_size++)
			;

		ginfo->systems = g_new(typeof(*systems), ginfo->systems_size + 1);
		for (i = 0; i < ginfo->systems_size; i++)
			ginfo->systems[i] = g_strdup(systems[i]);
		ginfo->systems[i] = NULL;

		qsort(ginfo->systems, ginfo->systems_size, sizeof(gchar *), str_cmp);
	}

	if (events) {
		for (ginfo->event_ids_size = 0;
		     events[ginfo->event_ids_size] >= 0;
		     ginfo->event_ids_size++)
			;

		ginfo->event_ids = g_new(typeof(*events), ginfo->event_ids_size + 1);
		for (i = 0; i < ginfo->event_ids_size; i++)
			ginfo->event_ids[i] = events[i];
		ginfo->event_ids[i] = -1;

		qsort(ginfo->event_ids, ginfo->event_ids_size, sizeof(gint), id_cmp);
	}

	redraw_graph(ginfo);
}

static void redraw_pixmap_backend(struct graph_info *ginfo)
{
	GdkPixmap *old_pix;

	old_pix = ginfo->curr_pixmap;

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

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

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

	draw_info(ginfo, ginfo->draw->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) {
//		gtk_adjustment_set_lower(ginfo->vadj, -100.0);
		gtk_adjustment_set_value(ginfo->vadj, ginfo->vadj_value);
	}
}

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

	gtk_widget_set_size_request(widget, ginfo->draw_width, ginfo->draw_height);

	redraw_pixmap_backend(ginfo);

	/* debug */
	ginfo->vadj_value = gtk_adjustment_get_value(ginfo->vadj);
	dprintf(2, "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;

	graph_free_systems(ginfo);
	graph_free_events(ginfo);

	filter_task_hash_free(ginfo->task_filter);
	filter_task_hash_free(ginfo->hide_tasks);

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

	return TRUE;
}

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

	gdk_draw_drawable(ginfo->info->window,
			  ginfo->info->style->fg_gc[GTK_WIDGET_STATE(ginfo->info)],
			  ginfo->info_pixmap,
			  event->area.x, event->area.y,
			  event->area.x, event->area.y,
			  event->area.width, event->area.height);

	return FALSE;
}

static void info_draw_cpu_label(struct graph_info *ginfo, gint cpu)
{
	PangoLayout *layout;
	gchar buf[BUFSIZ];
	gint width, height;

	snprintf(buf, BUFSIZ, "CPU %d", cpu);

	layout = gtk_widget_create_pango_layout(ginfo->info, buf);
	pango_layout_get_pixel_size(layout, &width, &height);
	width += 4;

	if (width > largest_cpu_label)
		largest_cpu_label = width;
	gdk_draw_rectangle(ginfo->info_pixmap,
			   ginfo->info->style->white_gc,
			   TRUE,
			   CPU_X, CPU_LABEL(cpu)+4,
			   width, height);
	gdk_draw_layout(ginfo->info_pixmap,
			ginfo->info->style->black_gc,
			CPU_X+ 2, CPU_LABEL(cpu) + 4,
			layout);
	g_object_unref(layout);
}

static void info_draw_cpu_labels(struct graph_info *ginfo)
{
	gint cpu;
#if 0
	clear_old_cpu_labels(ginfo);
	ginfo->cpu_x = gtk_adjustment_get_value(ginfo->vadj) + 5;
#endif

	for (cpu = 0; cpu < ginfo->cpus; cpu++)
		info_draw_cpu_label(ginfo, cpu);
}

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

	if (ginfo->info_pixmap)
		g_object_unref(ginfo->info_pixmap);

	ginfo->info_pixmap = gdk_pixmap_new(ginfo->info->window,
					    ginfo->info->allocation.width,
					    ginfo->info->allocation.height,
					    -1);

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

	info_draw_cpu_labels(ginfo);

	gtk_widget_set_size_request(ginfo->info, largest_cpu_label + 10,
				    ginfo->draw_height);
	
	return TRUE;
}

static GtkWidget *
create_graph_info(struct graph_info *ginfo)
{
	GtkWidget *info;

	info = gtk_drawing_area_new();

	gtk_signal_connect(GTK_OBJECT(info), "expose_event",
			   (GtkSignalFunc) info_expose_event, ginfo);
	gtk_signal_connect(GTK_OBJECT(info), "configure_event",
			   (GtkSignalFunc) info_configure_event, ginfo);

	gtk_widget_set_events(info, GDK_EXPOSURE_MASK);

	return info;
}

struct graph_info *
trace_graph_create_with_callbacks(struct tracecmd_input *handle,
				  struct graph_callbacks *cbs)
{
	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->all_events = TRUE;

	ginfo->callbacks = cbs;

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

	ginfo->task_filter = filter_task_hash_alloc();
	ginfo->hide_tasks = filter_task_hash_alloc();

	ginfo->widget = gtk_hbox_new(FALSE, 0);
	gtk_widget_show(ginfo->widget);

	ginfo->scrollwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ginfo->scrollwin),
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
	gtk_widget_show(ginfo->scrollwin);


	ginfo->info_scrollwin = gtk_scrolled_window_new(NULL,
		gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(ginfo->scrollwin)));

	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ginfo->info_scrollwin),
				       GTK_POLICY_NEVER,
				       GTK_POLICY_NEVER);
	gtk_widget_show(ginfo->info_scrollwin);
	gtk_box_pack_start(GTK_BOX(ginfo->widget), ginfo->info_scrollwin, FALSE, FALSE, 0);

	ginfo->info = create_graph_info(ginfo);
	gtk_widget_show(ginfo->info);

	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ginfo->info_scrollwin),
					      ginfo->info);

	gtk_box_pack_start(GTK_BOX (ginfo->widget), ginfo->scrollwin, TRUE, TRUE, 0);

	ginfo->draw_height = CPU_SPACE(ginfo->cpus);
	ginfo->vadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(ginfo->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;

		free_record(record);
		record = tracecmd_read_cpu_last(handle, cpu);

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

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

	convert_nano(ginfo->end_time, &sec, &usec);
	dprintf(1, "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);


	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ginfo->scrollwin),
					      ginfo->draw);
	gtk_widget_show(ginfo->draw);

	return ginfo;
}

struct graph_info *
trace_graph_create(struct tracecmd_input *handle)
{
	return trace_graph_create_with_callbacks(handle, NULL);
}