/* 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 #include #include #include #include "../config.h" //#define GTKEXTRA_2 #ifdef HAVE_GUI #include #include #include #include #include #include #include #include #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 #include #include #ifdef GTKEXTRA_2 #include #endif #include #include #include #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;icpu->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=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;igp->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;igp || !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(maxycount) maxy=chc->count; if(maxxhisto_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;igp->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;iprofile_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)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;iendaddress;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_cycleshisto_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 = "
/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