/* ----------------------------------------------------------------------------- * * (c) The GHC Team 2000 * * RTS GTK Front Panel * * ---------------------------------------------------------------------------*/ #ifdef RTS_GTK_FRONTPANEL /* Alas, not Posix. */ /* #include "PosixSource.h" */ #include "Rts.h" #include "RtsUtils.h" #include "MBlock.h" #include "FrontPanel.h" #include "Stats.h" #include "RtsFlags.h" #include "Schedule.h" #include #include #include #include "VisSupport.h" #include "VisWindow.h" static GtkWidget *window, *map_drawing_area, *gen_drawing_area; static GtkWidget *res_drawing_area; static GtkWidget *continue_but, *stop_but, *quit_but; static GtkWidget *statusbar; static GtkWidget *live_label, *allocated_label; static GtkWidget *footprint_label, *alloc_rate_label; static GtkWidget *map_ruler, *gen_ruler; static GtkWidget *res_vruler, *res_hruler; static GtkWidget *running_label, *b_read_label, *b_write_label, *total_label; static GtkWidget *b_mvar_label, *b_bh_label, *b_throwto_label, *sleeping_label; static guint status_context_id; gboolean continue_now = FALSE, stop_now = FALSE, quit = FALSE; UpdateMode update_mode = Continuous; static GdkPixmap *map_pixmap = NULL; static GdkPixmap *gen_pixmap = NULL; static GdkPixmap *res_pixmap = NULL; #define N_GENS 10 static GdkColor bdescr_color = { 0, 0xffff, 0, 0 }, /* red */ free_color = { 0, 0, 0, 0xffff }, /* blue */ gen_colors[N_GENS] = { { 0, 0, 0xffff, 0 }, { 0, 0, 0xf000, 0 }, { 0, 0, 0xe000, 0 }, { 0, 0, 0xd000, 0 }, { 0, 0, 0xc000, 0 }, { 0, 0, 0xb000, 0 }, { 0, 0, 0xa000, 0 }, { 0, 0, 0x9000, 0 }, { 0, 0, 0x8000, 0 }, { 0, 0, 0x7000, 0 } }; GdkGC *my_gc = NULL; static void *mem_start = (void *) 0x50000000; static void colorBlock( void *addr, GdkColor *color, nat block_width, nat block_height, nat blocks_per_line ); static void residencyCensus( void ); static void updateResidencyGraph( void ); static void updateThreadsPanel( void ); /* Some code pinched from examples/scribble-simple in the GTK+ * distribution. */ /* Create a new backing pixmap of the appropriate size */ static gint configure_event( GtkWidget *widget, GdkEventConfigure *event STG_UNUSED, GdkPixmap **pixmap ) { if (*pixmap) gdk_pixmap_unref(*pixmap); *pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); gdk_draw_rectangle (*pixmap, widget->style->white_gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); debugBelch("configure!\n"); updateFrontPanel(); return TRUE; } /* Redraw the screen from the backing pixmap */ static gint expose_event( GtkWidget *widget, GdkEventExpose *event, GdkPixmap **pixmap ) { gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], *pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } void initFrontPanel( void ) { GdkColormap *colormap; GtkWidget *gen_hbox; gtk_init( &prog_argc, &prog_argv ); window = create_GHC_Front_Panel(); map_drawing_area = lookup_widget(window, "memmap"); gen_drawing_area = lookup_widget(window, "generations"); res_drawing_area = lookup_widget(window, "res_drawingarea"); stop_but = lookup_widget(window, "stop_but"); continue_but = lookup_widget(window, "continue_but"); quit_but = lookup_widget(window, "quit_but"); statusbar = lookup_widget(window, "statusbar"); live_label = lookup_widget(window, "live_label"); footprint_label = lookup_widget(window, "footprint_label"); allocated_label = lookup_widget(window, "allocated_label"); alloc_rate_label = lookup_widget(window, "alloc_rate_label"); gen_hbox = lookup_widget(window, "gen_hbox"); gen_ruler = lookup_widget(window, "gen_ruler"); map_ruler = lookup_widget(window, "map_ruler"); res_vruler = lookup_widget(window, "res_vruler"); res_hruler = lookup_widget(window, "res_hruler"); running_label = lookup_widget(window, "running_label"); b_read_label = lookup_widget(window, "blockread_label"); b_write_label = lookup_widget(window, "blockwrite_label"); b_mvar_label = lookup_widget(window, "blockmvar_label"); b_bh_label = lookup_widget(window, "blockbh_label"); b_throwto_label = lookup_widget(window, "blockthrowto_label"); sleeping_label = lookup_widget(window, "sleeping_label"); total_label = lookup_widget(window, "total_label"); status_context_id = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "context" ); /* hook up some signals for the mem map drawing area */ gtk_signal_connect (GTK_OBJECT(map_drawing_area), "expose_event", (GtkSignalFunc)expose_event, &map_pixmap); gtk_signal_connect (GTK_OBJECT(map_drawing_area), "configure_event", (GtkSignalFunc)configure_event, &map_pixmap); gtk_widget_set_events(map_drawing_area, GDK_EXPOSURE_MASK); /* hook up some signals for the gen drawing area */ gtk_signal_connect (GTK_OBJECT(gen_drawing_area), "expose_event", (GtkSignalFunc)expose_event, &gen_pixmap); gtk_signal_connect (GTK_OBJECT(gen_drawing_area), "configure_event", (GtkSignalFunc)configure_event, &gen_pixmap); gtk_widget_set_events(gen_drawing_area, GDK_EXPOSURE_MASK); /* hook up some signals for the res drawing area */ gtk_signal_connect (GTK_OBJECT(res_drawing_area), "expose_event", (GtkSignalFunc)expose_event, &res_pixmap); gtk_signal_connect (GTK_OBJECT(res_drawing_area), "configure_event", (GtkSignalFunc)configure_event, &res_pixmap); gtk_widget_set_events(res_drawing_area, GDK_EXPOSURE_MASK); /* allocate our colors */ colormap = gdk_colormap_get_system(); gdk_colormap_alloc_color(colormap, &bdescr_color, TRUE, TRUE); gdk_colormap_alloc_color(colormap, &free_color, TRUE, TRUE); { gboolean success[N_GENS]; gdk_colormap_alloc_colors(colormap, gen_colors, N_GENS, TRUE, TRUE, success); if (!success) { barf("can't allocate colors"); } } /* set the labels on the generation histogram */ { char buf[64]; nat g, s; GtkWidget *label; for(g = 0; g < RtsFlags.GcFlags.generations; g++) { for(s = 0; s < generations[g].n_steps; s++) { g_snprintf( buf, 64, "%d.%d", g, s ); label = gtk_label_new( buf ); gtk_box_pack_start( GTK_BOX(gen_hbox), label, TRUE, TRUE, 5 ); gtk_widget_show(label); } } } gtk_widget_show(window); /* wait for the user to press "Continue" before getting going... */ gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, "Program start"); gtk_widget_set_sensitive( stop_but, FALSE ); continue_now = FALSE; while (continue_now == FALSE) { gtk_main_iteration(); } gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id ); gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, "Running"); gtk_widget_set_sensitive( continue_but, FALSE ); gtk_widget_set_sensitive( stop_but, TRUE ); gtk_widget_set_sensitive( quit_but, FALSE ); while (gtk_events_pending()) { gtk_main_iteration(); } } void stopFrontPanel( void ) { gtk_widget_set_sensitive( quit_but, TRUE ); gtk_widget_set_sensitive( continue_but, FALSE ); gtk_widget_set_sensitive( stop_but, FALSE ); updateFrontPanel(); gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, "Program finished"); quit = FALSE; while (quit == FALSE) { gtk_main_iteration(); } } static void waitForContinue( void ) { gtk_widget_set_sensitive( continue_but, TRUE ); gtk_widget_set_sensitive( stop_but, FALSE ); stop_now = FALSE; continue_now = FALSE; while (continue_now == FALSE) { gtk_main_iteration(); } gtk_widget_set_sensitive( continue_but, FALSE ); gtk_widget_set_sensitive( stop_but, TRUE ); } void updateFrontPanelBeforeGC( nat N ) { char buf[1000]; updateFrontPanel(); if (update_mode == BeforeGC || update_mode == BeforeAfterGC || stop_now == TRUE) { g_snprintf( buf, 1000, "Stopped (before GC, generation %d)", N ); gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf ); waitForContinue(); gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id ); } g_snprintf( buf, 1000, "Garbage collecting (generation %d)", N ); gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf); while (gtk_events_pending()) { gtk_main_iteration(); } } static void numLabel( GtkWidget *lbl, nat n ) { char buf[64]; g_snprintf(buf, 64, "%d", n); gtk_label_set_text( GTK_LABEL(lbl), buf ); } void updateFrontPanelAfterGC( nat N, lnat live ) { char buf[1000]; gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id ); /* is a major GC? */ if (N == RtsFlags.GcFlags.generations-1) { residencyCensus(); } updateFrontPanel(); if (update_mode == AfterGC || update_mode == BeforeAfterGC || stop_now == TRUE) { snprintf( buf, 1000, "Stopped (after GC, generation %d)", N ); gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf ); waitForContinue(); gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id ); } { double words_to_megs = (1024 * 1024) / sizeof(W_); double time = mut_user_time(); snprintf( buf, 1000, "%.2f", (double)live / words_to_megs ); gtk_label_set_text( GTK_LABEL(live_label), buf ); snprintf( buf, 1000, "%.2f", (double)total_allocated / words_to_megs ); gtk_label_set_text( GTK_LABEL(allocated_label), buf ); snprintf( buf, 1000, "%.2f", (double)(mblocks_allocated * MBLOCK_SIZE_W) / words_to_megs ); gtk_label_set_text( GTK_LABEL(footprint_label), buf ); if ( time == 0.0 ) snprintf( buf, 1000, "%.2f", time ); else snprintf( buf, 1000, "%.2f", (double)(total_allocated / words_to_megs) / time ); gtk_label_set_text( GTK_LABEL(alloc_rate_label), buf ); } while (gtk_events_pending()) { gtk_main_iteration(); } } void updateFrontPanel( void ) { void *m, *a; bdescr *bd; updateThreadsPanel(); if (my_gc == NULL) { my_gc = gdk_gc_new( window->window ); } if (map_pixmap != NULL) { nat height, width, blocks_per_line, block_height, block_width, mblock_height; height = map_drawing_area->allocation.height; width = map_drawing_area->allocation.width; mblock_height = height / mblocks_allocated; blocks_per_line = 16; block_height = mblock_height / ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line); while (block_height == 0) { blocks_per_line *= 2; block_height = mblock_height / ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line); } block_width = width / blocks_per_line; gdk_draw_rectangle (map_pixmap, map_drawing_area->style->bg_gc[GTK_STATE_NORMAL], TRUE, 0, 0, map_drawing_area->allocation.width, map_drawing_area->allocation.height); for ( m = mem_start; (char *)m < (char *)mem_start + (mblocks_allocated * MBLOCK_SIZE); (char *)m += MBLOCK_SIZE ) { /* color the bdescr area first */ for (a = m; a < FIRST_BLOCK(m); (char *)a += BLOCK_SIZE) { colorBlock( a, &bdescr_color, block_width, block_height, blocks_per_line ); } #if 0 /* Segfaults because bd appears to be bogus but != NULL. stolz, 2003-06-24 */ /* color each block */ for (; a <= LAST_BLOCK(m); (char *)a += BLOCK_SIZE) { bd = Bdescr((P_)a); ASSERT(bd->start == a); if (bd->flags & BF_FREE) { colorBlock( a, &free_color, block_width, block_height, blocks_per_line ); } else { colorBlock( a, &gen_colors[bd->gen_no], block_width, block_height, blocks_per_line ); } } #endif } { nat height = map_drawing_area->allocation.height, block_height, mblock_height; block_height = (height / mblocks_allocated) / ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line); if (block_height < 1) block_height = 1; mblock_height = block_height * ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line); gtk_ruler_set_range( GTK_RULER(map_ruler), 0, (double)(height * mblocks_allocated) / (double)((mblock_height * mblocks_allocated)), 0, (double)(height * mblocks_allocated) / (double)((mblock_height * mblocks_allocated)) ); } gtk_widget_draw( map_drawing_area, NULL ); } if (gen_pixmap != NULL) { GdkRectangle rect; nat g, s, columns, column, max_blocks, height_blocks, width, height; gdk_draw_rectangle (gen_pixmap, gen_drawing_area->style->white_gc, TRUE, 0, 0, gen_drawing_area->allocation.width, gen_drawing_area->allocation.height); height = gen_drawing_area->allocation.height; width = gen_drawing_area->allocation.width; columns = 0; max_blocks = 0; for(g = 0; g < RtsFlags.GcFlags.generations; g++) { columns += generations[g].n_steps; for(s = 0; s < generations[g].n_steps; s++) { if (generations[g].steps[s].n_blocks > max_blocks) { max_blocks = generations[g].steps[s].n_blocks; } } } /* find a reasonable height value larger than max_blocks */ { nat n = 0; while (max_blocks != 0) { max_blocks >>= 1; n++; } height_blocks = 1 << n; } column = 0; for(g = 0; g < RtsFlags.GcFlags.generations; g++) { for(s = 0; s < generations[g].n_steps; s++, column++) { gdk_gc_set_foreground(my_gc, &gen_colors[g]); rect.x = column * (width / columns); if (generations[g].steps[s].n_blocks == 0) rect.y = height; else rect.y = height - (height * generations[g].steps[s].n_blocks / height_blocks); rect.width = (width / columns); rect.height = height - rect.y; gdk_draw_rectangle( gen_pixmap, my_gc, TRUE/*filled*/, rect.x, rect.y, rect.width, rect.height ); } } gtk_ruler_set_range( GTK_RULER(gen_ruler), height_blocks * BLOCK_SIZE / (1024 * 1024), 0, 0, height_blocks * BLOCK_SIZE / (1024 * 1024) ); gtk_widget_draw( gen_drawing_area, NULL ); } if (res_pixmap != NULL) { updateResidencyGraph(); } while (gtk_events_pending()) { gtk_main_iteration_do(FALSE/*don't block*/); } } static void colorBlock( void *addr, GdkColor *color, nat block_width, nat block_height, nat blocks_per_line ) { GdkRectangle rect; nat block_no; gdk_gc_set_foreground(my_gc, color); block_no = ((char *)addr - (char *)mem_start) / BLOCK_SIZE; rect.x = (block_no % blocks_per_line) * block_width; rect.y = block_no / blocks_per_line * block_height; rect.width = block_width; rect.height = block_height; gdk_draw_rectangle( map_pixmap, my_gc, TRUE/*filled*/, rect.x, rect.y, rect.width, rect.height ); } static void updateThreadsPanel( void ) { nat running = 0, b_read = 0, b_write = 0, b_mvar = 0, b_throwto = 0, b_bh = 0, sleeping = 0, total = 0; StgTSO *t; for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) { switch (t->what_next) { case ThreadKilled: break; case ThreadComplete: break; default: switch (t->why_blocked) { case BlockedOnRead: b_read++; break; case BlockedOnWrite: b_write++; break; case BlockedOnDelay: sleeping++; break; case BlockedOnMVar: b_mvar++; break; case BlockedOnException: b_throwto++; break; case BlockedOnBlackHole: b_bh++; break; case NotBlocked: running++; break; } } } total = running + b_read + b_write + b_mvar + b_throwto + b_bh + sleeping; numLabel(running_label, running); numLabel(b_read_label, b_read); numLabel(b_write_label, b_write); numLabel(b_mvar_label, b_mvar); numLabel(b_bh_label, b_bh); numLabel(b_throwto_label, b_throwto); numLabel(sleeping_label, sleeping); numLabel(total_label, total); } typedef enum { Thunk, Fun, Constr, BlackHole, Array, Thread, Other, N_Cats } ClosureCategory; #define N_SLICES 100 static nat *res_prof[N_SLICES]; static double res_time[N_SLICES]; static nat next_slice = 0; static void residencyCensus( void ) { nat slice = next_slice++, *prof; bdescr *bd; nat g, s, size, type; StgPtr p; StgInfoTable *info; if (slice >= N_SLICES) { barf("too many slices"); } res_prof[slice] = stgMallocBytes(N_Cats * sizeof(nat), "residencyCensus"); prof = res_prof[slice]; memset(prof, 0, N_Cats * sizeof(nat)); res_time[slice] = mut_user_time(); for(g = 0; g < RtsFlags.GcFlags.generations; g++) { for(s = 0; s < generations[g].n_steps; s++) { /* skip over g0s0 if multi-generational */ if (RtsFlags.GcFlags.generations > 1 && g == 0 && s == 0) continue; if (RtsFlags.GcFlags.generations == 1) { /* bd = generations[g].steps[s].to_blocks; FIXME to_blocks does not exist */ } else { bd = generations[g].steps[s].blocks; } for (; bd != NULL; bd = bd->link) { p = bd->start; while (p < bd->free) { info = get_itbl((StgClosure *)p); type = Other; switch (info->type) { case CONSTR: case BCO: if (((StgClosure *)p)->header.info == &stg_DEAD_WEAK_info) { size = sizeofW(StgWeak); type = Other; break; } /* else, fall through... */ case CONSTR_1_0: case CONSTR_0_1: case CONSTR_1_1: case CONSTR_0_2: case CONSTR_2_0: size = sizeW_fromITBL(info); type = Constr; break; case FUN_1_0: case FUN_0_1: size = sizeofW(StgHeader) + 1; goto fun; case FUN_1_1: case FUN_0_2: case FUN_2_0: case FUN: size = sizeW_fromITBL(info); fun: type = Fun; break; case THUNK_1_0: case THUNK_0_1: case THUNK_SELECTOR: size = sizeofW(StgHeader) + 2; goto thunk; case THUNK_1_1: case THUNK_0_2: case THUNK_2_0: case THUNK: size = sizeW_fromITBL(info); thunk: type = Thunk; break; case CAF_BLACKHOLE: case EAGER_BLACKHOLE: case BLACKHOLE: /* case BLACKHOLE_BQ: FIXME: case does not exist */ size = sizeW_fromITBL(info); type = BlackHole; break; case AP: size = pap_sizeW((StgPAP *)p); type = Thunk; break; case PAP: size = pap_sizeW((StgPAP *)p); type = Fun; break; case ARR_WORDS: size = arr_words_sizeW(stgCast(StgArrWords*,p)); type = Array; break; case MUT_ARR_PTRS: case MUT_ARR_PTRS_FROZEN: size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p); type = Array; break; case TSO: size = tso_sizeW((StgTSO *)p); type = Thread; break; case WEAK: case STABLE_NAME: case MVAR: case MUT_VAR: /* case MUT_CONS: FIXME: case does not exist */ case IND_PERM: case IND_OLDGEN_PERM: size = sizeW_fromITBL(info); type = Other; break; default: barf("updateResidencyGraph: strange closure " "%d", info->type ); } prof[type] += size; p += size; } } } } } static void updateResidencyGraph( void ) { nat total, prev_total, i, max_res; double time; double time_scale = 1; nat last_slice = next_slice-1; double res_scale = 1; /* in megabytes, doubles */ nat *prof; nat width, height; GdkPoint points[4]; gdk_draw_rectangle (res_pixmap, res_drawing_area->style->bg_gc[GTK_STATE_NORMAL], TRUE, 0, 0, res_drawing_area->allocation.width, res_drawing_area->allocation.height); if (next_slice == 0) return; time = res_time[last_slice]; while (time > time_scale) { time_scale *= 2; } max_res = 0; for (i = 0; i < next_slice; i++) { prof = res_prof[i]; total = prof[Thunk] + prof[Fun] + prof[Constr] + prof[BlackHole] + prof[Array] + prof[Other]; if (total > max_res) { max_res = total; } } while (max_res > res_scale) { res_scale *= 2; } height = res_drawing_area->allocation.height; width = res_drawing_area->allocation.width; points[0].x = 0; points[0].y = height; points[1].y = height; points[3].x = 0; points[3].y = height; gdk_gc_set_foreground(my_gc, &free_color); prev_total = 0; for (i = 0; i < next_slice; i++) { prof = res_prof[i]; total = prof[Thunk] + prof[Fun] + prof[Constr] + prof[BlackHole] + prof[Array] + prof[Other]; points[1].x = width * res_time[i] / time_scale; points[2].x = points[1].x; points[2].y = height - ((height * total) / res_scale); gdk_draw_polygon(res_pixmap, my_gc, TRUE/*filled*/, points, 4); points[3] = points[2]; points[0] = points[1]; } gtk_ruler_set_range( GTK_RULER(res_vruler), res_scale / ((1024*1024)/sizeof(W_)), 0, 0, res_scale / ((1024*1024)/sizeof(W_)) ); gtk_ruler_set_range( GTK_RULER(res_hruler), 0, time_scale, 0, time_scale ); gtk_widget_draw( res_drawing_area, NULL ); } #endif /* RTS_GTK_FRONTPANEL */