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