[project @ 2001-08-14 13:40:07 by sewardj]
[ghc-hetmet.git] / ghc / rts / FrontPanel.c
1 /* -----------------------------------------------------------------------------
2  * $Id: FrontPanel.c,v 1.5 2001/08/14 13:40:09 sewardj Exp $
3  *
4  * (c) The GHC Team 2000
5  *
6  * RTS GTK Front Panel
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #ifdef RTS_GTK_FRONTPANEL
11
12 /* Alas, not Posix. */
13 /* #include "PosixSource.h" */
14
15 #include "Rts.h"
16 #include "RtsUtils.h"
17 #include "MBlock.h"
18 #include "FrontPanel.h"
19 #include "Storage.h"
20 #include "StoragePriv.h"
21 #include "Stats.h"
22 #include "RtsFlags.h"
23 #include "Schedule.h"
24
25 #include <unistd.h>
26 #include <gdk/gdktypes.h>
27 #include <gtk/gtk.h>
28
29 #include "VisSupport.h"
30 #include "VisWindow.h"
31
32 static GtkWidget *window, *map_drawing_area, *gen_drawing_area;
33 static GtkWidget *res_drawing_area;
34 static GtkWidget *continue_but, *stop_but, *quit_but;
35 static GtkWidget *statusbar;
36 static GtkWidget *live_label, *allocated_label;
37 static GtkWidget *footprint_label, *alloc_rate_label;
38 static GtkWidget *map_ruler, *gen_ruler;
39 static GtkWidget *res_vruler, *res_hruler;
40 static GtkWidget *running_label, *b_read_label, *b_write_label, *total_label;
41 static GtkWidget *b_mvar_label, *b_bh_label, *b_throwto_label, *sleeping_label;
42
43 static guint status_context_id;
44
45 gboolean continue_now = FALSE, stop_now = FALSE, quit = FALSE;
46 UpdateMode update_mode = Continuous;
47
48 static GdkPixmap *map_pixmap = NULL;
49 static GdkPixmap *gen_pixmap = NULL;
50 static GdkPixmap *res_pixmap = NULL;
51
52 #define N_GENS 10
53
54 static GdkColor 
55     bdescr_color = { 0, 0xffff, 0, 0 }, /* red */
56     free_color   = { 0, 0, 0, 0xffff }, /* blue */
57     gen_colors[N_GENS] = {
58         { 0, 0, 0xffff, 0 },
59         { 0, 0, 0xf000, 0 },
60         { 0, 0, 0xe000, 0 },
61         { 0, 0, 0xd000, 0 },
62         { 0, 0, 0xc000, 0 },
63         { 0, 0, 0xb000, 0 },
64         { 0, 0, 0xa000, 0 },
65         { 0, 0, 0x9000, 0 },
66         { 0, 0, 0x8000, 0 },
67         { 0, 0, 0x7000, 0 }
68     };
69
70 GdkGC *my_gc = NULL;
71
72 static void *mem_start = (void *) 0x50000000;
73
74 static void colorBlock( void *addr, GdkColor *color, 
75                         nat block_width, nat block_height, 
76                         nat blocks_per_line );
77
78 static void residencyCensus( void );
79 static void updateResidencyGraph( void );
80 static void updateThreadsPanel( void );
81
82 /* Some code pinched from examples/scribble-simple in the GTK+
83  * distribution.
84  */
85
86 /* Create a new backing pixmap of the appropriate size */
87 static gint 
88 configure_event( GtkWidget *widget, GdkEventConfigure *event STG_UNUSED,
89                  GdkPixmap **pixmap )
90 {
91   if (*pixmap)
92     gdk_pixmap_unref(*pixmap);
93
94   *pixmap = gdk_pixmap_new(widget->window,
95                            widget->allocation.width,
96                            widget->allocation.height,
97                            -1);
98
99   gdk_draw_rectangle (*pixmap,
100                       widget->style->white_gc,
101                       TRUE,
102                       0, 0,
103                       widget->allocation.width,
104                       widget->allocation.height);
105
106   fprintf(stderr, "configure!\n");
107   updateFrontPanel();
108   return TRUE;
109 }
110
111 /* Redraw the screen from the backing pixmap */
112 static gint 
113 expose_event( GtkWidget *widget, GdkEventExpose *event, GdkPixmap **pixmap )
114 {
115   gdk_draw_pixmap(widget->window,
116                   widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
117                   *pixmap,
118                   event->area.x, event->area.y,
119                   event->area.x, event->area.y,
120                   event->area.width, event->area.height);
121
122   return FALSE;
123 }
124
125 void
126 initFrontPanel( void )
127 {
128     GdkColormap *colormap;
129     GtkWidget *gen_hbox;
130
131     gtk_init( &prog_argc, &prog_argv );
132
133     window = create_GHC_Front_Panel();
134     map_drawing_area  = lookup_widget(window, "memmap");
135     gen_drawing_area  = lookup_widget(window, "generations");
136     res_drawing_area  = lookup_widget(window, "res_drawingarea");
137     stop_but          = lookup_widget(window, "stop_but");
138     continue_but      = lookup_widget(window, "continue_but");
139     quit_but          = lookup_widget(window, "quit_but");
140     statusbar         = lookup_widget(window, "statusbar");
141     live_label        = lookup_widget(window, "live_label");
142     footprint_label   = lookup_widget(window, "footprint_label");
143     allocated_label   = lookup_widget(window, "allocated_label");
144     alloc_rate_label  = lookup_widget(window, "alloc_rate_label");
145     gen_hbox          = lookup_widget(window, "gen_hbox");
146     gen_ruler         = lookup_widget(window, "gen_ruler");
147     map_ruler         = lookup_widget(window, "map_ruler");
148     res_vruler        = lookup_widget(window, "res_vruler");
149     res_hruler        = lookup_widget(window, "res_hruler");
150     running_label     = lookup_widget(window, "running_label");
151     b_read_label      = lookup_widget(window, "blockread_label");
152     b_write_label     = lookup_widget(window, "blockwrite_label");
153     b_mvar_label      = lookup_widget(window, "blockmvar_label");
154     b_bh_label        = lookup_widget(window, "blockbh_label");
155     b_throwto_label   = lookup_widget(window, "blockthrowto_label");
156     sleeping_label    = lookup_widget(window, "sleeping_label");
157     total_label       = lookup_widget(window, "total_label");
158     
159     status_context_id = 
160         gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "context" );
161
162     /* hook up some signals for the mem map drawing area */
163     gtk_signal_connect (GTK_OBJECT(map_drawing_area), "expose_event",
164                         (GtkSignalFunc)expose_event, &map_pixmap);
165     gtk_signal_connect (GTK_OBJECT(map_drawing_area), "configure_event",
166                         (GtkSignalFunc)configure_event, &map_pixmap);
167
168     gtk_widget_set_events(map_drawing_area, GDK_EXPOSURE_MASK);
169
170     /* hook up some signals for the gen drawing area */
171     gtk_signal_connect (GTK_OBJECT(gen_drawing_area), "expose_event",
172                         (GtkSignalFunc)expose_event, &gen_pixmap);
173     gtk_signal_connect (GTK_OBJECT(gen_drawing_area), "configure_event",
174                         (GtkSignalFunc)configure_event, &gen_pixmap);
175
176     gtk_widget_set_events(gen_drawing_area, GDK_EXPOSURE_MASK);
177     
178     /* hook up some signals for the res drawing area */
179     gtk_signal_connect (GTK_OBJECT(res_drawing_area), "expose_event",
180                         (GtkSignalFunc)expose_event, &res_pixmap);
181     gtk_signal_connect (GTK_OBJECT(res_drawing_area), "configure_event",
182                         (GtkSignalFunc)configure_event, &res_pixmap);
183
184     gtk_widget_set_events(res_drawing_area, GDK_EXPOSURE_MASK);
185     
186     /* allocate our colors */
187     colormap = gdk_colormap_get_system();
188     gdk_colormap_alloc_color(colormap, &bdescr_color, TRUE, TRUE);
189     gdk_colormap_alloc_color(colormap, &free_color, TRUE, TRUE);
190
191     {
192         gboolean success[N_GENS];
193         gdk_colormap_alloc_colors(colormap, gen_colors, N_GENS, TRUE,
194                                   TRUE, success);
195         if (!success) { barf("can't allocate colors"); }
196     }
197
198     /* set the labels on the generation histogram */
199     {
200         char buf[64];
201         nat g, s;
202         GtkWidget *label;
203
204         for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
205             for(s = 0; s < generations[g].n_steps; s++) {
206                 g_snprintf( buf, 64, "%d.%d", g, s );
207                 label = gtk_label_new( buf );
208                 gtk_box_pack_start( GTK_BOX(gen_hbox), label,
209                                     TRUE, TRUE, 5 );
210                 gtk_widget_show(label);
211             }
212         }
213     }
214
215     gtk_widget_show(window);
216
217     /* wait for the user to press "Continue" before getting going... */
218     gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, 
219                         "Program start");
220     gtk_widget_set_sensitive( stop_but, FALSE );
221     continue_now = FALSE;
222     while (continue_now == FALSE) {
223         gtk_main_iteration();
224     }
225     gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id );
226     gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, 
227                         "Running");
228
229     gtk_widget_set_sensitive( continue_but, FALSE );
230     gtk_widget_set_sensitive( stop_but, TRUE );
231     gtk_widget_set_sensitive( quit_but, FALSE );
232
233     while (gtk_events_pending()) {
234         gtk_main_iteration();
235     }
236 }
237
238 void
239 stopFrontPanel( void )
240 {
241     gtk_widget_set_sensitive( quit_but, TRUE );
242     gtk_widget_set_sensitive( continue_but, FALSE );
243     gtk_widget_set_sensitive( stop_but, FALSE );
244
245     updateFrontPanel();
246
247     gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, 
248                         "Program finished");
249
250     quit = FALSE;
251     while (quit == FALSE) {
252         gtk_main_iteration();
253     }
254 }
255
256 static void
257 waitForContinue( void )
258 {
259     gtk_widget_set_sensitive( continue_but, TRUE );
260     gtk_widget_set_sensitive( stop_but, FALSE );
261     stop_now = FALSE;
262     continue_now = FALSE;
263     while (continue_now == FALSE) {
264         gtk_main_iteration();
265     }
266     gtk_widget_set_sensitive( continue_but, FALSE );
267     gtk_widget_set_sensitive( stop_but, TRUE );
268 }
269
270 void
271 updateFrontPanelBeforeGC( nat N )
272 {
273     char buf[1000];
274
275     updateFrontPanel();
276
277     if (update_mode == BeforeGC 
278         || update_mode == BeforeAfterGC
279         || stop_now == TRUE) {
280         g_snprintf( buf, 1000, "Stopped (before GC, generation %d)", N );
281         gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf );
282         waitForContinue();
283         gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id );
284     }
285
286     g_snprintf( buf, 1000, "Garbage collecting (generation %d)", N );
287     gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf);
288
289     while (gtk_events_pending()) {
290         gtk_main_iteration();
291     }
292 }
293
294 static void
295 numLabel( GtkWidget *lbl, nat n )
296 {
297     char buf[64];
298     g_snprintf(buf, 64, "%d", n);
299     gtk_label_set_text( GTK_LABEL(lbl), buf );
300 }
301
302 void
303 updateFrontPanelAfterGC( nat N, lnat live )
304 {
305     char buf[1000];
306
307     gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id );
308
309     /* is a major GC? */
310     if (N == RtsFlags.GcFlags.generations-1) {
311         residencyCensus();
312     }
313
314     updateFrontPanel();
315
316     if (update_mode == AfterGC 
317         || update_mode == BeforeAfterGC
318         || stop_now == TRUE) {
319         snprintf( buf, 1000, "Stopped (after GC, generation %d)", N );
320         gtk_statusbar_push( GTK_STATUSBAR(statusbar), status_context_id, buf );
321         waitForContinue();
322         gtk_statusbar_pop( GTK_STATUSBAR(statusbar), status_context_id );
323     }
324
325     {
326         double words_to_megs = (1024 * 1024) / sizeof(W_);
327         double time = mut_user_time();
328
329         snprintf( buf, 1000, "%.2f", (double)live / words_to_megs );
330         gtk_label_set_text( GTK_LABEL(live_label), buf );
331
332         snprintf( buf, 1000, "%.2f", (double)total_allocated / words_to_megs );
333         gtk_label_set_text( GTK_LABEL(allocated_label), buf );
334
335         snprintf( buf, 1000, "%.2f",
336                   (double)(mblocks_allocated * MBLOCK_SIZE_W) / words_to_megs );
337         gtk_label_set_text( GTK_LABEL(footprint_label), buf );
338
339         if ( time == 0.0 )
340             snprintf( buf, 1000, "%.2f", time );
341         else
342             snprintf( buf, 1000, "%.2f",
343                       (double)(total_allocated / words_to_megs) / time );
344         gtk_label_set_text( GTK_LABEL(alloc_rate_label), buf );
345     }
346
347     while (gtk_events_pending()) {
348         gtk_main_iteration();
349     }
350 }
351
352 void
353 updateFrontPanel( void )
354 {
355     void *m, *a;
356     bdescr *bd;
357
358     updateThreadsPanel();
359
360     if (my_gc == NULL) {
361         my_gc = gdk_gc_new( window->window );
362     }
363
364     if (map_pixmap != NULL) {
365         nat height, width, blocks_per_line, 
366             block_height, block_width, mblock_height;
367
368         height = map_drawing_area->allocation.height;
369         width  = map_drawing_area->allocation.width;
370
371         mblock_height =  height / mblocks_allocated;
372         blocks_per_line = 16;
373         block_height  = mblock_height / 
374             ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line);
375         while (block_height == 0) {
376             blocks_per_line *= 2;
377             block_height  = mblock_height / 
378                 ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line);
379         }
380         block_width = width / blocks_per_line;
381
382         gdk_draw_rectangle (map_pixmap,
383                             map_drawing_area->style->bg_gc[GTK_STATE_NORMAL],
384                             TRUE,
385                             0, 0,
386                             map_drawing_area->allocation.width,
387                             map_drawing_area->allocation.height);
388         
389         for ( m = mem_start; 
390               (char *)m < (char *)mem_start + 
391                   (mblocks_allocated * MBLOCK_SIZE); 
392               (char *)m += MBLOCK_SIZE ) {
393             
394             /* color the bdescr area first */
395             for (a = m; a < FIRST_BLOCK(m); (char *)a += BLOCK_SIZE) {
396                 colorBlock( a, &bdescr_color, 
397                             block_width, block_height, blocks_per_line );
398             }
399             
400             /* color each block */
401             for (; a <= LAST_BLOCK(m); (char *)a += BLOCK_SIZE) {
402                 bd = Bdescr((P_)a);
403                 ASSERT(bd->start == a);
404                 if (bd->free == (void *)-1) {
405                     colorBlock( a, &free_color, 
406                                 block_width, block_height, blocks_per_line );
407                 } else if (bd->gen != NULL) {
408                     colorBlock( a, &gen_colors[bd->gen->no],
409                                 block_width, block_height, blocks_per_line );
410                 } else if (bd->link != NULL) {
411                     if (bd->link->free == (void *)-1) {
412                         colorBlock( a, &free_color, 
413                                     block_width, block_height, blocks_per_line );
414                         
415                     } else if (bd->link->gen != NULL) {
416                         colorBlock( a, &gen_colors[bd->link->gen->no],
417                                     block_width, block_height, blocks_per_line );
418                     } else {
419                         belch("block at %p: can't indentify", bd->start);
420                     }
421                 }
422             }
423         }
424
425         
426         { 
427             nat height = map_drawing_area->allocation.height,
428                 block_height, mblock_height;
429
430             block_height = (height / mblocks_allocated) / 
431                 ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line);
432             if (block_height < 1) block_height = 1;
433             mblock_height = block_height * 
434                 ((MBLOCK_SIZE/BLOCK_SIZE) / blocks_per_line);
435
436             gtk_ruler_set_range( GTK_RULER(map_ruler), 0, 
437                                  (double)(height * mblocks_allocated) / 
438                                  (double)((mblock_height * mblocks_allocated)),
439                                  0,
440                                  (double)(height * mblocks_allocated) / 
441                                  (double)((mblock_height * mblocks_allocated))
442                 );
443         }
444                                   
445         gtk_widget_draw( map_drawing_area, NULL );
446     }
447
448     if (gen_pixmap != NULL) {
449
450         GdkRectangle rect;
451         nat g, s, columns, column, max_blocks, height_blocks,
452             width, height;
453         
454         gdk_draw_rectangle (gen_pixmap,
455                             gen_drawing_area->style->white_gc,
456                             TRUE,
457                             0, 0,
458                             gen_drawing_area->allocation.width,
459                             gen_drawing_area->allocation.height);
460
461         height = gen_drawing_area->allocation.height;
462         width  = gen_drawing_area->allocation.width;
463
464         columns = 0; max_blocks = 0;
465         for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
466             columns += generations[g].n_steps;
467             for(s = 0; s < generations[g].n_steps; s++) {
468                 if (generations[g].steps[s].n_blocks > max_blocks) {
469                     max_blocks = generations[g].steps[s].n_blocks;
470                 }
471             }
472         }
473
474         /* find a reasonable height value larger than max_blocks */
475         { 
476             nat n = 0;
477             while (max_blocks != 0) {
478                 max_blocks >>= 1; n++;
479             }
480             height_blocks = 1 << n;
481         }
482
483         column = 0;
484         for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
485             for(s = 0; s < generations[g].n_steps; s++, column++) {
486                 gdk_gc_set_foreground(my_gc, &gen_colors[g]);
487
488                 rect.x = column * (width / columns);
489
490                 if (generations[g].steps[s].n_blocks == 0)
491                     rect.y = height;
492                 else
493                     rect.y = height - 
494                         (height * generations[g].steps[s].n_blocks
495                          / height_blocks);
496
497                 rect.width = (width / columns);
498                 rect.height = height - rect.y;
499
500                 gdk_draw_rectangle( gen_pixmap, my_gc, TRUE/*filled*/, 
501                                     rect.x, rect.y, rect.width,
502                                     rect.height );
503             }
504         }
505
506         gtk_ruler_set_range( GTK_RULER(gen_ruler), 
507                              height_blocks * BLOCK_SIZE / (1024 * 1024),
508                              0, 0,
509                              height_blocks * BLOCK_SIZE / (1024 * 1024)
510             );
511
512         gtk_widget_draw( gen_drawing_area, NULL );
513     }
514
515     if (res_pixmap != NULL) {
516         updateResidencyGraph();
517     }
518
519     while (gtk_events_pending()) {
520         gtk_main_iteration_do(FALSE/*don't block*/);
521     }
522 }
523
524 static void
525 colorBlock( void *addr, GdkColor *color, 
526             nat block_width, nat block_height, nat blocks_per_line )
527 {
528     GdkRectangle rect;
529     nat block_no;
530
531     gdk_gc_set_foreground(my_gc, color);
532
533     block_no = ((char *)addr - (char *)mem_start) / BLOCK_SIZE;
534
535     rect.x = (block_no % blocks_per_line) * block_width;
536     rect.y = block_no / blocks_per_line * block_height;
537     rect.width = block_width;
538     rect.height = block_height;
539     gdk_draw_rectangle( map_pixmap, my_gc, TRUE/*filled*/, 
540                         rect.x, rect.y, rect.width, rect.height );
541 }
542
543 static void
544 updateThreadsPanel( void )
545 {
546     nat running = 0,
547         b_read = 0,
548         b_write = 0,
549         b_mvar = 0,
550         b_throwto = 0,
551         b_bh = 0,
552         sleeping = 0,
553         total = 0;
554
555     StgTSO *t;
556
557     for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) {
558         switch (t->what_next) {
559         case ThreadKilled:          break;
560         case ThreadComplete:        break;
561         default:
562             switch (t->why_blocked) {
563             case BlockedOnRead:       b_read++;    break;
564             case BlockedOnWrite:      b_write++;   break;
565             case BlockedOnDelay:      sleeping++;  break;
566             case BlockedOnMVar:       b_mvar++;    break;
567             case BlockedOnException:  b_throwto++; break;
568             case BlockedOnBlackHole:  b_bh++;      break;
569             case NotBlocked:          running++;   break;
570             }
571         }
572     }
573     total = running + b_read + b_write + b_mvar + b_throwto + b_bh + sleeping;
574     numLabel(running_label,   running);
575     numLabel(b_read_label,    b_read);
576     numLabel(b_write_label,   b_write);
577     numLabel(b_mvar_label,    b_mvar);
578     numLabel(b_bh_label,      b_bh);
579     numLabel(b_throwto_label, b_throwto);
580     numLabel(sleeping_label,  sleeping);
581     numLabel(total_label,     total);
582 }
583
584 typedef enum { Thunk, Fun, Constr, BlackHole,
585                Array, Thread, Other, N_Cats } ClosureCategory;
586
587 #define N_SLICES 100
588
589 static nat *res_prof[N_SLICES];
590 static double res_time[N_SLICES];
591 static nat next_slice = 0;
592
593 static void
594 residencyCensus( void )
595 {
596     nat slice = next_slice++, *prof;
597     bdescr *bd;
598     nat g, s, size, type;
599     StgPtr p;
600     StgInfoTable *info;
601
602     if (slice >= N_SLICES) {
603         barf("too many slices");
604     }
605     res_prof[slice] = stgMallocBytes(N_Cats * sizeof(nat), "residencyCensus");
606     prof = res_prof[slice];
607     memset(prof, 0, N_Cats * sizeof(nat));
608
609     res_time[slice] = mut_user_time();
610     
611     for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
612         for(s = 0; s < generations[g].n_steps; s++) {
613
614             /* skip over g0s0 if multi-generational */
615             if (RtsFlags.GcFlags.generations > 1 &&
616                 g == 0 && s == 0) continue;
617
618             if (RtsFlags.GcFlags.generations == 1) {
619                 bd = generations[g].steps[s].to_space;
620             } else {
621                 bd = generations[g].steps[s].blocks;
622             }
623
624             for (; bd != NULL; bd = bd->link) {
625
626                 p = bd->start;
627
628                 while (p < bd->free) {
629                     info = get_itbl((StgClosure *)p);
630                     type = Other;
631                     
632                     switch (info->type) {
633
634                     case CONSTR:
635                     case BCO:
636                         if (((StgClosure *)p)->header.info == &stg_DEAD_WEAK_info) {
637                             size = sizeofW(StgWeak);
638                             type = Other;
639                             break;
640                         }
641                         /* else, fall through... */
642                     case CONSTR_1_0:
643                     case CONSTR_0_1:
644                     case CONSTR_1_1:
645                     case CONSTR_0_2:
646                     case CONSTR_2_0:
647                         size = sizeW_fromITBL(info);
648                         type = Constr;
649                         break;
650                         
651                     case FUN_1_0:
652                     case FUN_0_1:
653                         size = sizeofW(StgHeader) + 1;
654                         goto fun;
655                     case FUN_1_1:
656                     case FUN_0_2:
657                     case FUN_2_0:
658                     case FUN:
659                         size = sizeW_fromITBL(info);
660                     fun:
661                         type = Fun;
662                         break;
663
664                     case THUNK_1_0:
665                     case THUNK_0_1:
666                     case THUNK_SELECTOR:
667                         size = sizeofW(StgHeader) + 2;
668                         goto thunk;
669                     case THUNK_1_1:
670                     case THUNK_0_2:
671                     case THUNK_2_0:
672                     case THUNK:
673                         size = sizeW_fromITBL(info);
674                     thunk:
675                         type = Thunk;
676                         break;
677
678                     case CAF_BLACKHOLE:
679                     case SE_CAF_BLACKHOLE:
680                     case SE_BLACKHOLE:
681                     case BLACKHOLE:
682                     case BLACKHOLE_BQ:
683                         size = sizeW_fromITBL(info);
684                         type = BlackHole;
685                         break;
686
687                     case AP_UPD:
688                         size = pap_sizeW((StgPAP *)p);
689                         type = Thunk;
690                         break;
691
692                     case PAP:
693                         size = pap_sizeW((StgPAP *)p);
694                         type = Fun;
695                         break;
696                         
697                     case ARR_WORDS:
698                         size = arr_words_sizeW(stgCast(StgArrWords*,p));
699                         type = Array;
700                         break;
701                         
702                     case MUT_ARR_PTRS:
703                     case MUT_ARR_PTRS_FROZEN:
704                         size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
705                         type = Array;
706                         break;
707                         
708                     case TSO:
709                         size = tso_sizeW((StgTSO *)p);
710                         type = Thread;
711                         break;
712                         
713                     case WEAK:
714                     case FOREIGN:
715                     case STABLE_NAME:
716                     case MVAR:
717                     case MUT_VAR:
718                     case IND_PERM:
719                     case IND_OLDGEN_PERM:
720                         size = sizeW_fromITBL(info);
721                         type = Other;
722                         break;
723
724                     default:
725                         barf("updateResidencyGraph: strange closure "
726                              "%d", info->type );
727                     }
728
729                     prof[type] += size;
730                     p += size;
731                 }
732             }
733         }
734     }
735
736 }
737             
738 static void
739 updateResidencyGraph( void )
740 {
741     nat total, prev_total, i, max_res;
742     double time;
743     double time_scale = 1;
744     nat last_slice = next_slice-1;
745     double res_scale  = 1; /* in megabytes, doubles */
746     nat *prof;
747     nat width, height;
748     GdkPoint points[4];
749
750     gdk_draw_rectangle (res_pixmap,
751                         res_drawing_area->style->bg_gc[GTK_STATE_NORMAL],
752                         TRUE,
753                         0, 0,
754                         res_drawing_area->allocation.width,
755                         res_drawing_area->allocation.height);
756     
757     if (next_slice == 0) return;
758
759     time = res_time[last_slice];
760     while (time > time_scale) {
761         time_scale *= 2;
762     }
763
764     max_res = 0; 
765     for (i = 0; i < next_slice; i++) {
766         prof = res_prof[i];
767         total = prof[Thunk] + prof[Fun] + prof[Constr] +
768             prof[BlackHole] + prof[Array] + prof[Other];
769         if (total > max_res) {
770             max_res = total;
771         }
772     }
773     while (max_res > res_scale) {
774         res_scale *= 2;
775     }
776
777     height = res_drawing_area->allocation.height;
778     width  = res_drawing_area->allocation.width;
779
780     points[0].x = 0;
781     points[0].y = height;
782     points[1].y = height;
783     points[3].x = 0;
784     points[3].y = height;
785
786     gdk_gc_set_foreground(my_gc, &free_color);
787
788     prev_total = 0;
789     for (i = 0; i < next_slice; i++) {
790         prof = res_prof[i];
791         total = prof[Thunk] + prof[Fun] + prof[Constr] +
792             prof[BlackHole] + prof[Array] + prof[Other];
793         points[1].x = width * res_time[i] / time_scale;
794         points[2].x = points[1].x;
795         points[2].y = height - ((height * total) / res_scale);
796         gdk_draw_polygon(res_pixmap, my_gc, TRUE/*filled*/, points, 4);
797         points[3] = points[2];
798         points[0] = points[1];
799     }
800
801     gtk_ruler_set_range( GTK_RULER(res_vruler), 
802                          res_scale / ((1024*1024)/sizeof(W_)),
803                          0, 0,
804                          res_scale / ((1024*1024)/sizeof(W_)) );
805
806     gtk_ruler_set_range( GTK_RULER(res_hruler), 
807                          0, time_scale, 0, time_scale );
808
809
810     gtk_widget_draw( res_drawing_area, NULL );
811 }
812
813 #endif /* RTS_GTK_FRONTPANEL */