/*
   Copyright (C) 1998,1999,2000,2001,2002,2003
   T. Scott Dattalo and Ralf Forsberg

This file is part of gpsim.

gpsim 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; either version 2, or (at your option)
any later version.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>

#include "../config.h"

//#define GTKEXTRA_2

#ifdef HAVE_GUI

#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <glib.h>
#include <string.h>
#include <math.h>

#include <assert.h>

#include "../src/sim_context.h"
#include "../src/interface.h"
#include "../src/errors.h"

#include "gui.h"
#include "gui_profile.h"
#include "gui_regwin.h"

#include "../src/symbol.h"

#include <gtkextra/gtkplot.h>
#include <gtkextra/gtkplotdata.h>
#include <gtkextra/gtkplotcanvas.h>
#ifdef GTKEXTRA_2
#include <gtkextra/gtkplotcanvasplot.h>
#endif
#include <gtkextra/gtkplotbar.h>
#include <gtkextra/gtkplotps.h>
#include <gtkextra/gtkplotprint.h>

#define PROFILE_COLUMNS    3
static char *profile_titles[PROFILE_COLUMNS]={"Address", "Cycles","Instruction"};

#define PROFILE_RANGE_COLUMNS    3
static char *profile_range_titles[PROFILE_RANGE_COLUMNS]={"Start address", "End address", "Cycles"};

#define PROFILE_REGISTER_COLUMNS    4
static char *profile_register_titles[PROFILE_REGISTER_COLUMNS]={"Address", "Register", "Read count", "Write count"};

#define PROFILE_EXESTATS_COLUMNS    9
static char *profile_exestats_titles[PROFILE_EXESTATS_COLUMNS]={"From address", "To address", "Executions", "Min", "Max",  "Median", "Average", "Std. Dev.", "Total"};

struct profile_entry {
    unsigned int address;
    guint64 last_count;
};

struct profile_range_entry {
    char startaddress_text[64];
    char endaddress_text[64];
    unsigned int startaddress;
    unsigned int endaddress;
    guint64 last_count;
};

struct profile_register_entry {
    unsigned int address;
    guint64 last_count_read;
    guint64 last_count_write;
};

typedef enum {
    MENU_REMOVE_GROUP,
    MENU_ADD_GROUP,
    MENU_ADD_ALL_LABELS,
    MENU_ADD_FUNCTION_LABELS,
    MENU_PLOT,
    MENU_SAVE_PS,
    MENU_PRINT,
} menu_id;

typedef struct _menu_item {
    char *name;
    menu_id id;
    GtkWidget *item;
} menu_item;

static menu_item range_menu_items[] = {
    {"Remove range", MENU_REMOVE_GROUP},
    {"Add range...", MENU_ADD_GROUP},
    {"Add all labels", MENU_ADD_ALL_LABELS},
    {"Add C functions (bad hack (labels not containing \"_DS_\"))", MENU_ADD_FUNCTION_LABELS},
    {"Snapshot to plot", MENU_PLOT},
};

#if 0 // defined but not used
static menu_item plot_menu_items[] = {
    {"Save postscript...", MENU_SAVE_PS},
    {"Print", MENU_PRINT},
};
#endif

static menu_item exestats_menu_items[] = {
    {"Plot distribution", MENU_PLOT},
};


extern int gui_message(char *message);

static GtkStyle *normal_style;

int plot_profile(Profile_Window *pw, char **pointlabel, guint64 *cyclearray, int numpoints);
int plot_routine_histogram(Profile_Window *pw);
float calculate_stddev(GList *start, GList *stop, float average);
double calculate_median(GList *start, GList *stop);

// Used only in popup menus
Profile_Window *popup_pw;


//========================================================================
class ProfileEntry : public GUIRegister {
public:

  Processor *cpu;
  unsigned int address;
  guint64 last_count;

};

//========================================================================
static void remove_entry(Profile_Window *pw, struct profile_entry *entry)
{
    gtk_clist_remove(GTK_CLIST(pw->profile_range_clist),pw->range_current_row);
    pw->profile_range_list=g_list_remove(pw->profile_range_list,entry);
    free(entry);
}

#if 0 // defined but not used
static unsigned int lookup_address_symbol(const char *name)
{
  Symbol_Table &st = CSimulationContext::GetContext()->GetSymbolTable();
  Value *pValue = st.find(name);
  if(pValue != NULL) {
    int i;
    pValue->get(i);
    return i;
  }
  return UINT_MAX;
}

static void add_range(Profile_Window *pw,
		      const char *startaddress_text,
		      const char *endaddress_text)
{
    guint64 gcycles;
    struct profile_range_entry *profile_range_entry;
    unsigned int startaddress;
    unsigned int endaddress;
    char count_string[100];
    char *entry[PROFILE_COLUMNS]={(char *)startaddress_text,(char *)endaddress_text,count_string};
    int row;
    unsigned int i;

    char *end;
    char msg[128];

    startaddress = strtoul(startaddress_text,&end,0);
    if(*end!='\0')
    {
	// Try to look the address up in symbol table.
	startaddress=lookup_address_symbol(startaddress_text);
	if(startaddress==UINT_MAX)
	{
	    startaddress=0;
	    sprintf(msg,"Could not find symbol \"%s\"",startaddress_text);
	    gui_message(msg);
	}
    }

    endaddress = strtoul(endaddress_text,&end,0);
    if(*end!='\0')
    {
	// Try to look the address up in symbol table.
        endaddress=lookup_address_symbol(endaddress_text);
	if(endaddress==UINT_MAX)
	{
	    endaddress=0;
	    sprintf(msg,"Could not find symbol \"%s\"",endaddress_text);
	    gui_message(msg);
	}
    }

    gcycles=0;
    for(i=startaddress;i<endaddress;i++)
    {
      gcycles+=gpGuiProcessor->cpu->cycles_used(i);
    }
    sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",gcycles);

    row=gtk_clist_append(GTK_CLIST(pw->profile_range_clist), entry);

    // FIXME this memory is never freed?
    profile_range_entry = (struct profile_range_entry*)malloc(sizeof(struct profile_range_entry));
    strcpy(profile_range_entry->startaddress_text,startaddress_text);
    strcpy(profile_range_entry->endaddress_text,endaddress_text);
    profile_range_entry->startaddress=startaddress;
    profile_range_entry->endaddress=endaddress;
    profile_range_entry->last_count=gcycles;

    gtk_clist_set_row_data(GTK_CLIST(pw->profile_range_clist), row, (gpointer)profile_range_entry);

    pw->profile_range_list = g_list_append(pw->profile_range_list, (gpointer)profile_range_entry);

    gtk_clist_sort(GTK_CLIST(pw->profile_range_clist));
}

static void a_cb(GtkWidget *w, gpointer user_data)
{
  *(int*)user_data=TRUE;
}

static void b_cb(GtkWidget *w, gpointer user_data)
{
  *(int*)user_data=FALSE;
}

static void add_range_dialog(Profile_Window *pw)
{
    static GtkWidget *dialog=0;
    GtkWidget *button;
    GtkWidget *hbox;
    GtkWidget *label;
    static GtkWidget *startentry;
    static GtkWidget *endentry;
    int retval=-1;

    if(dialog==0)
    {
	dialog = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(dialog),"Add range");
	gtk_signal_connect_object(GTK_OBJECT(dialog),
				  "delete_event",
				  GTK_SIGNAL_FUNC(gtk_widget_hide),
				  GTK_OBJECT(dialog));

	label=gtk_label_new("addresses can be entered either as symbols, or as values. \nValues can be entered in decimal, hexadecimal, and octal.\nFor example: 31 is the same as 0x1f and 037");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,FALSE,FALSE,20);
	
	hbox = gtk_hbox_new(0,0);
	gtk_widget_show(hbox);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,FALSE,FALSE,20);

	button = gtk_button_new_with_label("Add range");
	gtk_widget_show(button);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			   FALSE,FALSE,10);
	gtk_signal_connect(GTK_OBJECT(button),"clicked",
			   GTK_SIGNAL_FUNC(a_cb),(gpointer)&retval);
	
	button = gtk_button_new_with_label("Cancel");
	gtk_widget_show(button);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			   FALSE,FALSE,10);
	gtk_signal_connect(GTK_OBJECT(button),"clicked",
			   GTK_SIGNAL_FUNC(b_cb),(gpointer)&retval);

	label=gtk_label_new("Enter start address");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(hbox), label,
			   FALSE,FALSE, 20);

	startentry=gtk_entry_new();
	gtk_widget_show(startentry);
	gtk_box_pack_start(GTK_BOX(hbox), startentry,FALSE,FALSE,20);

	label=gtk_label_new("Enter stop address");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(hbox), label,
			   FALSE,FALSE, 20);

	endentry=gtk_entry_new();
	gtk_widget_show(endentry);
	gtk_box_pack_start(GTK_BOX(hbox), endentry,FALSE,FALSE,20);


    }

    gtk_widget_show_now(dialog);

    gtk_grab_add(dialog);
    while(retval==-1 && GTK_WIDGET_VISIBLE(dialog))
	gtk_main_iteration();
    gtk_grab_remove(dialog);
    
    gtk_widget_hide(dialog);

    if(retval==(int)TRUE)
    {
	// Add range.

	const gchar *startentry_text;
	const gchar *endentry_text;

	startentry_text = gtk_entry_get_text(GTK_ENTRY(startentry));
	if(*startentry_text!='\0')
	{
	    endentry_text = gtk_entry_get_text(GTK_ENTRY(endentry));
	    if(*endentry_text!='\0')
	    {
                add_range(pw, startentry_text, endentry_text);
	    }
	}
    }
    
    return;
}

/*
 this function compares sym pointers for g_list_sort()
 */
static gint
symcompare(Value *sym1, Value *sym2)
{
  try {
    int i1,i2;

    sym1->get(i1);
    sym2->get(i2);
    if(i1 < i2)
      return -1;

    if(i1 > i2)
      return 1;
  }
  catch (Error *e) {
    
    delete e;
  }
  return 0;
}

static void
file_selection_ok (GtkWidget        *w,
		   GtkFileSelection *fs)
{
    const char *file;

    file=gtk_file_selection_get_filename (fs);
#if GTK_MAJOR_VERSION >= 2
    gtk_plot_canvas_export_ps(GTK_PLOT_CANVAS(popup_pw->plot_canvas), (char *)file,
                              GTK_PLOT_PORTRAIT, 0, GTK_PLOT_LETTER);
#else
    gtk_plot_canvas_export_ps(GTK_PLOT_CANVAS(popup_pw->plot_canvas), (char *)file, 0, 0,
			      GTK_PLOT_LETTER);
#endif

    gtk_widget_hide (GTK_WIDGET (fs));
}

static void
print_plot (Profile_Window *pw)
{
    char *file;
    char cmd[200];

    file=tempnam("/tmp","gpsimplot");
#if GTK_MAJOR_VERSION >= 2
    gtk_plot_canvas_export_ps(GTK_PLOT_CANVAS(popup_pw->plot_canvas), file,
                              GTK_PLOT_PORTRAIT, 0, GTK_PLOT_LETTER);
#else
    gtk_plot_canvas_export_ps(GTK_PLOT_CANVAS(popup_pw->plot_canvas), file, 0, 0,
			      GTK_PLOT_LETTER);
#endif
    sprintf(cmd,"lpr %s",file);
    system(cmd);
    remove(file);
}

extern int gui_question(char *question, char *a, char *b);

static GtkItemFactoryCallback 
open_plotsave_dialog(Profile_Window *pw)
{
    static GtkWidget *window = 0;

    if (!window)
    {

	window = gtk_file_selection_new ("Save postscript to file...");

	gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (window));

	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);

	gtk_signal_connect_object(GTK_OBJECT(window),
				  "delete_event",
				  GTK_SIGNAL_FUNC(gtk_widget_hide),
				  GTK_OBJECT(window));
//	gtk_signal_connect_object (GTK_OBJECT (window), "destroy",
//				   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
//				   GTK_OBJECT(window));

	gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button),
			    "clicked", GTK_SIGNAL_FUNC(file_selection_ok),
			    window);
	gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button),
				   "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
				   GTK_OBJECT (window));
    }
    gtk_widget_show (window);
    return 0;
}


// called when user has selected a menu item in plot window
static void
plot_popup_activated(GtkWidget *widget, gpointer data)
{
    menu_item *item;
    struct profile_entry *entry;

    if(widget==0 || data==0)
    {
	printf("Warning plot_popup_activated(%p,%p)\n",widget,data);
	return;
    }
    
    item = (menu_item *)data;

    entry = (struct profile_entry *)gtk_clist_get_row_data(GTK_CLIST(popup_pw->profile_range_clist),popup_pw->range_current_row);

    switch(item->id)
    {
    case MENU_SAVE_PS:
	open_plotsave_dialog(popup_pw);
	break;
    case MENU_PRINT:
        print_plot(popup_pw);
	break;
    default:
	puts("Unhandled menuitem?");
	break;
    }
}
#endif

// called when user has selected a menu item in exestats tab
static void
exestats_popup_activated(GtkWidget *widget, gpointer data)
{
    menu_item *item;

    if(widget==0 || data==0)
    {
	printf("Warning exestats_popup_activated(%p,%p)\n",widget,data);
	return;
    }
    
    item = (menu_item *)data;

    switch(item->id)
    {
    case MENU_PLOT:
        plot_routine_histogram(popup_pw);
	break;
    default:
	puts("Unhandled menuitem?");
	break;
    }
}

#if 0 // defined but not used
// called from plot_do_popup
static GtkWidget *
plot_build_menu(Profile_Window *pw)
{
  GtkWidget *menu;
  GtkWidget *item;
  unsigned int i;


  if(pw==0)
  {
      printf("Warning build_menu(%p)\n",pw);
      return 0;
  }
    
  popup_pw = pw;
  
  menu=gtk_menu_new();

  item = gtk_tearoff_menu_item_new ();
  gtk_menu_append (GTK_MENU (menu), item);
  gtk_widget_show (item);
  
  for (i=0; i < (sizeof(plot_menu_items)/sizeof(plot_menu_items[0])) ; i++){
      plot_menu_items[i].item=item=gtk_menu_item_new_with_label(plot_menu_items[i].name);

      gtk_signal_connect(GTK_OBJECT(item),"activate",
			 (GtkSignalFunc) plot_popup_activated,
			 &plot_menu_items[i]);
      
      gtk_widget_show(item);
      gtk_menu_append(GTK_MENU(menu),item);
  }

  return menu;
}
#endif

// called from exestats_do_popup
static GtkWidget *
exestats_build_menu(Profile_Window *pw)
{
  GtkWidget *menu;
  GtkWidget *item;
  unsigned int i;


  if(pw==0)
  {
      printf("Warning build_menu(%p)\n",pw);
      return 0;
  }
    
  popup_pw = pw;
  
  menu=gtk_menu_new();

  item = gtk_tearoff_menu_item_new ();
  gtk_menu_append (GTK_MENU (menu), item);
  gtk_widget_show (item);
  
  for (i=0; i < (sizeof(exestats_menu_items)/sizeof(exestats_menu_items[0])) ; i++){
      exestats_menu_items[i].item=item=gtk_menu_item_new_with_label(exestats_menu_items[i].name);

      gtk_signal_connect(GTK_OBJECT(item),"activate",
			 (GtkSignalFunc) exestats_popup_activated,
			 &exestats_menu_items[i]);
      
      gtk_widget_show(item);
      gtk_menu_append(GTK_MENU(menu),item);
  }

  return menu;
}

// button press handler
static gint
exestats_do_popup(GtkWidget *widget, GdkEventButton *event, Profile_Window *pw)
{

    GtkWidget *popup;

    if(widget==0 || event==0 || pw==0)
    {
	printf("Warning exestats_popup(%p,%p,%p)\n",widget,event,pw);
	return 0;
    }

    popup=pw->exestats_popup_menu;

    if( (event->type == GDK_BUTTON_PRESS) &&  (event->button == 3) )
    {

      gtk_menu_popup(GTK_MENU(popup), 0, 0, 0, 0,
		     3, event->time);
    }
    return FALSE;
}

#if 0 // defined but not used
// button press handler
static gint
plot_do_popup(GtkWidget *widget, GdkEventButton *event, Profile_Window *pw)
{

    GtkWidget *popup;

    if(widget==0 || event==0 || pw==0)
    {
	printf("Warning do_popup(%p,%p,%p)\n",widget,event,pw);
	return 0;
    }

    popup=pw->plot_popup_menu;

    if( (event->type == GDK_BUTTON_PRESS) &&  (event->button == 3) )
    {

      gtk_menu_popup(GTK_MENU(popup), 0, 0, 0, 0,
		     3, event->time);
    }
    return FALSE;
}
#endif

int plot_profile(Profile_Window *pw, char **pointlabel, guint64 *cyclearray, int numpoints)
{

#if 0
    static GtkWidget *window1;
    GtkWidget *vbox1;
    GtkWidget *scrollw1;
    static GtkWidget *active_plot;
    static GtkWidget *canvas;
    static GdkColor color1;
    static GdkColor color2;
    static GdkColor bg_color;
    gint page_width, page_height;
    int scale = 1;
    static GtkPlotText *infotext;
    static GtkPlotText **bartext;
    GtkWidget *plot;
    static GtkPlotData *dataset;
    char infostring[128];
    char filename[128];

    int i;

    guint64 i64, x;

    guint64 maxy=0;

    static double *px2;//[] = {.1, .2, .3, .4, .5, .6, .7, .8};
    static double *py2;//[] = {.012*1000, .067*1000, .24*1000, .5*1000, .65*1000, .5*1000, .24*1000, .067*1000};
    gdouble tickdelta;
    gdouble barwidth;
    time_t t;

    static int has_old_graph=0;
    static int last_numpoints=0;

    if(pw->gp->cpu->program_memory_size() <=0)
        return 0;

    if(has_old_graph)
    {
	gtk_plot_remove_text(GTK_PLOT(active_plot),infotext);
	for(i=0;i<last_numpoints;i++)
	{
	    gtk_plot_remove_text(GTK_PLOT(active_plot),bartext[i]);

	}
        free(px2);
        free(py2);
        free(bartext);
    }

    px2=(double*)malloc(numpoints*sizeof(double));
    py2=(double*)malloc(numpoints*sizeof(double));
    bartext=(GtkPlotText**)malloc(numpoints*sizeof(GtkPlotText*));

#define WINDOWWIDTH 550
#define WINDOWHEIGHT 650

#define PLOTXPOS 0.25
#define PLOTWIDTH 0.50
#define PLOTYPOS 0.15
#define PLOTHEIGHT 0.50

    barwidth=PLOTWIDTH/(numpoints*1.1);

    for(i=0;i<numpoints;i++)
    {
	px2[i]=(i+1)*barwidth*2;
	if(maxy<cyclearray[i])
            maxy=cyclearray[i];
	py2[i]=cyclearray[i];
    }

    maxy=maxy + (maxy/10);


    // Compute tickdelta for easy reading.
    x=maxy;
    i64=1;
    while(x>=10L)
    {
	x/=10L;
        i64*=10L;
    }
    tickdelta=x*i64/10;

    if(tickdelta<1)
        tickdelta=1;

    
    if(!pw || !pw->gp || !pw->gp->cpu)
      return 0;

    t=time(0);

    // Compute module name to put in infostring
    for(i=0;i<pw->gp->cpu->files.nsrc_files();i++)
    {
      //struct file_context *gpsim_file;
      FileContext *fc = pw->gp->cpu->files[i];

      const char *file_name;

      if(fc)
	file_name = fc->name().c_str();
      else
	continue;
        

      //gpsim_file = &(gp->cpu->files[i]);
      //file_name = gpsim_file->name;
	if(!strcmp(file_name+strlen(file_name)-4,".asm")
	   ||!strcmp(file_name+strlen(file_name)-4,".ASM")
	   ||!strcmp(file_name+strlen(file_name)-4,".hex")
	   ||!strcmp(file_name+strlen(file_name)-4,".HEX")
	  )
	{
	    strncpy(filename,file_name,strlen(file_name)-4);
	    filename[strlen(file_name)-4]=0;
            break;
	}
    }


    // This information is put at the top of the plot
    sprintf(infostring,"\\BFile:\\N\"%s\" \\BDate:\\N%s \\BProcessor:\\N\"%s\"",
	    filename,
            ctime(&t),
	    pw->gp->cpu->name().c_str());

    // ctime adds a newline. Remove it.
    for(i=0;infostring[i];i++)
	if(infostring[i]=='\n')
	    infostring[i]=' ';



    page_width = GTK_PLOT_LETTER_W * scale;
    page_height = GTK_PLOT_LETTER_H * scale;

    // Only create the window once.
    if(!window1)
    {
	window1=gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window1), "Profile plot");
	gtk_widget_set_usize(window1,WINDOWWIDTH,WINDOWHEIGHT);
	gtk_container_border_width(GTK_CONTAINER(window1),0);

	gtk_signal_connect_object(GTK_OBJECT(window1),
				  "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(window1));
//	gtk_signal_connect_object (GTK_OBJECT (window1), "destroy",
//				   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
//				   GTK_OBJECT(window1));

	vbox1=gtk_vbox_new(FALSE,0);
	gtk_container_add(GTK_CONTAINER(window1),vbox1);
	gtk_widget_show(vbox1);

	scrollw1=gtk_scrolled_window_new(0, 0);
	gtk_container_border_width(GTK_CONTAINER(scrollw1),0);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw1),
				       GTK_POLICY_ALWAYS,GTK_POLICY_ALWAYS);
	gtk_box_pack_start(GTK_BOX(vbox1),scrollw1, TRUE, TRUE,0);
	gtk_widget_show(scrollw1);

	pw->plot_canvas=canvas = gtk_plot_canvas_new(page_width, page_height, 1.);
	GTK_PLOT_CANVAS_SET_FLAGS(GTK_PLOT_CANVAS(canvas), GTK_PLOT_CANVAS_DND_FLAGS);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollw1), canvas);

	gtk_widget_show(canvas);


	pw->plot_popup_menu=plot_build_menu(pw);

	gtk_signal_connect(GTK_OBJECT(canvas),
			   "button_press_event",
			   (GtkSignalFunc) plot_do_popup,
			   pw);

	plot = gtk_plot_new_with_size(0, PLOTWIDTH, PLOTHEIGHT);
	gtk_widget_show(plot);

	active_plot=plot;

	gdk_color_parse("light yellow", &bg_color);
	gdk_color_alloc(gtk_widget_get_colormap(active_plot), &bg_color);
	gtk_plot_set_background(GTK_PLOT(active_plot), &bg_color);

	gdk_color_parse("black", &color1);
	gdk_color_alloc(gtk_widget_get_colormap(active_plot), &color1);
	gdk_color_parse("black", &color2);
	gdk_color_alloc(gtk_widget_get_colormap(canvas), &color2);

#ifdef GTKEXTRA_2
	gtk_plot_hide_legends(GTK_PLOT(active_plot));
	gtk_plot_axis_show_labels(gtk_plot_get_axis(GTK_PLOT(active_plot),GTK_PLOT_AXIS_TOP),0);
	gtk_plot_axis_show_labels(gtk_plot_get_axis(GTK_PLOT(active_plot),GTK_PLOT_AXIS_BOTTOM),0);
	gtk_plot_axis_show_labels(gtk_plot_get_axis(GTK_PLOT(active_plot),GTK_PLOT_AXIS_RIGHT),0);
	gtk_plot_axis_set_visible(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP), TRUE);
	gtk_plot_grids_set_visible(GTK_PLOT(active_plot), TRUE, TRUE, TRUE, TRUE);
	gtk_plot_canvas_put_child(GTK_PLOT_CANVAS(canvas), gtk_plot_canvas_plot_new(GTK_PLOT(active_plot)), PLOTXPOS, PLOTYPOS, PLOTWIDTH, PLOTHEIGHT);
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP));
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_BOTTOM));
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT));
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT));
#else
	gtk_plot_hide_legends(GTK_PLOT(active_plot));

	gtk_plot_axis_show_labels(GTK_PLOT(active_plot),GTK_PLOT_AXIS_TOP,0);
	gtk_plot_axis_show_labels(GTK_PLOT(active_plot),GTK_PLOT_AXIS_BOTTOM,0);
	gtk_plot_axis_show_labels(GTK_PLOT(active_plot),GTK_PLOT_AXIS_RIGHT,0);
	gtk_plot_axis_set_visible(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP, TRUE);
	gtk_plot_grids_set_visible(GTK_PLOT(active_plot), TRUE, TRUE, TRUE, TRUE);
	gtk_plot_canvas_add_plot(GTK_PLOT_CANVAS(canvas), GTK_PLOT(active_plot), PLOTXPOS, PLOTYPOS);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_BOTTOM);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT);
#endif
	gtk_plot_set_legends_border(GTK_PLOT(active_plot), GTK_PLOT_BORDER_SHADOW, 3);
	gtk_plot_legends_move(GTK_PLOT(active_plot), .58, .05);
	gtk_widget_show(active_plot);



	dataset = GTK_PLOT_DATA(gtk_plot_bar_new(GTK_ORIENTATION_VERTICAL));
	gtk_plot_add_data(GTK_PLOT(active_plot), GTK_PLOT_DATA(dataset));
    }

#ifdef GTKEXTRA_2
    gtk_plot_axis_set_ticks(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT), tickdelta, 0);
    gtk_plot_set_range(GTK_PLOT(active_plot), 0., 1., 0., (gdouble)maxy);
    gtk_plot_axis_set_labels_numbers(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT),
				     maxy < 10000 ? GTK_PLOT_LABEL_FLOAT : GTK_PLOT_LABEL_EXP, 0);
#else
    gtk_plot_axis_set_ticks(GTK_PLOT(active_plot), GTK_PLOT_AXIS_Y, tickdelta, 0);
    gtk_plot_set_range(GTK_PLOT(active_plot), 0., 1., 0., (gdouble)maxy);
    gtk_plot_axis_set_labels_numbers(GTK_PLOT(active_plot),
				     GTK_PLOT_AXIS_LEFT,
				     maxy<10000?0:GTK_PLOT_LABEL_EXP,
                                     0);
#endif

    gtk_plot_data_set_points(GTK_PLOT_DATA(dataset), px2, py2, 0, 0, numpoints);
    gtk_plot_data_set_symbol(GTK_PLOT_DATA(dataset),
			     GTK_PLOT_SYMBOL_NONE,
			     GTK_PLOT_SYMBOL_FILLED,
			     0, 4.0, &color1,&color2);
#if GTK_MAJOR_VERSION >= 2
    gtk_plot_data_set_line_attributes(GTK_PLOT_DATA(dataset),
				      GTK_PLOT_LINE_NONE,
                                      GDK_CAP_BUTT, GDK_JOIN_MITER,
				      5, &color2);
#else
    gtk_plot_data_set_line_attributes(GTK_PLOT_DATA(dataset),
				      GTK_PLOT_LINE_NONE,
				      5, &color2);
#endif

    gtk_plot_data_set_connector(GTK_PLOT_DATA(dataset), GTK_PLOT_CONNECT_NONE);

    gtk_widget_show(GTK_WIDGET(dataset));

    // Put the description text under each bar in the plot.
    for(i=0;i<numpoints;i++)
    {

	bartext[i]=gtk_plot_put_text(GTK_PLOT(active_plot),
				     PLOTXPOS+px2[i]*PLOTWIDTH,
				     PLOTYPOS+PLOTHEIGHT+0.01,
				     0,
				     20,
				     270,
				     0,
				     0,
				     TRUE,
				     GTK_JUSTIFY_LEFT,
				     pointlabel[i]);

	gtk_plot_draw_text(GTK_PLOT(active_plot),*bartext[i]);

    }

    infotext=gtk_plot_put_text(GTK_PLOT(active_plot),
			       0.5,
			       PLOTYPOS-0.05,
			       0,
			       20,
			       00,
			       0,
			       0,
			       TRUE,
			       GTK_JUSTIFY_CENTER,
			       infostring);

    gtk_plot_draw_text(GTK_PLOT(active_plot),*infotext);

    gtk_widget_queue_draw(window1);

    gtk_widget_show(window1);

    has_old_graph=1;
    last_numpoints=numpoints;
#endif // 
    return 0;
}

int plot_routine_histogram(Profile_Window *pw)
{
#if 0
    static GtkWidget *window1;
    GtkWidget *vbox1;
    GtkWidget *scrollw1;
    static GtkWidget *active_plot;
    static GtkWidget *canvas;
    static GdkColor color1;
    static GdkColor color2;
    static GdkColor bg_color;
    gint page_width, page_height;
    int scale = 1;
    static GtkPlotText *infotext1;
    static GtkPlotText *infotext2;
    GtkWidget *plot;
    static GtkPlotData *dataset;
    char infostring[128];
    char filename[128];

    int i,j;

    guint64 i64, x, y;
    guint64 mincycles, maxcycles, totalcycles, totalcount;
    double averagecycles, mediancycles, stddevcycles;

    guint64 maxy=0;
    guint64 maxx=0;
    guint64 minx=0xffffffffffffffffull;
    guint64 margin;

    static double *px2;//[] = {.1, .2, .3, .4, .5, .6, .7, .8};
    static double *py2;//[] = {.012*1000, .067*1000, .24*1000, .5*1000, .65*1000, .5*1000, .24*1000, .067*1000};
    gdouble tickdelta_x;
    gdouble tickdelta_y;
    gdouble barwidth;
    time_t t;

    static int has_old_graph=0;
    static int last_numpoints=0;
    int numpoints;
    GList *iter;

    if(!pw || !pw->gp || !pw->gp->cpu)
      return 0;

    if(pw->gp->cpu->program_memory_size() <=0)
      return 0;

    if(pw->histogram_profile_list==0)
        return 0;

    if(has_old_graph)
    {
	gtk_plot_remove_text(GTK_PLOT(active_plot),infotext1);
	gtk_plot_remove_text(GTK_PLOT(active_plot),infotext2);
        free(px2);
        free(py2);
    }

#define WINDOWWIDTH 550
#define WINDOWHEIGHT 650

#define PLOTXPOS 0.25
#define PLOTWIDTH 0.50
#define PLOTYPOS 0.15
#define PLOTHEIGHT 0.50

    // Find the number of points and allocate the point arrays
    numpoints=0;
    iter=pw->histogram_profile_list;
    while(iter!=0)
    {
	numpoints++;
	iter=iter->next;
    }

    px2=(double*)malloc(numpoints*sizeof(double));
    py2=(double*)malloc(numpoints*sizeof(double));

    totalcycles=0;
    totalcount=0;
    // Find values, and put them in the point arrays
    j=0;
    iter=pw->histogram_profile_list;

    while(iter!=0)
    {
	struct cycle_histogram_counter *chc;
        chc=(struct cycle_histogram_counter*)iter->data;

	px2[j]=(double)chc->histo_cycles;
	py2[j]=(double)chc->count;
	if(maxy<chc->count)
	    maxy=chc->count;
	if(maxx<chc->histo_cycles)
	    maxx=chc->histo_cycles;
	if(minx>chc->histo_cycles)
	    minx=chc->histo_cycles;

	totalcycles+=chc->histo_cycles*chc->count;
        totalcount+=chc->count;

        j++;
	iter=iter->next;
    }

    mincycles=minx;
    maxcycles=maxx;
    averagecycles=totalcycles/(float)totalcount;
    mediancycles=calculate_median(pw->histogram_profile_list,0);
    stddevcycles=calculate_stddev(pw->histogram_profile_list,0,averagecycles);

    barwidth=PLOTWIDTH/(1.3*(maxx-minx/*numpoints*/));

    // Compute tickdelta for easy reading.
    y=maxy;
    i64=1;
    while(y>=10L)
    {
	y/=10L;
        i64*=10L;
    }
    tickdelta_y=y*i64/10;
    if(tickdelta_y<1)
	tickdelta_y=1;

    // Compute tickdelta for easy reading.
    x=maxx-minx;
    i64=1;
    while(x>=10L)
    {
	x/=10L;
        i64*=10L;
    }
    tickdelta_x=x*i64/5;
    if(tickdelta_x<1)
        tickdelta_x=1;


    maxy=maxy +(maxy>>3)+1;
    margin = maxx-minx;
    margin= margin + (margin>>3) + (margin>>5)+1;
    maxx=maxx+margin;
    if(minx>margin)
	minx=minx-margin;
    else
        minx=0;

    page_width = GTK_PLOT_LETTER_W * scale;
    page_height = GTK_PLOT_LETTER_H * scale;

    // Only create the window once.
    if(!window1)
    {
	window1=gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window1), "Routine histogram");
	gtk_widget_set_usize(window1,WINDOWWIDTH,WINDOWHEIGHT);
	gtk_container_border_width(GTK_CONTAINER(window1),0);

	gtk_signal_connect_object(GTK_OBJECT(window1),
				  "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(window1));

	vbox1=gtk_vbox_new(FALSE,0);
	gtk_container_add(GTK_CONTAINER(window1),vbox1);
	gtk_widget_show(vbox1);

	scrollw1=gtk_scrolled_window_new(0, 0);
	gtk_container_border_width(GTK_CONTAINER(scrollw1),0);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw1),
				       GTK_POLICY_ALWAYS,GTK_POLICY_ALWAYS);
	gtk_box_pack_start(GTK_BOX(vbox1),scrollw1, TRUE, TRUE,0);
	gtk_widget_show(scrollw1);

	pw->plot_canvas=canvas = gtk_plot_canvas_new(page_width, page_height, 1.);
	GTK_PLOT_CANVAS_SET_FLAGS(GTK_PLOT_CANVAS(canvas), GTK_PLOT_CANVAS_DND_FLAGS);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollw1), canvas);

	gtk_widget_show(canvas);


	pw->plot_popup_menu=plot_build_menu(pw);

	gtk_signal_connect(GTK_OBJECT(canvas),
			   "button_press_event",
			   (GtkSignalFunc) plot_do_popup,
			   pw);

	plot = gtk_plot_new_with_size(0, PLOTWIDTH, PLOTHEIGHT);
	gtk_widget_show(plot);

	active_plot=plot;

	gdk_color_parse("light yellow", &bg_color);
	gdk_color_alloc(gtk_widget_get_colormap(active_plot), &bg_color);
	gtk_plot_set_background(GTK_PLOT(active_plot), &bg_color);

	gdk_color_parse("black", &color1);
	gdk_color_alloc(gtk_widget_get_colormap(active_plot), &color1);
	gdk_color_parse("black", &color2);
	gdk_color_alloc(gtk_widget_get_colormap(canvas), &color2);

#ifdef GTKEXTRA_2
	gtk_plot_hide_legends(GTK_PLOT(active_plot));
	gtk_plot_axis_show_labels(gtk_plot_get_axis(GTK_PLOT(active_plot),GTK_PLOT_AXIS_TOP),0);
	gtk_plot_axis_show_labels(gtk_plot_get_axis(GTK_PLOT(active_plot),GTK_PLOT_AXIS_RIGHT),0);
	gtk_plot_axis_set_visible(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP), TRUE);
	gtk_plot_grids_set_visible(GTK_PLOT(active_plot), TRUE, TRUE, TRUE, TRUE);
	gtk_plot_canvas_put_child(GTK_PLOT_CANVAS(canvas), gtk_plot_canvas_plot_new(GTK_PLOT(active_plot)), PLOTXPOS, PLOTYPOS, PLOTWIDTH, PLOTHEIGHT);
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP));
	gtk_plot_axis_hide_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT));
#else
	gtk_plot_hide_legends(GTK_PLOT(active_plot));
	gtk_plot_axis_show_labels(GTK_PLOT(active_plot),GTK_PLOT_AXIS_TOP,0);
	gtk_plot_axis_show_labels(GTK_PLOT(active_plot),GTK_PLOT_AXIS_RIGHT,0);
	gtk_plot_axis_set_visible(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP, TRUE);
	gtk_plot_grids_set_visible(GTK_PLOT(active_plot), TRUE, TRUE, TRUE, TRUE);
	gtk_plot_canvas_add_plot(GTK_PLOT_CANVAS(canvas), GTK_PLOT(active_plot), PLOTXPOS, PLOTYPOS);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_TOP);
	gtk_plot_axis_hide_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT);
#endif
	gtk_plot_set_legends_border(GTK_PLOT(active_plot), GTK_PLOT_BORDER_SHADOW, 3);
	gtk_plot_legends_move(GTK_PLOT(active_plot), .58, .05);
	gtk_widget_show(active_plot);



	dataset = GTK_PLOT_DATA(gtk_plot_bar_new(GTK_ORIENTATION_VERTICAL));
	gtk_plot_add_data(GTK_PLOT(active_plot), GTK_PLOT_DATA(dataset));
    }

#ifdef GTKEXTRA_2
    gtk_plot_axis_set_ticks(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_RIGHT), tickdelta_y, 1);
    gtk_plot_axis_set_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT), "Frequency");
    gtk_plot_axis_set_labels_numbers(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT),
				     GTK_PLOT_LABEL_FLOAT, 0);

    gtk_plot_axis_set_ticks(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT), tickdelta_x, 1);
    gtk_plot_axis_set_title(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_BOTTOM), "Cycles");
    gtk_plot_axis_set_labels_numbers(gtk_plot_get_axis(GTK_PLOT(active_plot), GTK_PLOT_AXIS_BOTTOM),
				     maxx<10000 ? GTK_PLOT_LABEL_FLOAT : GTK_PLOT_LABEL_EXP, 0);
#else
    gtk_plot_axis_set_ticks(GTK_PLOT(active_plot), GTK_PLOT_AXIS_Y, tickdelta_y, 1);
    gtk_plot_axis_set_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_LEFT, "Frequency");
    gtk_plot_axis_set_labels_numbers(GTK_PLOT(active_plot),
				     GTK_PLOT_AXIS_LEFT,
				     0,
                                     0);

    gtk_plot_axis_set_ticks(GTK_PLOT(active_plot), GTK_PLOT_AXIS_X, tickdelta_x, 1);
    gtk_plot_axis_set_title(GTK_PLOT(active_plot), GTK_PLOT_AXIS_BOTTOM, "Cycles");
    gtk_plot_axis_set_labels_numbers(GTK_PLOT(active_plot),
				     GTK_PLOT_AXIS_BOTTOM,
				     maxx<10000?0:GTK_PLOT_LABEL_EXP,
				     0);
#endif
    gtk_plot_set_range(GTK_PLOT(active_plot), minx, (gdouble)maxx, 0., (gdouble)maxy);

    gtk_plot_data_set_points(GTK_PLOT_DATA(dataset), px2, py2, 0, 0, numpoints);
    gtk_plot_data_set_symbol(GTK_PLOT_DATA(dataset),
			     GTK_PLOT_SYMBOL_NONE,
			     GTK_PLOT_SYMBOL_FILLED,
			     0, 4.0, &color1,&color2);
#if GTK_MAJOR_VERSION >= 2
    gtk_plot_data_set_line_attributes(GTK_PLOT_DATA(dataset),
				      GTK_PLOT_LINE_NONE,
                                      GDK_CAP_BUTT, GDK_JOIN_MITER,
				      5, &color2);
#else
    gtk_plot_data_set_line_attributes(GTK_PLOT_DATA(dataset),
				      GTK_PLOT_LINE_NONE,
				      5, &color2);
#endif

    gtk_plot_data_set_connector(GTK_PLOT_DATA(dataset), GTK_PLOT_CONNECT_NONE);

    gtk_widget_show(GTK_WIDGET(dataset));


    // Infostring1
    t=time(0);
    // Compute module name to put in infostring
    for(i=0;i<pw->gp->cpu->files.nsrc_files();i++)
    {
      //struct file_context *gpsim_file;
      FileContext *fc = pw->gp->cpu->files[i];

      const char *file_name;

      if(fc)
	file_name = fc->name().c_str();
      else
	continue;
        

      //gpsim_file = &(gp->cpu->files[i]);
      //file_name = gpsim_file->name;
	if(!strcmp(file_name+strlen(file_name)-4,".asm")
	   ||!strcmp(file_name+strlen(file_name)-4,".ASM")
	   ||!strcmp(file_name+strlen(file_name)-4,".hex")
	   ||!strcmp(file_name+strlen(file_name)-4,".HEX")
	  )
	{
	    strncpy(filename,file_name,strlen(file_name)-4);
	    filename[strlen(file_name)-4]=0;
            break;
	}
    }
    // This information is put at top of the plot
    sprintf(infostring,"\\BFile:\\N\"%s\" \\BDate:\\N%s \\BProcessor:\\N\"%s\"",
	    filename,
            ctime(&t),
	    pw->gp->cpu->name().c_str());

    // ctime adds a newline. Remove it.
    for(i=0;infostring[i];i++)
	if(infostring[i]=='\n')
	    infostring[i]=' ';
    infotext1=gtk_plot_put_text(GTK_PLOT(active_plot),
				0.5,
				PLOTYPOS-0.05,
				0,
				20,
				00,
				0,
				0,
				TRUE,
				GTK_JUSTIFY_CENTER,
				infostring);
    gtk_plot_draw_text(GTK_PLOT(active_plot),*infotext1);

    static char * pInfoStringFormat = 
      "\\BMin:\\N\%" PRINTF_INT64_MODIFIER
      "d \\BMax:\\N%" PRINTF_INT64_MODIFIER
      "d \\BAverage:\\N%.1f \\BMedian:\\N%.1f \\BStandard deviation:\\N%.1f";
    // Infostring2
    sprintf(infostring,pInfoStringFormat, // "\\BMin:\\N\%lld \\BMax:\\N%lld \\BAverage:\\N%.1f \\BMedian:\\N%.1f \\BStandard deviation:\\N%.1f",
	    mincycles,
	    maxcycles,
	    averagecycles,
	    mediancycles,
	    stddevcycles);
    infotext2=gtk_plot_put_text(GTK_PLOT(active_plot),
				0.5,
				PLOTYPOS-0.03,
				0,
				20,
				00,
				0,
				0,
				TRUE,
				GTK_JUSTIFY_CENTER,
				infostring);
    gtk_plot_draw_text(GTK_PLOT(active_plot),*infotext2);

    gtk_widget_queue_draw(window1);

    gtk_widget_show(window1);

    has_old_graph=1;
    last_numpoints=numpoints;
#endif
    return 0;
}

// called when user has selected a menu item
static void
popup_activated(GtkWidget *widget, gpointer data)
{
#if 0

  ////////
  ///
  ///  THIS CODE IS BROKEN.
  ///
  ////////
    char fromaddress_string[256];
    char toaddress_string[256];
    menu_item *item;
    sym *s;
    GList *symlist=0;
    GList *iter;

    struct profile_entry *entry;
    struct profile_range_entry *range_entry=0;

    if(widget==0 || data==0)
    {
	printf("Warning popup_activated(%p,%p)\n",widget,data);
	return;
    }


    Value *sym=0;
    Symbol_Table_Iterator sti;

    item = (menu_item *)data;
    entry = (struct profile_entry *)gtk_clist_get_row_data(GTK_CLIST(popup_pw->profile_range_clist),popup_pw->range_current_row);

    switch(item->id)
    {
    case MENU_REMOVE_GROUP:
      remove_entry(popup_pw,entry);
      break;
    case MENU_ADD_GROUP:
      add_range_dialog(popup_pw);
      break;
    case MENU_ADD_ALL_LABELS:
      for(sym=sti.begin(); sym != sti.end(); sym = sti.next()) {

      if(typeid(*sym) == typeid(address_symbol)) {

        char *pstr=(char*)malloc(sym->name().length()+1);
        strncpy(pstr,
          sym->name().data(),
          sym->name().length());
        pstr[sym->name().length()]=0;

        sym * data=(sym*)malloc(sizeof(sym));
        data->name = pstr;
        data->type = (*sti)->isa();
        (*sti)->get(data->value);
        symlist=g_list_append(symlist,data);
        }
      }
      symlist=g_list_sort(symlist,(GCompareFunc)symcompare);
      strcpy(fromaddress_string,"0");
      iter=symlist;
      while(iter!=0)
      {
        s=(sym*)iter->data;

        strcpy(toaddress_string,s->name);
        add_range(popup_pw,fromaddress_string,toaddress_string);
        strcpy(fromaddress_string,toaddress_string);
        toaddress_string[0]='\0';
        free(s->name);
        free(s);
        iter=iter->next;
      }

      sprintf(toaddress_string,"%d",gp->cpu->program_memory_size());
      add_range(popup_pw,fromaddress_string,toaddress_string);

      while(symlist!=0)
        symlist=g_list_remove(symlist,symlist->data);

      break;
    case MENU_ADD_FUNCTION_LABELS:
      for(Symbol_Table::iterator sti = st.begin(); sti != st.end(); sti++) {

        if(((*sti)->isa() == SYMBOL_ADDRESS) &&
          !strstr((*sti)->name().data(),"_DS_")) {

          char *pstr=(char*)malloc((*sti)->name().length()+1);
          strncpy(pstr,
                  (*sti)->name().data(),
                  (*sti)->name().length());
          pstr[(*sti)->name().length()]=0;

          sym * data=(sym*)malloc(sizeof(sym));
          data->name = pstr;
          data->type = (*sti)->isa();
          (*sti)->get(data->value);
          symlist=g_list_append(symlist,data);
        }
      }

      symlist=g_list_sort(symlist,(GCompareFunc)symcompare);

      iter=symlist;
      if(iter!=0)
      {
        s=(sym*)iter->data;
        strcpy(fromaddress_string,s->name);
              free(s->name);
        free(s);
        iter=iter->next;
        while(iter!=0)
        {
          s=(sym*)iter->data;
          strcpy(toaddress_string,s->name);
          add_range(popup_pw,fromaddress_string,toaddress_string);
          strcpy(fromaddress_string,toaddress_string);
          toaddress_string[0]='\0';
          free(s);
          iter=iter->next;
	      }
	      sprintf(toaddress_string,"%d",gp->cpu->program_memory_size());
	      add_range(popup_pw,fromaddress_string,toaddress_string);
      }

      while(symlist!=0)
        symlist=g_list_remove(symlist,symlist->data);

      break;
    case MENU_PLOT:
	{
	guint64 *cyclearray;//{100,200,300,400,500,600,900,555};
	char **pointlabel;/*={
	    "start - labelx 0",
	    "start - labelx 1",
	    "start - labelx 2",
	    "start - dgfdslabelx 3",
	    "start - labelx 4",
	    "start - labelx 5",
	    "start - labelx 6",
	    "start - labelx 7"
	};*/
	int numpoints=8;
        int i;

	pointlabel=(char**)malloc(sizeof(char*)*numpoints);
        cyclearray=(guint64*)malloc(sizeof(guint64)*numpoints);

	for(i=0;i<numpoints;i++)
	{
	    range_entry = (struct profile_range_entry *)gtk_clist_get_row_data(GTK_CLIST(popup_pw->profile_range_clist),i);
	    if(range_entry==0)
	    {
		if(i!=0)
		    plot_profile(popup_pw,pointlabel,cyclearray,i);
                break;
	    }
	    else
	    {
                pointlabel[i]=(char*)malloc(128);
		sprintf(pointlabel[i],"%s (end: %s)",range_entry->startaddress_text,range_entry->endaddress_text);
                cyclearray[i]=range_entry->last_count;
	    }
	}
        if(range_entry!=0)
	    plot_profile(popup_pw,pointlabel,cyclearray,numpoints);
	}
        break;
    default:
	puts("Unhandled menuitem?");
	break;
    }
#endif
}

static void update_menus(Profile_Window *pw)
{
    GtkWidget *item;
    struct profile_entry *entry;
    unsigned int i;

    for (i=0; i < (sizeof(range_menu_items)/sizeof(range_menu_items[0])) ; i++){
	item=range_menu_items[i].item;
//	if(range_menu_items[i].id!=MENU_ADD_GROUP)
	{
	    if(pw)
	    {
		entry = (struct profile_entry *)gtk_clist_get_row_data(GTK_CLIST(pw->profile_range_clist),pw->range_current_row);
		if(range_menu_items[i].id!=MENU_ADD_GROUP &&
		   range_menu_items[i].id!=MENU_ADD_ALL_LABELS &&
		   range_menu_items[i].id!=MENU_ADD_FUNCTION_LABELS &&
		   range_menu_items[i].id!=MENU_PLOT &&
		   entry==0)
		    gtk_widget_set_sensitive (item, FALSE);
		else
		    gtk_widget_set_sensitive (item, TRUE);
	    }
	    else
	    {
		gtk_widget_set_sensitive (item, FALSE);
	    }
	}
    }
}

static gint
key_press(GtkWidget *widget,
	  GdkEventKey *key, 
	  gpointer data)
{

  struct profile_range_entry *entry;
  Profile_Window *pw = (Profile_Window *) data;

  if(!pw) return(FALSE);
  if(!pw->gp) return(FALSE);
  if(!pw->gp->cpu) return(FALSE);

  switch(key->keyval) {

  case GDK_Delete:
      entry = (struct profile_range_entry *)gtk_clist_get_row_data(GTK_CLIST(pw->profile_range_clist),pw->range_current_row);
      if(entry!=0)
	  remove_entry(pw,(struct profile_entry *)entry);
      break;
  }
  return TRUE;
}

static gint profile_range_list_row_selected(GtkCList *profilelist,gint row, gint column,GdkEvent *event, Profile_Window *pw)
{
    struct profile_range_entry *entry;
    //    int bit;
    
    pw->range_current_row=row;
//    pw->current_column=column;

    entry = (struct profile_range_entry *)gtk_clist_get_row_data(GTK_CLIST(pw->profile_clist), row);

    if(!entry)
	return TRUE;

    update_menus(pw);
  
    return 0;
}

// called from do_popup
static GtkWidget *
build_menu(Profile_Window *pw)
{
  GtkWidget *menu;
  GtkWidget *item;
  unsigned int i;


  if(!pw)
  {
    printf("Warning profile window is null\n");
    return 0;
  }
    
  popup_pw = pw;
  
  menu=gtk_menu_new();

  item = gtk_tearoff_menu_item_new ();
  gtk_menu_append (GTK_MENU (menu), item);
  gtk_widget_show (item);
  
  for (i=0; i < (sizeof(range_menu_items)/sizeof(range_menu_items[0])) ; i++){
      range_menu_items[i].item=item=gtk_menu_item_new_with_label(range_menu_items[i].name);

      gtk_signal_connect(GTK_OBJECT(item),"activate",
			 (GtkSignalFunc) popup_activated,
			 &range_menu_items[i]);
      
      gtk_widget_show(item);
      gtk_menu_append(GTK_MENU(menu),item);
  }

  update_menus(pw);
  
  return menu;
}

// button press handler
static gint
do_popup(GtkWidget *widget, GdkEventButton *event, Profile_Window *pw)
{

    GtkWidget *popup;

  if(widget==0 || event==0 || pw==0)
  {
      printf("Warning do_popup(%p,%p,%p)\n",widget,event,pw);
      return 0;
  }

  popup=pw->range_popup_menu;
    if( (event->type == GDK_BUTTON_PRESS) &&  (event->button == 3) )
    {

      gtk_menu_popup(GTK_MENU(popup), 0, 0, 0, 0,
		     3, event->time);
    }
    return FALSE;
}

/*
 the function comparing rows of profile list for sorting
 FIXME this can be improved. When we have equal cells in sort_column
 of the two rows, compare another column instead of returning 'match'.
 */
static gint
profile_compare_func(GtkCList *clist, gconstpointer ptr1,gconstpointer ptr2)
{
    char *text1, *text2;
    long val1, val2;
    GtkCListRow *row1 = (GtkCListRow *) ptr1;
    GtkCListRow *row2 = (GtkCListRow *) ptr2;
//    char *p;

    switch (row1->cell[clist->sort_column].type)
    {
    case GTK_CELL_TEXT:
	text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
	break;
    case GTK_CELL_PIXTEXT:
	text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
	break;
    default:
	assert(0);
	break;
    }

    switch (row2->cell[clist->sort_column].type)
    {
    case GTK_CELL_TEXT:
	text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
	break;
    case GTK_CELL_PIXTEXT:
	text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
	break;
    default:
	assert(0);
	break;
    }

    if (!text2)
	assert(0);
    //	return (text1 != 0);

    if (!text1)
	assert(0);
    //	return -1;

    if(1==sscanf(text1,"%li",&val1))
    {
	if(1==sscanf(text2,"%li",&val2))
	{
//	    printf("Value %d %d\n",val1,val2);
	    return val1-val2;
	}
    }
    return strcmp(text1,text2);
}



gint histogram_list_compare_func_cycles(gconstpointer a, gconstpointer b)
{
    const struct cycle_histogram_counter *h1=(struct cycle_histogram_counter*)a;
    const struct cycle_histogram_counter *h2=(struct cycle_histogram_counter*)b;

    if(h1->histo_cycles > h2->histo_cycles)
	return 1;
    if(h1->histo_cycles == h2->histo_cycles)
	return 0;
    return -1;
}

gint histogram_list_compare_func(gconstpointer a, gconstpointer b)
{
    const struct cycle_histogram_counter *h1=(struct cycle_histogram_counter*)a;
    const struct cycle_histogram_counter *h2=(struct cycle_histogram_counter*)b;

    if(h1->start_address > h2->start_address)
	return 1;
    if(h1->start_address == h2->start_address)
    {
	if(h1->stop_address > h2->stop_address)
	    return 1;
	if(h1->stop_address == h2->stop_address)
	{
	    if(h1->histo_cycles*h1->count > h2->histo_cycles*h2->count)
		return 1;
	    if(h1->histo_cycles*h1->count == h2->histo_cycles*h2->count)
		return 0;
	}
    }
    return -1;
}

double calculate_median(GList *start, GList *stop)
{
    GList *sorted_list=0;

    struct cycle_histogram_counter *chc_start, *chc_stop;//, *chc_result;
//    GList *result;
    int count_sum=0;

    if(start==0)
	return -4.2;

    if(stop==0)
    {
        stop=start;
	while(stop->next!=0)
	    stop=stop->next;
    }

    // Copy list and sort it on cycles
    while(start!=stop)
    {
	sorted_list=g_list_append(sorted_list,start->data);
        start=start->next;
    }
    sorted_list=g_list_append(sorted_list,start->data);

    sorted_list=g_list_sort(sorted_list,histogram_list_compare_func_cycles);

    start=sorted_list;
    stop=start;
    while(stop->next!=0)
	stop=stop->next;


    chc_start=(struct cycle_histogram_counter*)start->data;
    chc_stop=(struct cycle_histogram_counter*)stop->data;

    while(start!=stop)
    {
	if(count_sum>=0)
	{
	    // Move start to right
	    start = start->next;
	    count_sum-=chc_start->count;
	    chc_start=(struct cycle_histogram_counter*)start->data;
            continue;
	}
	else
	{
	    // Move stop to left
	    stop=stop->prev;
	    count_sum+=chc_stop->count;
	    chc_stop=(struct cycle_histogram_counter*)stop->data;
            continue;
	}
    }

    if(count_sum>(int)chc_start->count)
    {
        start=start->next;
	chc_start=(struct cycle_histogram_counter*)start->data;
	g_list_free(sorted_list);
	return (double)chc_start->histo_cycles;
    }
    if(-count_sum>(int)chc_start->count)
    {
        start=start->prev;
	chc_start=(struct cycle_histogram_counter*)start->data;
	g_list_free(sorted_list);
	return (double)chc_start->histo_cycles;
    }
    if(-count_sum==(int)chc_start->count)
    {
        stop=stop->prev;
	chc_stop=(struct cycle_histogram_counter*)stop->data;
	g_list_free(sorted_list);
	return (chc_start->histo_cycles+chc_stop->histo_cycles)/2.0;
    }
    if(count_sum==(int)chc_start->count)
    {
        stop=stop->next;
	chc_stop=(struct cycle_histogram_counter*)stop->data;
	g_list_free(sorted_list);
	return (chc_start->histo_cycles+chc_stop->histo_cycles)/2.0;
    }
    if((unsigned int)abs(count_sum)<chc_start->count)
    {
	g_list_free(sorted_list);
	return (double)chc_start->histo_cycles;
    }

    assert(0);
    return 0.0;
}

float calculate_stddev(GList *start, GList *stop, float average)
{
    float variance;
    int count=0;
    float sum=0;
    struct cycle_histogram_counter *chc_start, *chc_stop;

    if(start==stop)
	return 0.0;

    if(stop==0)
    {
        stop=start;
	while(stop->next!=0)
	    stop=stop->next;
    }

    while(start!=stop)
    {
	float diff, diff2;

	chc_start=(struct cycle_histogram_counter*)start->data;
	chc_stop=(struct cycle_histogram_counter*)stop->data;

	diff=chc_start->histo_cycles-average;

	diff2=diff*diff;

	sum+=diff2*chc_start->count;

	count+=chc_start->count;

	start=start->next;
    }

    variance=sum/count;
    return sqrt(variance);
}


void Profile_Window::Update()
{

  unsigned int i;

  char count_string[100];
  GList *iter;

  if(!enabled)
    return;

  if(!gp || !gp->cpu)
  {
    return;
  }

  // Update profile list
  iter=profile_list;
  while(iter)
  {
      struct profile_entry *entry;
      guint64 count;

      entry=(struct profile_entry*)iter->data;

      count=gp->cpu->cycles_used(gp->cpu->map_pm_address2index(entry->address));

      if(entry->last_count!=count)
      {
        int row;

        entry->last_count=count;
        row=gtk_clist_find_row_from_data(GTK_CLIST(profile_clist),entry);
        if(row==-1)
        {
          break;
        }

        sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",count);
        gtk_clist_set_text (GTK_CLIST(profile_clist),row,1,count_string);
      }
      iter=iter->next;
  }
  gtk_clist_sort(profile_clist);


  // Update range list
  iter=profile_range_list;

  while(iter)
  {
      struct profile_range_entry *range_entry;
      guint64 count;
      range_entry=(struct profile_range_entry*)iter->data;

      count=0;
      for(i=range_entry->startaddress;i<range_entry->endaddress;i++)
      {
        count+=gp->cpu->cycles_used(i);
      }

      if(range_entry->last_count!=count)
      {
	  int row;

	  range_entry->last_count=count;
	  row=gtk_clist_find_row_from_data(GTK_CLIST(profile_range_clist),range_entry);
	  if(row==-1)
	  {
	      break;
	  }

	  sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",count);
	  gtk_clist_set_text (GTK_CLIST(profile_range_clist),row,2,count_string);
      }
      iter=iter->next;
  }
  gtk_clist_sort(profile_range_clist);

  // Update register list
  iter=profile_register_list;
  while(iter)
  {
      struct profile_register_entry *register_entry;
      guint64 count_read, count_write;

      register_entry=(struct profile_register_entry*)iter->data;

      Register *reg = gp->cpu->rma.get_register(register_entry->address);
      count_read  = reg->read_access_count;
      count_write = reg->write_access_count;

      if(register_entry->last_count_read!=count_read||
	 register_entry->last_count_write!=count_write)
      {
	  int row;

	  register_entry->last_count_read=count_read;
	  register_entry->last_count_write=count_write;
	  row=gtk_clist_find_row_from_data(GTK_CLIST(profile_register_clist),register_entry);
	  if(row==-1)
	  {
	      break;
	  }

	  sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",count_read);
	  gtk_clist_set_text (GTK_CLIST(profile_register_clist),row,2,count_string);
	  sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",count_write);
	  gtk_clist_set_text (GTK_CLIST(profile_register_clist),row,3,count_string);
      }
      iter=iter->next;
  }

  // Update cummulative statistics list
  histogram_profile_list = g_list_sort(histogram_profile_list,
					   histogram_list_compare_func);
  // Remove all of clist (for now)
  gtk_clist_freeze(GTK_CLIST(profile_exestats_clist));
  gtk_clist_clear(GTK_CLIST(profile_exestats_clist));
  if(histogram_profile_list!=0)
  {
      struct cycle_histogram_counter *chc;
      int count_sum=0;
      unsigned int start=0xffffffff, stop=0xffffffff;
      guint64 min=0xffffffffffffffffULL, max=0;
      guint64 cycles_sum=0;
      GList *list_start=0, *list_end=0;
	char fromaddress_string[100]="";
	char toaddress_string[100]="";
	char executions_string[100]="";
	char min_string[100]="";
	char max_string[100]="";
	char median_string[100]="";
	char average_string[100]="";
	char stddev_string[100]="";
	char total_string[100]="";
	char *entry[PROFILE_EXESTATS_COLUMNS]={
	    fromaddress_string,
	    toaddress_string,
	    executions_string,
            min_string,
	    max_string,
            median_string,
	    average_string,
            stddev_string,
            total_string
	};

	iter=histogram_profile_list;
        list_start = iter;
      while(iter!=0)
      {
	  chc=(struct cycle_histogram_counter*)iter->data;

	  
	  if(start==chc->start_address &&
	     stop==chc->stop_address)
	  {
	      // Add data to statistics

	      count_sum+=chc->count;
	      if(chc->histo_cycles<min)
		  min=chc->histo_cycles;
	      if(chc->histo_cycles>max)
		  max=chc->histo_cycles;
              cycles_sum+=chc->histo_cycles*chc->count;
	  }
	  else
	  {
	      if(count_sum!=0)
	      {
		  // We have data, display it.
		  sprintf(fromaddress_string,"0x%04x",start);
		  sprintf(toaddress_string,"0x%04x",stop);
		  sprintf(executions_string,"%d",count_sum);
		  sprintf(min_string,"%ld",(long)min);
		  sprintf(max_string,"%ld",(long)max);
		  sprintf(median_string,"%.1f", calculate_median(list_start,list_end));
                  sprintf(average_string,"%.1f",cycles_sum/(float)count_sum);
		  sprintf(stddev_string,"%.1f",calculate_stddev(list_start,list_end,cycles_sum/(float)count_sum));
                  sprintf(total_string,"%d",(int)cycles_sum);
		  gtk_clist_append(GTK_CLIST(profile_exestats_clist),entry);
	      }

	      // Start new calculation
	      count_sum=chc->count;
	      start = chc->start_address;
	      stop = chc->stop_address;
	      min=chc->histo_cycles;
	      max=chc->histo_cycles;
	      cycles_sum=chc->histo_cycles*chc->count;
	      list_start = iter;

	  }
          list_end=iter;
          iter=iter->next;
      }
      // add current to clist

      sprintf(fromaddress_string,"0x%04x",start);
      sprintf(toaddress_string,"0x%04x",stop);
      sprintf(executions_string,"%d",count_sum);
      sprintf(min_string,"%ld",(long)min);
      sprintf(max_string,"%ld",(long)max);
      sprintf(median_string,"%.1f", calculate_median(list_start,list_end));
      sprintf(average_string,"%.1f",cycles_sum/(float)count_sum);
      sprintf(stddev_string,"%.1f",calculate_stddev(list_start,list_end,cycles_sum/(float)count_sum));
      sprintf(total_string,"%d",(int)cycles_sum);
      gtk_clist_append(GTK_CLIST(profile_exestats_clist),entry);
  }
  gtk_clist_thaw(GTK_CLIST(profile_exestats_clist));

}

#define END_OF_TIME 0xFFFFFFFFFFFFFFFFULL
static guint64 startcycle=END_OF_TIME;
static unsigned int startaddress;
static guint64 stopcycle=END_OF_TIME;
static unsigned int stopaddress;

//------------------------------------------------------------------------
//
// ProfileStart class
//
class ProfileStart : public TriggerObject
{

public:
  ProfileStart(Profile_Window *_pw, int _address)
  {
    pw = _pw;
    address = _address;
  }

  void callback(void)
  {
    if(!gpGuiProcessor || !gpGuiProcessor->cpu || !pw->gp->cpu)
      return;

    if(startcycle==END_OF_TIME) {
      startcycle   = get_cycles().value;
      startaddress = pw->gp->cpu->pma->get_PC();
    }
  }

private:
  Profile_Window *pw;
  int address;
};

//------------------------------------------------------------------------
//
// ProfileStop class
//
class ProfileStop : public TriggerObject
{

public:
  ProfileStop(Profile_Window *_pw, int _address)
  {
    pw = _pw;
    address = _address;
  }

  void callback(void)
  {
    if(!gpGuiProcessor || !gpGuiProcessor->cpu || !pw->gp->cpu)
      return;

    if(stopcycle==END_OF_TIME && startcycle!=END_OF_TIME) {

      stopcycle = get_cycles().value;
      if(startcycle==stopcycle)
	// This was probably an attempt to measure the whole loop.
	// Set stopcycle to unset, and wait for the next one
	stopcycle=END_OF_TIME;

      else {
	
	guint64 cycles;
	GList *iter;
  stopaddress=pw->gp->cpu->pma->get_PC();

	// We have a new measurement
	cycles=(int)stopcycle-(int)startcycle;

	// Search to see if there are an entry with this startaddress,
	// stopaddress and cycle count.
	iter=pw->histogram_profile_list;
	while(iter!=0) {
	  
	  struct cycle_histogram_counter *chc;
	  chc=(struct cycle_histogram_counter*)iter->data;
	  if(chc->start_address == startaddress &&
	     chc->stop_address == stopaddress &&
	     chc->histo_cycles == cycles)
	    {
	      // If so then add 1 to the counter
	      chc->count++;
	      break;
	    }
	  iter=iter->next;
	}

	if(iter==0) {
	  
	  // Else malloc a new struct, fill with values and add (sorted) to list
	  struct cycle_histogram_counter *chc;

	  chc=(struct cycle_histogram_counter*)malloc(sizeof(struct cycle_histogram_counter));
	  chc->start_address=startaddress;
	  chc->stop_address=stopaddress;
	  chc->histo_cycles=cycles;
	  chc->count=1;

	  pw->histogram_profile_list=g_list_append(pw->histogram_profile_list,chc);
	}

	startcycle=stopcycle=END_OF_TIME;
      }
    }
  }


private:
  Profile_Window *pw;
  int address;
};

/*****************************************************************
 * StartExe
 *
 * Create a 'profile start' object for the program memory.
 *
 */
void Profile_Window::StartExe(int address)
{

  if(!enabled)
    ChangeView(VIEW_SHOW);

  if(gp->cpu->pma->address_has_profile_start(address))
    gp->cpu->pma->clear_profile_start_at_address(address);
  else {

    if(gp->cpu->pma->address_has_profile_stop(address))
      // Can't have both start and stop at the same address
      // ..it becomes difficult to calculate the cycles
      gp->cpu->pma->clear_profile_stop_at_address(address);

    // FIXME -- memory leak...
    gp->cpu->pma->set_profile_start_at_address(address,
					      new ProfileStart(this,address));

  }

}

/*****************************************************************
 * SopExe
 *
 * Create a 'profile stop' object for the program memory.
 *
 */
void Profile_Window::StopExe(int address)
{
  if(enabled)
    ChangeView(VIEW_SHOW);
      
  if(gp->cpu->pma->address_has_profile_stop(address))
    gp->cpu->pma->clear_profile_stop_at_address(address);
  else {
	
    if(gp->cpu->pma->address_has_profile_start(address))
      // Can't have both start and stop at the same address
      // ..it becomes difficult to calculate the cycles
      gp->cpu->pma->clear_profile_start_at_address(address);
	
    // FIXME -- memory leak...
    gp->cpu->pma->set_profile_stop_at_address(address,
					      new ProfileStop(this,address));
  }
}

/*****************************************************************
 * ProfileWindow_new_program
 *
 * 
 */

void Profile_Window::NewProgram(GUI_Processor *_gp)
{
  int row;
  unsigned int uPMIndex;

  if(!_gp)
    return;

  gp = _gp;

  if(!gp->cpu)
    return;

  program=1;
    
  if(!enabled)
    return;
    
  profile_keeper.enable_profiling();

  // Instruction clist
  gtk_clist_freeze(profile_clist);
  Processor *pProcessor = gp->cpu;
  ProgramMemoryAccess *pPMA = pProcessor->pma;
  for(uPMIndex=0; uPMIndex < pProcessor->program_memory_size(); uPMIndex++) {
    
    struct profile_entry *profile_entry;
    char address_string[100];
    char instruction_string[100];
    char count_string[100];
    char *entry[PROFILE_COLUMNS]={address_string,count_string,instruction_string};
    guint64 cycles;
    instruction * pInstruction = pProcessor->pma->getFromIndex(uPMIndex);
    unsigned int uAddress = pProcessor->map_pm_index2address(uPMIndex);
    if(pPMA->hasValid_opcode_at_index(uPMIndex)) {
	
      sprintf(address_string, "0x%04x",uAddress);
      strcpy(instruction_string, pInstruction->name().c_str());

      cycles=pProcessor->cycles_used(uPMIndex);
      sprintf(count_string,"0x%" PRINTF_INT64_MODIFIER "x",cycles);

      row=gtk_clist_append(GTK_CLIST(profile_clist), entry);

      // FIXME this memory is never freed?
      profile_entry = (struct profile_entry*)malloc(sizeof(struct profile_entry));
      profile_entry->address=uAddress;
      profile_entry->last_count=cycles;

      gtk_clist_set_row_data(GTK_CLIST(profile_clist), row, (gpointer)profile_entry);

      profile_list = g_list_append(profile_list, (gpointer)profile_entry);
    }
  }
  gtk_clist_thaw(profile_clist);

  // Register clist
  gtk_clist_freeze(profile_register_clist);
  for(unsigned int i=0; i < pProcessor->rma.get_size(); i++) {
    
    struct profile_register_entry *profile_register_entry;
    char address_string[100];
    char count_string_read[100];
    char count_string_write[100];
    char register_string[100];
    char *entry_register[PROFILE_REGISTER_COLUMNS]={address_string,register_string,count_string_read,count_string_write};
    guint64 read_cycles;
    guint64 write_cycles;
    char *name;

    Register *reg = pProcessor->rma.get_register(i);

    //
    // If the register is valid, but it's not aliased and it's not a special function
    // register, then we can profile it.
    //

    if(reg && reg->isa() != Register::INVALID_REGISTER    // i.e. the register is valid
          &&
       !((reg->isa() == Register::SFR_REGISTER) || (i != reg->address)) ) {

	sprintf(address_string,"0x%04x",i);
	name = (char*)reg->name().c_str();
	if(name==0)
	  name = address_string;
	strcpy(register_string, name);

	read_cycles=reg->read_access_count;
	sprintf(count_string_read,"0x%" PRINTF_INT64_MODIFIER "x",read_cycles);

	write_cycles=reg->write_access_count;
	sprintf(count_string_write,"0x%" PRINTF_INT64_MODIFIER "x",write_cycles);

	row=gtk_clist_append(GTK_CLIST(profile_register_clist), entry_register);

	// FIXME this memory is never freed?
	profile_register_entry = (struct profile_register_entry*) malloc(sizeof(struct profile_register_entry));
	profile_register_entry->address=i;
	profile_register_entry->last_count_read=read_cycles;
	profile_register_entry->last_count_read=write_cycles;

	gtk_clist_set_row_data(GTK_CLIST(profile_register_clist), row, (gpointer)profile_register_entry);

	profile_register_list = g_list_append(profile_register_list, (gpointer)profile_register_entry);
      }
  }
  gtk_clist_thaw(profile_register_clist);

}

/*****************************************************************
 * ProfileWindow_new_processor
 *
 * 
 */

void Profile_Window::NewProcessor(GUI_Processor *_gp)
{

  if(!gp)
    return;

  if(!enabled)
    return;
}

static int delete_event(GtkWidget *widget,
			GdkEvent  *event,
                        Register_Window *rw)
{
  rw->ChangeView(VIEW_HIDE);

  return TRUE;
}

gdouble gaussian(GtkPlot *plot, GtkPlotData *data, gdouble x, gboolean *err)
{
 gdouble y;
 *err = FALSE;
 y = 1000*x;//.65*exp(-.5*pow(x-.5,2)/.02);

 return y;
}

void Profile_Window::Build(void)
{
  if(bIsBuilt)
    return;

  GtkWidget *label;
  GtkWidget *main_vbox;
  GtkWidget *scrolled_window;
    
  gint column_width,char_width;
  window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect(GTK_OBJECT (window), "delete_event",
		     GTK_SIGNAL_FUNC(delete_event), this);

  main_vbox=gtk_vbox_new(FALSE,1);
  gtk_container_set_border_width(GTK_CONTAINER(main_vbox),0); 
  gtk_container_add(GTK_CONTAINER(window), main_vbox);
  gtk_widget_show(main_vbox);

  gtk_window_set_title(GTK_WINDOW(window), "profile viewer");

  notebook = gtk_notebook_new();
  gtk_widget_show(notebook);

  gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);


  // Instruction profile clist
  profile_clist=GTK_CLIST(gtk_clist_new_with_titles(PROFILE_COLUMNS,profile_titles));
  //profile_clist = GTK_CLIST(profile_clist);
  gtk_clist_set_column_auto_resize(GTK_CLIST(profile_clist),0,TRUE);
  gtk_clist_set_column_auto_resize(GTK_CLIST(profile_clist),1,TRUE);
//  gtk_clist_set_sort_column (pw->profile_clist,1);
//  gtk_clist_set_sort_type (pw->profile_clist,GTK_SORT_DESCENDING);
  gtk_clist_set_compare_func(GTK_CLIST(profile_clist),
			     (GtkCListCompareFunc)profile_compare_func);

  GTK_WIDGET_UNSET_FLAGS(profile_clist,GTK_CAN_DEFAULT);

  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(profile_clist));
  
  gtk_widget_show(GTK_WIDGET(profile_clist));

  gtk_widget_show(scrolled_window);

//  gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
  label=gtk_label_new("Instruction profile");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),scrolled_window,label);
  ///////////////////////////////////////////////////
  ///////////////////////////////////////////////////


  // Instruction range profile clist
  profile_range_clist=GTK_CLIST(gtk_clist_new_with_titles(PROFILE_RANGE_COLUMNS,profile_range_titles));
  gtk_clist_set_column_auto_resize(profile_range_clist,0,TRUE);
  gtk_clist_set_column_auto_resize(profile_range_clist,1,TRUE);
  gtk_clist_set_sort_column (profile_range_clist,2);
  gtk_clist_set_sort_type (profile_range_clist,GTK_SORT_DESCENDING);
  gtk_clist_set_compare_func(GTK_CLIST(profile_range_clist),
			     (GtkCListCompareFunc)profile_compare_func);

  GTK_WIDGET_UNSET_FLAGS(profile_range_clist,GTK_CAN_DEFAULT);
    
  range_popup_menu=build_menu(this);

  gtk_signal_connect(GTK_OBJECT(profile_range_clist),
		     "button_press_event",
		     (GtkSignalFunc) do_popup,
		     this);
  gtk_signal_connect(GTK_OBJECT(profile_range_clist),"key_press_event",
		     (GtkSignalFunc) key_press,
		     (gpointer) this);
  gtk_signal_connect(GTK_OBJECT(profile_range_clist),"select_row",
		     (GtkSignalFunc)profile_range_list_row_selected,this);

  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(profile_range_clist));
  
  gtk_widget_show(GTK_WIDGET(profile_range_clist));

  gtk_widget_show(scrolled_window);

  label=gtk_label_new("Instruction range profile");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),scrolled_window,label);

  ///////////////////////////////////////////////////


  // Register profile clist
  profile_register_clist=GTK_CLIST(gtk_clist_new_with_titles(PROFILE_REGISTER_COLUMNS,profile_register_titles));
  gtk_clist_set_column_auto_resize(profile_register_clist,0,TRUE);
  gtk_clist_set_column_auto_resize(profile_register_clist,1,TRUE);
  gtk_clist_set_column_auto_resize(profile_register_clist,2,TRUE);
  gtk_clist_set_column_auto_resize(profile_register_clist,3,TRUE);
//  gtk_clist_set_sort_column (pw->profile_register_clist,1);
//  gtk_clist_set_sort_type (pw->profile_register_clist,GTK_SORT_DESCENDING);
  gtk_clist_set_compare_func(profile_register_clist,
			     (GtkCListCompareFunc)profile_compare_func);

  GTK_WIDGET_UNSET_FLAGS(profile_register_clist,GTK_CAN_DEFAULT);
    
  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(profile_register_clist));
  
  gtk_widget_show(GTK_WIDGET(profile_register_clist));

  gtk_widget_show(scrolled_window);

//  gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
  label=gtk_label_new("Register profile");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),scrolled_window,label);

  ///////////////////////////////////////////////////


  // Execution time statistics tab
  profile_exestats_clist=GTK_CLIST(gtk_clist_new_with_titles(PROFILE_EXESTATS_COLUMNS,profile_exestats_titles));
  gtk_clist_set_column_auto_resize(profile_exestats_clist,0,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,1,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,2,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,3,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,4,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,5,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,6,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,7,TRUE);
  gtk_clist_set_column_auto_resize(profile_exestats_clist,8,TRUE);

  GTK_WIDGET_UNSET_FLAGS(profile_exestats_clist,GTK_CAN_DEFAULT);

  exestats_popup_menu=exestats_build_menu(this);
  gtk_signal_connect(GTK_OBJECT(profile_exestats_clist),
		     "button_press_event",
		     (GtkSignalFunc) exestats_do_popup,
		     this);

  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(profile_exestats_clist));
  
  gtk_widget_show(GTK_WIDGET(profile_exestats_clist));

  gtk_widget_show(scrolled_window);

  label=gtk_label_new("Routine profile");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),scrolled_window,label);
  ///////////////////////////////////////////////////


  gtk_window_set_default_size(GTK_WINDOW(window), width,height);
  gtk_widget_set_uposition(GTK_WIDGET(window),x,y);
  gtk_window_set_wmclass(GTK_WINDOW(window),name(),"Gpsim");

  normal_style = gtk_style_new ();
#if GTK_MAJOR_VERSION >= 2
  char_width = gdk_string_width(gtk_style_get_font(normal_style) ,"9");
#else
  char_width = gdk_string_width (normal_style->font,"9");
#endif
  column_width = 3 * char_width + 6;

  gtk_signal_connect_after(GTK_OBJECT(window), "configure_event",
  			   GTK_SIGNAL_FUNC(gui_object_configure_event),this);



  gtk_widget_show (window);


  bIsBuilt=true;

  NewProcessor(gp);

  if(program)
    NewProgram(gp);

  Update();

  UpdateMenuItem();

}

Profile_Window::Profile_Window(GUI_Processor *_gp)
{

  menu = "<main>/Windows/Profile";

  gp = _gp;
  set_name("profile");
  window = 0;
  wc = WC_data;
  wt = WT_profile_window;

  profile_list=0;
  profile_range_list=0;
  profile_register_list=0;
  histogram_profile_list=0;

  program=0;

  get_config();

  if(enabled)
      Build();

}


#endif // HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1