1 *********************************************************************
3 Stuff for printing out GC statistics
5 usertime() -- The current user time in seconds
6 elapsedtime() -- The current elapsed time in seconds
13 *********************************************************************
16 #define NON_POSIX_SOURCE /*needed for solaris2 only?*/
18 /* how is this to work given we have not read platform.h yet? */
20 #define _INCLUDE_HPUX_SOURCE
24 #include "SMinternal.h"
27 #ifdef HAVE_SYS_TYPES_H
28 #include <sys/types.h>
35 #ifdef HAVE_SYS_TIMES_H
36 #include <sys/times.h>
39 #ifdef HAVE_SYS_TIME_H
43 #if defined(HAVE_SYS_RESOURCE_H) && ! irix_TARGET_OS
44 #include <sys/resource.h>
47 /* Needed for Solaris2 */
48 #if /* defined(HAVE_SYS_RUSAGE_H) && */ defined(solaris2_TARGET_OS)
49 #include <sys/rusage.h>
52 #ifdef HAVE_SYS_TIMEB_H
53 #include <sys/timeb.h>
57 #include <sys/syscall.h>
58 #define getrusage(a, b) syscall(SYS_GETRUSAGE, a, b)
59 #define HAVE_GETRUSAGE
62 static StgDouble GC_start_time, GC_tot_time = 0; /* User GC Time */
63 static StgDouble GCe_start_time, GCe_tot_time = 0; /* Elapsed GC time */
65 #if defined(GCap) || defined(GCgn)
66 static StgDouble GC_min_time = 0;
67 static StgDouble GCe_min_time = 0;
69 static I_ GC_min_no = 0;
70 static I_ GC_min_since_maj = 0;
71 static I_ GC_live_maj = 0; /* Heap live at last major collection */
72 static I_ GC_alloc_since_maj = 0; /* Heap alloc since collection major */
75 static I_ GC_maj_no = 0;
76 static ullong GC_tot_alloc = 0; /* Total heap allocated -- 64 bits? */
78 static I_ GC_start_faults = 0, GC_end_faults = 0;
81 ullong_format_string(ullong x, char *s, rtsBool with_commas)
84 sprintf(s, "%ld", (I_)x);
85 else if (x < (ullong)1000000)
86 sprintf(s, (with_commas) ? "%ld,%3.3ld" : "%ld%3.3ld",
87 (I_)((x)/(ullong)1000),
88 (I_)((x)%(ullong)1000));
89 else if (x < (ullong)1000000000)
90 sprintf(s, (with_commas) ? "%ld,%3.3ld,%3.3ld" : "%ld%3.3ld%3.3ld",
91 (I_)((x)/(ullong)1000000),
92 (I_)((x)/(ullong)1000%(ullong)1000),
93 (I_)((x)%(ullong)1000));
95 sprintf(s, (with_commas) ? "%ld,%3.3ld,%3.3ld,%3.3ld" : "%ld%3.3ld%3.3ld%3.3ld",
96 (I_)((x)/(ullong)1000000000),
97 (I_)((x)/(ullong)1000000%(ullong)1000),
98 (I_)((x)/(ullong)1000%(ullong)1000),
99 (I_)((x)%(ullong)1000));
103 /* "constants" for "usertime" and "elapsedtime" */
105 static StgDouble ElapsedTimeStart = 0.0; /* setup when beginning things */
106 static StgDouble TicksPerSecond = 0.0; /* ditto */
108 /* usertime() -- The current user time in seconds */
113 #if ! (defined(HAVE_GETRUSAGE) || defined(HAVE_TIMES))
114 /* We will #ifdef around the fprintf for machines
115 we *know* are unsupported. (WDP 94/05)
117 fprintf(stderr, "NOTE: `usertime' does nothing!\n");
120 #else /* not stumped */
122 /* "times" is the more standard, but we prefer "getrusage"
123 (because we are old worn-out BSD hackers)
125 # if defined(HAVE_GETRUSAGE) && ! irix_TARGET_OS
128 getrusage(RUSAGE_SELF, &t);
129 return(t.ru_utime.tv_sec + 1e-6*t.ru_utime.tv_usec);
131 # else /* HAVE_TIMES */
135 return(((StgDouble)(t.tms_utime))/TicksPerSecond);
137 # endif /* HAVE_TIMES */
138 #endif /* not stumped */
142 /* elapsedtime() -- The current elapsed time in seconds */
147 #if ! (defined(HAVE_TIMES) || defined(HAVE_FTIME))
148 /* We will #ifdef around the fprintf for machines
149 we *know* are unsupported. (WDP 94/05)
151 fprintf(stderr, "NOTE: `elapsedtime' does nothing!\n");
154 #else /* not stumped */
156 /* "ftime" may be nicer, but "times" is more standard;
157 but, on a Sun, if you do not get the SysV one, you are *hosed*...
160 # if defined(HAVE_TIMES) && ! sunos4_TARGET_OS
163 return (((StgDouble) times(&t))/TicksPerSecond - ElapsedTimeStart);
165 # else /* HAVE_FTIME */
169 return t.time + 1e-3*t.millitm - ElapsedTimeStart;
171 # endif /* HAVE_FTIME */
172 #endif /* not stumped */
176 start_time(STG_NO_ARGS)
180 /* Determine TicksPerSecond ... */
182 ticks = sysconf(_SC_CLK_TCK);
184 fprintf(stderr, "stat_init: bad call to 'sysconf'!\n");
187 TicksPerSecond = (StgDouble) ticks;
189 #else /* no "sysconf"; had better guess */
191 TicksPerSecond = (StgDouble) (HZ);
193 # else /* had better guess wildly */
194 /* We will #ifdef around the fprintf for machines
195 we *know* are unsupported. (WDP 94/05)
197 fprintf(stderr, "NOTE: Guessing `TicksPerSecond = 60'!\n");
198 TicksPerSecond = 60.0;
202 ElapsedTimeStart = elapsedtime();
205 static StgDouble InitUserTime = 0.0; /* user time taken for initialization */
206 static StgDouble InitElapsedTime = 0.0; /* elapsed time taken for initialization */
208 void end_init(STG_NO_ARGS)
210 InitUserTime = usertime();
211 InitElapsedTime = elapsedtime();
215 pagefaults(STG_NO_ARGS)
217 #if !defined(HAVE_GETRUSAGE) || irix_TARGET_OS
222 getrusage(RUSAGE_SELF, &t);
227 /* Called at the beginning of execution of the program */
228 /* Writes the command line and inits stats header */
231 stat_init(char *collector, char *comment1, char *comment2)
233 FILE *sf = RTSflags.GcFlags.statsFile;
236 char temp[BIG_STRING_LEN];
237 ullong_format_string( (ullong)RTSflags.GcFlags.heapSize*sizeof(W_), temp, rtsTrue/*commas*/);
238 fprintf(sf, "\nCollector: %s HeapSize: %s (bytes)\n\n", collector, temp);
239 if (RTSflags.GcFlags.giveStats) {
240 #if !defined(HAVE_GETRUSAGE) || irix_TARGET_OS
241 fprintf(sf, "NOTE: `pagefaults' does nothing!\n");
244 /*######## ####### ####### ##.# ##.## ##.## ####.## ####.## #### ####*/
245 " Alloc Collect Live Resid GC GC TOT TOT Page Flts %s\n",
248 " bytes bytes bytes ency user elap user elap GC MUT %s\n",
252 #if defined(GCap) || defined(GCgn)
255 /*######## ####### ##.# ####### ##.# ### ##.## ##.## ##.## ##.## ####.## ####.## #### ####*/
256 " Alloc Promote Promo Live Resid Minor Minor Minor Major Major TOT TOT Page Flts\n");
258 " bytes bytes ted bytes ency No user elap user elap user elap MUT Major\n");
260 #endif /* generational */
266 /* Called at the beginning of each GC */
267 static I_ rub_bell = 0;
270 stat_startGC(I_ alloc)
272 FILE *sf = RTSflags.GcFlags.statsFile;
274 #if defined(GCap) || defined(GCgn)
275 I_ bell = alloc == 0 ? RTSflags.GcFlags.ringBell : 0;
276 #else /* ! generational */
277 I_ bell = RTSflags.GcFlags.ringBell;
278 #endif /* ! generational */
282 fprintf(stderr, " GC ");
285 fprintf(stderr, "\007");
290 GC_start_time = usertime();
291 GCe_start_time = elapsedtime();
293 #if defined(GCap) || defined(GCgn)
294 if (RTSflags.GcFlags.giveStats || alloc == 0) {
295 GC_start_faults = pagefaults();
297 #else /* ! generational */
298 if (RTSflags.GcFlags.giveStats) {
299 GC_start_faults = pagefaults();
301 #endif /* ! generational */
306 /* Called at the end of each GC */
309 stat_endGC(I_ alloc, I_ collect, I_ live, char *comment)
311 FILE *sf = RTSflags.GcFlags.statsFile;
314 StgDouble time = usertime();
315 StgDouble etime = elapsedtime();
317 if (RTSflags.GcFlags.giveStats) {
318 I_ faults = pagefaults();
320 fprintf(sf, "%8ld %7ld %7ld %5.1f%%",
321 alloc*sizeof(W_), collect*sizeof(W_), live*sizeof(W_), collect == 0 ? 0.0 : (live / (StgDouble) collect * 100));
322 fprintf(sf, " %5.2f %5.2f %7.2f %7.2f %4ld %4ld %s\n",
323 (time-GC_start_time),
324 (etime-GCe_start_time),
327 faults - GC_start_faults,
328 GC_start_faults - GC_end_faults,
331 GC_end_faults = faults;
335 #if defined(GCap) || defined(GCgn)
336 else if(alloc == 0 && collect != 0) {
337 I_ faults = pagefaults();
339 fprintf(sf, "%8ld %7ld %5.1f%% %7ld %5.1f%%",
340 GC_alloc_since_maj*sizeof(W_), (collect - GC_live_maj)*sizeof(W_),
341 (collect - GC_live_maj) / (StgDouble) GC_alloc_since_maj * 100,
342 live*sizeof(W_), live / (StgDouble) RTSflags.GcFlags.heapSize * 100);
343 fprintf(sf, " %3ld %5.2f %5.2f %5.2f %5.2f %7.2f %7.2f %4ld %4ld\n",
344 GC_min_since_maj, GC_min_time, GCe_min_time,
345 (time-GC_start_time),
346 (etime-GCe_start_time),
349 faults - GC_start_faults,
350 GC_start_faults - GC_end_faults
353 GC_end_faults = faults;
356 #endif /* generational */
358 #if defined(GCap) || defined(GCgn)
359 if (alloc == 0 && collect != 0) {
362 GC_min_no += GC_min_since_maj;
363 GC_min_since_maj = 0;
364 GC_tot_alloc += (ullong) GC_alloc_since_maj;
365 GC_alloc_since_maj = 0;
366 GC_tot_time += time-GC_start_time + GC_min_time;
368 GCe_tot_time += etime-GCe_start_time + GCe_min_time;
372 GC_alloc_since_maj += alloc;
373 GC_min_time += time-GC_start_time;
374 GCe_min_time += etime-GCe_start_time;
376 #else /* ! generational */
378 GC_tot_alloc += (ullong) alloc;
379 GC_tot_time += time-GC_start_time;
380 GCe_tot_time += etime-GCe_start_time;
381 #endif /* ! generational */
386 fprintf(stderr, "\b\b\b \b\b\b");
391 /* Called at the end of execution -- to print a summary of statistics */
396 FILE *sf = RTSflags.GcFlags.statsFile;
399 char temp[BIG_STRING_LEN];
400 StgDouble time = usertime();
401 StgDouble etime = elapsedtime();
403 if (RTSflags.GcFlags.giveStats) {
404 fprintf(sf, "%8ld\n\n", alloc*sizeof(W_));
407 #if defined(GCap) || defined (GCgn)
409 fprintf(sf, "%8ld %7.7s %6.6s %7.7s %6.6s",
410 (GC_alloc_since_maj + alloc)*sizeof(W_), "", "", "", "");
411 fprintf(sf, " %3ld %5.2f %5.2f\n\n",
412 GC_min_since_maj, GC_min_time, GCe_min_time);
414 GC_min_no += GC_min_since_maj;
415 GC_tot_time += GC_min_time;
416 GCe_tot_time += GCe_min_time;
417 GC_tot_alloc += (ullong) (GC_alloc_since_maj + alloc);
418 ullong_format_string(GC_tot_alloc*sizeof(W_), temp, rtsTrue/*commas*/);
419 fprintf(sf, "%11s bytes allocated in the heap\n", temp);
420 if ( ResidencySamples > 0 ) {
421 ullong_format_string(MaxResidency*sizeof(W_), temp, rtsTrue/*commas*/);
422 fprintf(sf, "%11s bytes maximum residency (%.1f%%, %ld sample(s))\n",
424 MaxResidency / (StgDouble) RTSflags.GcFlags.heapSize * 100,
427 fprintf(sf, "%11ld garbage collections performed (%ld major, %ld minor)\n\n",
428 GC_maj_no + GC_min_no, GC_maj_no, GC_min_no);
430 #else /* ! generational */
432 GC_tot_alloc += (ullong) alloc;
433 ullong_format_string(GC_tot_alloc*sizeof(W_), temp, rtsTrue/*commas*/);
434 fprintf(sf, "%11s bytes allocated in the heap\n", temp);
435 if ( ResidencySamples > 0 ) {
436 ullong_format_string(MaxResidency*sizeof(W_), temp, rtsTrue/*commas*/);
437 fprintf(sf, "%11s bytes maximum residency (%.1f%%, %ld sample(s))\n",
439 MaxResidency / (StgDouble) RTSflags.GcFlags.heapSize * 100,
442 fprintf(sf, "%11ld garbage collections performed\n\n", GC_maj_no);
444 #endif /* ! generational */
446 fprintf(sf, " INIT time %6.2fs (%6.2fs elapsed)\n",
447 InitUserTime, InitElapsedTime);
448 fprintf(sf, " MUT time %6.2fs (%6.2fs elapsed)\n",
449 time - GC_tot_time - InitUserTime,
450 etime - GCe_tot_time - InitElapsedTime);
451 fprintf(sf, " GC time %6.2fs (%6.2fs elapsed)\n",
452 GC_tot_time, GCe_tot_time);
453 fprintf(sf, " Total time %6.2fs (%6.2fs elapsed)\n\n",
456 fprintf(sf, " %%GC time %5.1f%% (%.1f%% elapsed)\n\n",
457 GC_tot_time*100./time, GCe_tot_time*100./etime);
459 ullong_format_string((ullong)(GC_tot_alloc*sizeof(W_)/(time - GC_tot_time)), temp, rtsTrue/*commas*/);
460 fprintf(sf, " Alloc rate %s bytes per MUT second\n\n", temp);
462 fprintf(sf, " Productivity %5.1f%% of total user, %.1f%% of total elapsed\n\n",
463 (time - GC_tot_time - InitUserTime) * 100. / time,
464 (time - GC_tot_time - InitUserTime) * 100. / etime);