void startTicker(void) { #if defined(USE_TIMER_CREATE) { struct itimerspec it; it.it_value.tv_sec = TimeToSeconds(itimer_interval); it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000; it.it_interval = it.it_value; if (timer_settime(timer, 0, &it, NULL) != 0) { sysErrorBelch("timer_settime"); stg_exit(EXIT_FAILURE); } } #else { struct itimerval it; it.it_value.tv_sec = TimeToSeconds(itimer_interval); it.it_value.tv_usec = TimeToUS(itimer_interval) % 1000000; it.it_interval = it.it_value; if (setitimer(ITIMER_REAL, &it, NULL) != 0) { sysErrorBelch("setitimer"); stg_exit(EXIT_FAILURE); } } #endif }
static void *itimer_thread_func(void *_handle_tick) { TickProc handle_tick = _handle_tick; uint64_t nticks; int timerfd = -1; #if defined(USE_TIMERFD_FOR_ITIMER) && USE_TIMERFD_FOR_ITIMER struct itimerspec it; it.it_value.tv_sec = TimeToSeconds(itimer_interval); it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000; it.it_interval = it.it_value; timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (timerfd == -1) { sysErrorBelch("timerfd_create"); stg_exit(EXIT_FAILURE); } if (!TFD_CLOEXEC) { fcntl(timerfd, F_SETFD, FD_CLOEXEC); } if (timerfd_settime(timerfd, 0, &it, NULL)) { sysErrorBelch("timerfd_settime"); stg_exit(EXIT_FAILURE); } #endif while (!exited) { if (USE_TIMERFD_FOR_ITIMER) { if (read(timerfd, &nticks, sizeof(nticks)) != sizeof(nticks)) { if (errno != EINTR) { sysErrorBelch("Itimer: read(timerfd) failed"); } } } else { if (usleep(TimeToUS(itimer_interval)) != 0 && errno != EINTR) { sysErrorBelch("usleep(TimeToUS(itimer_interval) failed"); } } // first try a cheap test if (stopped) { ACQUIRE_LOCK(&mutex); // should we really stop? if (stopped) { waitCondition(&start_cond, &mutex); } RELEASE_LOCK(&mutex); } else { handle_tick(0); } } if (USE_TIMERFD_FOR_ITIMER) close(timerfd); closeMutex(&mutex); closeCondition(&start_cond); return NULL; }
void stat_startGC (Capability *cap, gc_thread *gct) { nat bell = RtsFlags.GcFlags.ringBell; if (bell) { if (bell > 1) { debugBelch(" GC "); rub_bell = 1; } else { debugBelch("\007"); } } #if USE_PAPI if(papi_is_reporting) { /* Switch to counting GC events */ papi_stop_mutator_count(); papi_start_gc_count(); } #endif getProcessTimes(&gct->gc_start_cpu, &gct->gc_start_elapsed); // Post EVENT_GC_START with the same timestamp as used for stats // (though converted from Time=StgInt64 to EventTimestamp=StgWord64). // Here, as opposed to other places, the event is emitted on the cap // that initiates the GC and external tools expect it to have the same // timestamp as used in +RTS -s calculcations. traceEventGcStartAtT(cap, TimeToNS(gct->gc_start_elapsed - start_init_elapsed)); gct->gc_start_thread_cpu = getThreadCPUTime(); if (RtsFlags.GcFlags.giveStats != NO_GC_STATS) { gct->gc_start_faults = getPageFaults(); } }
void stat_endGC (Capability *cap, gc_thread *gct, W_ live, W_ copied, W_ slop, nat gen, nat par_n_threads, W_ par_max_copied, W_ par_tot_copied) { W_ tot_alloc; W_ alloc; if (RtsFlags.GcFlags.giveStats != NO_GC_STATS || RtsFlags.ProfFlags.doHeapProfile) // heap profiling needs GC_tot_time { Time cpu, elapsed, gc_cpu, gc_elapsed; // Has to be emitted while all caps stopped for GC, but before GC_END. // See trac.haskell.org/ThreadScope/wiki/RTSsummaryEvents // for a detailed design rationale of the current setup // of GC eventlog events. traceEventGcGlobalSync(cap); // Emitted before GC_END on all caps, which simplifies tools code. traceEventGcStats(cap, CAPSET_HEAP_DEFAULT, gen, copied * sizeof(W_), slop * sizeof(W_), /* current loss due to fragmentation */ (mblocks_allocated * BLOCKS_PER_MBLOCK - n_alloc_blocks) * BLOCK_SIZE_W * sizeof(W_), par_n_threads, par_max_copied * sizeof(W_), par_tot_copied * sizeof(W_)); getProcessTimes(&cpu, &elapsed); // Post EVENT_GC_END with the same timestamp as used for stats // (though converted from Time=StgInt64 to EventTimestamp=StgWord64). // Here, as opposed to other places, the event is emitted on the cap // that initiates the GC and external tools expect it to have the same // timestamp as used in +RTS -s calculcations. traceEventGcEndAtT(cap, TimeToNS(elapsed - start_init_elapsed)); gc_elapsed = elapsed - gct->gc_start_elapsed; gc_cpu = cpu - gct->gc_start_cpu; /* For the moment we calculate both per-HEC and total allocation. * There is thus redundancy here, but for the moment we will calculate * it both the old and new way and assert they're the same. * When we're sure it's working OK then we can simplify things. */ tot_alloc = calcTotalAllocated(); // allocated since the last GC alloc = tot_alloc - GC_tot_alloc; GC_tot_alloc = tot_alloc; if (RtsFlags.GcFlags.giveStats == VERBOSE_GC_STATS) { W_ faults = getPageFaults(); statsPrintf("%9" FMT_SizeT " %9" FMT_SizeT " %9" FMT_SizeT, alloc*sizeof(W_), copied*sizeof(W_), live*sizeof(W_)); statsPrintf(" %6.3f %6.3f %8.3f %8.3f %4" FMT_Word " %4" FMT_Word " (Gen: %2d)\n", TimeToSecondsDbl(gc_cpu), TimeToSecondsDbl(gc_elapsed), TimeToSecondsDbl(cpu), TimeToSecondsDbl(elapsed - start_init_elapsed), faults - gct->gc_start_faults, gct->gc_start_faults - GC_end_faults, gen); GC_end_faults = faults; statsFlush(); } GC_coll_cpu[gen] += gc_cpu; GC_coll_elapsed[gen] += gc_elapsed; if (GC_coll_max_pause[gen] < gc_elapsed) { GC_coll_max_pause[gen] = gc_elapsed; } GC_tot_copied += (StgWord64) copied; GC_par_max_copied += (StgWord64) par_max_copied; GC_par_tot_copied += (StgWord64) par_tot_copied; GC_tot_cpu += gc_cpu; traceEventHeapSize(cap, CAPSET_HEAP_DEFAULT, mblocks_allocated * MBLOCK_SIZE_W * sizeof(W_)); if (gen == RtsFlags.GcFlags.generations-1) { /* major GC? */ if (live > max_residency) { max_residency = live; } current_residency = live; residency_samples++; cumulative_residency += live; traceEventHeapLive(cap, CAPSET_HEAP_DEFAULT, live * sizeof(W_)); } if (slop > max_slop) max_slop = slop; } if (rub_bell) { debugBelch("\b\b\b \b\b\b"); rub_bell = 0; } #if USE_PAPI if(papi_is_reporting) { /* Switch to counting mutator events */ if (gen == 0) { papi_stop_gc0_count(); } else { papi_stop_gc1_count(); } papi_start_mutator_count(); } #endif }