1 /* -----------------------------------------------------------------------------
2 * $Id: Stats.c,v 1.15 1999/11/02 15:06:03 simonmar Exp $
4 * (c) The GHC Team, 1998-1999
6 * Statistics and timing-related functions.
8 * ---------------------------------------------------------------------------*/
10 #define NON_POSIX_SOURCE
15 #include "StoragePriv.h"
25 # ifdef HAVE_SYS_TIMES_H
26 # include <sys/times.h>
30 #ifdef HAVE_SYS_TIME_H
40 #if ! irix_TARGET_OS && ! defined(__MINGW32__)
41 # if defined(HAVE_SYS_RESOURCE_H)
42 # include <sys/resource.h>
46 #ifdef HAVE_SYS_TIMEB_H
47 #include <sys/timeb.h>
59 #define BIG_STRING_LEN 512
61 static double ElapsedTimeStart = 0.0;
62 static double TicksPerSecond = 0.0;
64 static double InitUserTime = 0.0;
65 static double InitElapsedTime = 0.0;
67 static ullong GC_tot_alloc = 0;
68 static ullong GC_tot_copied = 0;
70 static double GC_start_time, GC_tot_time = 0; /* User GC Time */
71 static double GCe_start_time, GCe_tot_time = 0; /* Elapsed GC time */
73 lnat MaxResidency = 0; /* in words; for stats only */
74 lnat ResidencySamples = 0; /* for stats only */
76 static lnat GC_start_faults = 0, GC_end_faults = 0;
78 static double *GC_coll_times;
80 /* ToDo: convert this to use integers? --SDM */
82 /* elapsedtime() -- The current elapsed time in seconds */
85 #define NS_PER_SEC 10000000LL
86 /* Convert FILETIMEs into secs since the Epoch (Jan1-1970) */
87 #define FT2longlong(ll,ft) \
88 (ll)=(ft).dwHighDateTime; \
90 (ll) |= (ft).dwLowDateTime; \
91 (ll) /= (unsigned long long) (NS_PER_SEC / CLOCKS_PER_SEC)
95 /* cygwin32 or mingw32 version */
99 FILETIME creationTime, exitTime, kernelTime, userTime;
100 long long int kT, uT;
103 /* ToDo: pin down elapsed times to just the OS thread(s) that
104 are evaluating/managing Haskell code.
106 if (!GetProcessTimes (GetCurrentProcess(), &creationTime,
107 &exitTime, &kernelTime, &userTime)) {
108 /* Probably on a Win95 box..*/
112 FT2longlong(kT,kernelTime);
113 FT2longlong(uT,userTime);
114 return (((StgDouble)(uT + kT))/TicksPerSecond - ElapsedTimeStart);
122 # if ! (defined(HAVE_TIMES) || defined(HAVE_FTIME))
123 /* We will #ifdef around the fprintf for machines
124 we *know* are unsupported. (WDP 94/05)
126 fprintf(stderr, "NOTE: `elapsedtime' does nothing!\n");
129 # else /* not stumped */
131 /* "ftime" may be nicer, but "times" is more standard;
132 but, on a Sun, if you do not get the SysV one, you are *hosed*...
135 # if defined(HAVE_TIMES) && ! sunos4_TARGET_OS
137 clock_t r = times(&t);
139 return (((double)r)/TicksPerSecond - ElapsedTimeStart);
141 # else /* HAVE_FTIME */
145 return (fabs(t.time + 1e-3*t.millitm - ElapsedTimeStart));
147 # endif /* HAVE_FTIME */
148 # endif /* not stumped */
152 /* mut_user_time_during_GC() and mut_user_time()
154 * This function can be used to get the current mutator time *during*
155 * a GC, i.e. between stat_startGC and stat_endGC. This is used in
156 * the heap profiler for accurately time stamping the heap sample.
159 mut_user_time_during_GC(void)
161 return (GC_start_time - GC_tot_time);
167 return (usertime() - GC_tot_time);
174 /* ToDo (on NT): better, get this via the performance data
175 that's stored in the registry. */
176 # if !defined(HAVE_GETRUSAGE) || irix_TARGET_OS || defined(_WIN32)
181 getrusage(RUSAGE_SELF, &t);
186 /* ToDo: use gettimeofday on systems that support it (u-sec accuracy) */
193 /* Determine TicksPerSecond ... */
195 ticks = sysconf(_SC_CLK_TCK);
197 fprintf(stderr, "stat_init: bad call to 'sysconf'!\n");
198 stg_exit(EXIT_FAILURE);
200 TicksPerSecond = (double) ticks;
202 /* no "sysconf"; had better guess */
204 TicksPerSecond = (StgDouble) (HZ);
206 #elif defined(CLOCKS_PER_SEC)
207 TicksPerSecond = (StgDouble) (CLOCKS_PER_SEC);
208 #else /* had better guess wildly */
209 /* We will #ifdef around the fprintf for machines
210 we *know* are unsupported. (WDP 94/05)
212 fprintf(stderr, "NOTE: Guessing `TicksPerSecond = 60'!\n");
213 TicksPerSecond = 60.0;
216 ElapsedTimeStart = elapsedtime();
224 FILE *sf = RtsFlags.GcFlags.statsFile;
226 if (RtsFlags.GcFlags.giveStats >= VERBOSE_GC_STATS) {
227 fprintf(sf, " Alloc Collect Live GC GC TOT TOT Page Flts\n");
228 fprintf(sf, " bytes bytes bytes user elap user elap\n");
231 (double *)stgMallocBytes(sizeof(double) * RtsFlags.GcFlags.generations,
233 for (i = 0; i < RtsFlags.GcFlags.generations; i++) {
234 GC_coll_times[i] = 0.0;
242 FILETIME creationTime, exitTime, kernelTime, userTime;
245 /* Convert FILETIMEs into long longs */
247 if (!GetProcessTimes (GetCurrentProcess(), &creationTime,
248 &exitTime, &kernelTime, &userTime)) {
249 /* Probably exec'ing this on a Win95 box..*/
253 FT2longlong(uT,userTime);
254 return (((StgDouble)uT)/TicksPerSecond);
261 # if ! (defined(HAVE_GETRUSAGE) || defined(HAVE_TIMES))
262 /* We will #ifdef around the fprintf for machines
263 we *know* are unsupported. (WDP 94/05)
265 fprintf(stderr, "NOTE: `usertime' does nothing!\n");
268 # else /* not stumped */
270 # if defined(HAVE_TIMES)
274 return(((double)(t.tms_utime))/TicksPerSecond);
276 # else /* HAVE_GETRUSAGE */
279 getrusage(RUSAGE_SELF, &t);
280 return(t.ru_utime.tv_sec + 1e-6*t.ru_utime.tv_usec);
282 # endif /* HAVE_GETRUSAGE */
283 # endif /* not stumped */
285 #endif /* ! _WIN32 */
290 InitUserTime = usertime();
291 InitElapsedTime = elapsedtime();
292 if (InitElapsedTime < 0.0) {
293 InitElapsedTime = 0.0;
297 /* -----------------------------------------------------------------------------
298 Called at the beginning of each GC
299 -------------------------------------------------------------------------- */
301 static nat rub_bell = 0;
306 FILE *sf = RtsFlags.GcFlags.statsFile;
308 nat bell = RtsFlags.GcFlags.ringBell;
312 fprintf(stderr, " GC ");
315 fprintf(stderr, "\007");
320 GC_start_time = usertime();
321 GCe_start_time = elapsedtime();
322 if (RtsFlags.GcFlags.giveStats) {
323 GC_start_faults = pagefaults();
328 /* -----------------------------------------------------------------------------
329 Called at the end of each GC
330 -------------------------------------------------------------------------- */
333 stat_endGC(lnat alloc, lnat collect, lnat live, lnat copied, lnat gen)
335 FILE *sf = RtsFlags.GcFlags.statsFile;
338 double time = usertime();
339 double etime = elapsedtime();
340 double gc_time = time-GC_start_time;
341 double gc_etime = etime-GCe_start_time;
343 if (RtsFlags.GcFlags.giveStats >= VERBOSE_GC_STATS) {
344 nat faults = pagefaults();
346 fprintf(sf, "%9ld %9ld %9ld",
347 alloc*sizeof(W_), collect*sizeof(W_), live*sizeof(W_));
348 fprintf(sf, " %5.2f %5.2f %7.2f %7.2f %4ld %4ld (Gen: %2ld)\n",
353 faults - GC_start_faults,
354 GC_start_faults - GC_end_faults,
357 GC_end_faults = faults;
361 GC_coll_times[gen] += time-GC_start_time;
363 GC_tot_copied += (ullong) copied;
364 GC_tot_alloc += (ullong) alloc;
365 GC_tot_time += time-GC_start_time;
366 GCe_tot_time += etime-GCe_start_time;
371 pthread_t me = pthread_self();
373 for (i = 0; i < RtsFlags.ConcFlags.nNodes; i++) {
374 if (me == task_ids[i].id) {
375 task_ids[i].gc_time += gc_time;
376 task_ids[i].gc_etime += gc_etime;
383 if (gen == RtsFlags.GcFlags.generations-1) { /* major GC? */
384 if (live > MaxResidency) {
392 fprintf(stderr, "\b\b\b \b\b\b");
397 /* -----------------------------------------------------------------------------
398 Called at the end of execution
400 NOTE: number of allocations is not entirely accurate: it doesn't
401 take into account the few bytes at the end of the heap that
402 were left unused when the heap-check failed.
403 -------------------------------------------------------------------------- */
408 FILE *sf = RtsFlags.GcFlags.statsFile;
411 char temp[BIG_STRING_LEN];
412 double time = usertime();
413 double etime = elapsedtime();
414 double MutTime, MutElapsedTime;
416 /* avoid divide by zero if time is measured as 0.00 seconds -- SDM */
417 if (time == 0.0) time = 0.0001;
418 if (etime == 0.0) etime = 0.0001;
420 if (RtsFlags.GcFlags.giveStats >= VERBOSE_GC_STATS) {
421 fprintf(sf, "%9ld %9.9s %9.9s", (lnat)alloc*sizeof(W_), "", "");
422 fprintf(sf, " %5.2f %5.2f\n\n", 0.0, 0.0);
425 GC_tot_alloc += alloc;
427 ullong_format_string(GC_tot_alloc*sizeof(W_), temp, rtsTrue/*commas*/);
428 fprintf(sf, "%11s bytes allocated in the heap\n", temp);
430 ullong_format_string(GC_tot_copied*sizeof(W_), temp, rtsTrue/*commas*/);
431 fprintf(sf, "%11s bytes copied during GC\n", temp);
433 if ( ResidencySamples > 0 ) {
434 ullong_format_string(MaxResidency*sizeof(W_), temp, rtsTrue/*commas*/);
435 fprintf(sf, "%11s bytes maximum residency (%ld sample(s))\n",
441 { /* Count garbage collections */
443 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
444 fprintf(sf, "%11d collections in generation %d (%6.2fs)\n",
445 generations[g].collections, g, GC_coll_times[g]);
448 fprintf(sf,"\n%11ld Mb total memory in use\n\n",
449 mblocks_allocated * MBLOCK_SIZE / (1024 * 1024));
451 MutElapsedTime = etime - GCe_tot_time - InitElapsedTime;
452 if (MutElapsedTime < 0) { MutElapsedTime = 0; } /* sometimes -0.00 */
455 MutTime = time - GC_tot_time - InitUserTime;
456 if (MutTime < 0) { MutTime = 0; }
459 /* For SMP, we have to get the user time from each thread
460 * and try to work out the total time.
465 for (i = 0; i < RtsFlags.ConcFlags.nNodes; i++) {
466 fprintf(sf, " Task %2d: MUT time: %6.2fs, GC time: %6.2fs\n",
467 i, task_ids[i].mut_time, task_ids[i].gc_time);
468 MutTime += task_ids[i].mut_time;
471 time = MutTime + GC_tot_time + InitUserTime;
475 fprintf(sf, " INIT time %6.2fs (%6.2fs elapsed)\n",
476 InitUserTime, InitElapsedTime);
477 fprintf(sf, " MUT time %6.2fs (%6.2fs elapsed)\n",
478 MutTime, MutElapsedTime);
479 fprintf(sf, " GC time %6.2fs (%6.2fs elapsed)\n",
480 GC_tot_time, GCe_tot_time);
481 fprintf(sf, " Total time %6.2fs (%6.2fs elapsed)\n\n",
484 fprintf(sf, " %%GC time %5.1f%% (%.1f%% elapsed)\n\n",
485 GC_tot_time*100./time, GCe_tot_time*100./etime);
487 if (time - GC_tot_time == 0.0)
488 ullong_format_string(0, temp, rtsTrue/*commas*/);
490 ullong_format_string((ullong)(GC_tot_alloc*sizeof(W_)/
491 (time - GC_tot_time)),
492 temp, rtsTrue/*commas*/);
494 fprintf(sf, " Alloc rate %s bytes per MUT second\n\n", temp);
496 fprintf(sf, " Productivity %5.1f%% of total user, %.1f%% of total elapsed\n\n",
497 (time - GC_tot_time - InitUserTime) * 100. / time,
498 (time - GC_tot_time - InitUserTime) * 100. / etime);
504 /* -----------------------------------------------------------------------------
507 Produce some detailed info on the state of the generational GC.
508 -------------------------------------------------------------------------- */
510 stat_describe_gens(void)
512 nat g, s, mut, mut_once, lge, live;
517 fprintf(stderr, " Gen Steps Max Mutable Mut-Once Step Blocks Live Large\n Blocks Closures Closures Objects\n");
519 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
520 for (m = generations[g].mut_list, mut = 0; m != END_MUT_LIST;
523 for (m = generations[g].mut_once_list, mut_once = 0; m != END_MUT_LIST;
526 fprintf(stderr, "%8d %8d %8d %9d %9d", g, generations[g].n_steps,
527 generations[g].max_blocks, mut, mut_once);
529 for (s = 0; s < generations[g].n_steps; s++) {
530 step = &generations[g].steps[s];
531 for (bd = step->large_objects, lge = 0; bd; bd = bd->link)
534 if (RtsFlags.GcFlags.generations == 1) {
539 for (; bd; bd = bd->link) {
540 live += (bd->free - bd->start) * sizeof(W_);
543 fprintf(stderr,"%46s","");
545 fprintf(stderr,"%6d %8d %8d %8d\n", s, step->n_blocks,
549 fprintf(stderr,"\n");