static void METHOD_FN(start) { if (! hpcrun_td_avail()){ return; // in the unlikely event that we are trying to start, but thread data is unavailable, // assume that all sample source ops are suspended. } TMSG(_TST_CTL,"starting _tst w value = (%d,%d), interval = (%d,%d)", itimer.it_interval.tv_sec, itimer.it_interval.tv_usec, itimer.it_value.tv_sec, itimer.it_value.tv_usec); if (setitimer(HPCRUN_PROFILE_TIMER, &itimer, NULL) != 0) { EMSG("setitimer failed (%d): %s", errno, strerror(errno)); hpcrun_ssfail_start("_tst"); } #ifdef USE_ELAPSED_TIME_FOR_WALLCLOCK int ret = time_getTimeCPU(&TD_GET(last_time_us)); if (ret != 0) { EMSG("time_getTimeCPU (clock_gettime) failed!"); abort(); } #endif TD_GET(ss_state)[self->sel_idx] = START; }
// // Returns: address of freeable region at the high end, // else NULL on failure. // void * hpcrun_malloc_freeable(size_t size) { return hpcrun_malloc(size); // For now, don't bother with freeable memory. #if 0 hpcrun_meminfo_t *mi = &TD_GET(memstore); void *addr, *ans; size = round_up(size); addr = mi->mi_low + size; // Recoverable out of memory. if (addr >= mi->mi_high) { TD_GET(mem_low) = 1; TMSG(MALLOC, "%s: size = %ld, failure: temporary out of memory", __func__, size); num_failures++; return NULL; } // Low on memory. if (addr + low_memsize > mi->mi_high) { TD_GET(mem_low) = 1; TMSG(MALLOC, "%s: low on memory, setting epoch flush flag", __func__); } ans = mi->mi_low; mi->mi_low = addr; total_freeable += size; TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr); return ans; #endif }
// Reclaim the freeable CCT memory at the low end. void hpcrun_reclaim_freeable_mem(void) { hpcrun_meminfo_t *mi = &TD_GET(memstore); mi->mi_low = mi->mi_start; TD_GET(mem_low) = 0; num_reclaims++; TMSG(MALLOC, "%s: %d", __func__, num_reclaims); }
void hpcrun_unw_throw(void) { if (DEBUG_NO_LONGJMP) return; if (hpcrun_below_pmsg_threshold()) { hpcrun_bt_dump(TD_GET(btbuf_cur), "PARTIAL"); } hpcrun_up_pmsg_count(); sigjmp_buf_t *it = &(TD_GET(bad_unwind)); (*hpcrun_get_real_siglongjmp())(it->jb, 9); }
void hpcrun_pre_dlopen(const char *path, int flags) { hpcrun_dlopen_write_lock(); atomic_add_i64(&num_dlopen_pending, 1L); TD_GET(inside_dlfcn) = true; }
static void METHOD_FN(start) { TMSG(NONE_CTL,"starting NONE"); TD_GET(ss_state)[self->sel_idx] = START; }
static void help_simulate_segv(bool no_backtrace) { if (DEBUG_NO_LONGJMP) return; if (no_backtrace) { return; } if (hpcrun_below_pmsg_threshold()) { hpcrun_bt_dump(TD_GET(btbuf_cur), "DROP"); } hpcrun_up_pmsg_count(); sigjmp_buf_t *it = &(TD_GET(bad_unwind)); (*hpcrun_get_real_siglongjmp())(it->jb, 9); }
// Factor out the body of the start method so we can restart itimer // without messages in the case that we are interrupting our own code. // safe = 1 if not inside our code, so ok to print debug messages // static void hpcrun_restart_timer(sample_source_t *self, int safe) { int ret; // if thread data is unavailable, assume that all sample source ops // are suspended. if (! hpcrun_td_avail()) { if (safe) { TMSG(ITIMER_CTL, "Thread data unavailable ==> sampling suspended"); } return; } thread_data_t *td = hpcrun_get_thread_data(); if (safe) { TMSG(ITIMER_HANDLER, "starting %s: value = (%d,%d), interval = (%d,%d)", the_event_name, itval_start.it_value.tv_sec, itval_start.it_value.tv_usec, itval_start.it_interval.tv_sec, itval_start.it_interval.tv_usec); } ret = hpcrun_start_timer(td); if (ret != 0) { if (safe) { TMSG(ITIMER_CTL, "setitimer failed to start!!"); EMSG("setitimer failed (%d): %s", errno, strerror(errno)); } hpcrun_ssfail_start("itimer"); } #ifdef USE_ELAPSED_TIME_FOR_WALLCLOCK ret = time_getTimeReal(&TD_GET(last_time_us)); if (ret != 0) { if (safe) { EMSG("time_getTimeReal (clock_gettime) failed!"); } monitor_real_abort(); } #endif TD_GET(ss_state)[self->sel_idx] = START; }
static void METHOD_FN(stop) { int rc; rc = setitimer(HPCRUN_PROFILE_TIMER, &zerotimer, NULL); TMSG(ITIMER_CTL,"stopping _tst"); TD_GET(ss_state)[self->sel_idx] = STOP; }
void hpcrun_pmsg(const char *tag, const char *fmt, ...) { #ifdef SINGLE_THREAD_LOGGING if ( getenv("OT") && (TD_GET(core_profile_trace_data.id) != THE_THREAD)) { return; } #endif // SINGLE_THREAD_LOGGING va_list_box box; va_list_box_start(box, fmt); hpcrun_write_msg_to_log(false, true, tag, fmt, &box); }
// We can't downgrade the lock during fnbounds_unmap_closed_dsos() // because it acquires the fnbounds lock before the dl-iterate lock, // and that is a LOR with sampling. // void hpcrun_post_dlclose(void *handle, int ret) { int outermost = (dlopen_num_writers == 1); TMSG(LOADMAP, "dlclose: handle = %p", handle); fnbounds_unmap_closed_dsos(); if (outermost) { TD_GET(inside_dlfcn) = false; } hpcrun_dlopen_write_unlock(); }
// It's safe to downgrade the lock during fnbounds_map_open_dsos() // because it acquires the dl-iterate lock before the fnbounds lock, // and that order is consistent with sampling. Note: can only // downgrade the lock on the last (outermost) dlopen. // void hpcrun_dlopen(const char *module_name, int flags, void *handle) { int outermost = (dlopen_num_writers == 1); TMSG(LOADMAP, "dlopen: handle = %p, name = %s", handle, module_name); if (outermost) { hpcrun_dlopen_downgrade_lock(); } fnbounds_map_open_dsos(); atomic_add_i64(&num_dlopen_pending, -1L); if (outermost) { TD_GET(inside_dlfcn) = false; hpcrun_dlopen_read_unlock(); } else { hpcrun_dlopen_write_unlock(); } }
static void METHOD_FN(stop) { TMSG(ITIMER_CTL, "stop %s", the_event_name); // itimer is process-wide, so it's worth blocking the signal in the // current thread at stop time. if (use_itimer) { monitor_real_pthread_sigmask(SIG_BLOCK, &timer_mask, NULL); } thread_data_t *td = hpcrun_get_thread_data(); int rc = hpcrun_stop_timer(td); if (rc != 0) { EMSG("stop %s failed, errno: %d", the_event_name, errno); } TD_GET(ss_state)[self->sel_idx] = STOP; }
static int _tst_signal_handler(int sig, siginfo_t* siginfo, void* context) { // If the interrupt came from inside our code, then drop the sample // and return and avoid any MSG. void* pc = hpcrun_context_pc(context); if (! hpcrun_safe_enter_async(pc)) { hpcrun_stats_num_samples_blocked_async_inc(); } else { TMSG(_TST_HANDLER,"_Tst sample event"); uint64_t metric_incr = 1; // default: one time unit #ifdef USE_ELAPSED_TIME_FOR_WALLCLOCK uint64_t cur_time_us; int ret = time_getTimeCPU(&cur_time_us); if (ret != 0) { EMSG("time_getTimeCPU (clock_gettime) failed!"); abort(); } metric_incr = cur_time_us - TD_GET(last_time_us); #endif int metric_id = hpcrun_event2metric(&__tst_obj, _TST_EVENT); hpcrun_sample_callpath_w_bt(context, metric_id, metric_incr, NULL, NULL, 0); } if (hpcrun_is_sampling_disabled()) { TMSG(SPECIAL, "No _tst restart, due to disabled sampling"); return 0; } #ifdef RESET_ITIMER_EACH_SAMPLE METHOD_CALL(&__tst_obj, start); #endif return 0; /* tell monitor that the signal has been handled */ }
static void METHOD_FN(stop) { TMSG(NONE_CTL,"stopping NONE"); TD_GET(ss_state)[self->sel_idx] = STOP; }
static int itimer_signal_handler(int sig, siginfo_t* siginfo, void* context) { static bool metrics_finalized = false; sample_source_t *self = &_itimer_obj; // if sampling is suppressed for this thread, restart timer, & exit if (hpcrun_thread_suppress_sample) { TMSG(ITIMER_HANDLER, "thread sampling suppressed"); hpcrun_restart_timer(self, 1); return 0; } // If we got a wallclock signal not meant for our thread, then drop the sample if (! wallclock_ok) { EMSG("Received itimer signal, but thread not initialized"); } // If the interrupt came from inside our code, then drop the sample // and return and avoid any MSG. void* pc = hpcrun_context_pc(context); if (! hpcrun_safe_enter_async(pc)) { hpcrun_stats_num_samples_blocked_async_inc(); if (! hpcrun_is_sampling_disabled()) { hpcrun_restart_timer(self, 0); } // tell monitor the signal has been handled. return 0; } // Ensure metrics are finalized. if (!metrics_finalized) { hpcrun_finalize_metrics(); metrics_finalized = true; } TMSG(ITIMER_HANDLER,"Itimer sample event"); uint64_t metric_incr = 1; // default: one time unit #if defined (USE_ELAPSED_TIME_FOR_WALLCLOCK) uint64_t cur_time_us = 0; int ret = time_getTimeReal(&cur_time_us); if (ret != 0) { EMSG("time_getTimeReal (clock_gettime) failed!"); monitor_real_abort(); } metric_incr = cur_time_us - TD_GET(last_time_us); #endif int metric_id = hpcrun_event2metric(self, ITIMER_EVENT); sample_val_t sv = hpcrun_sample_callpath(context, metric_id, metric_incr, 0/*skipInner*/, 0/*isSync*/); blame_shift_apply(metric_id, sv.sample_node, metric_incr); if (hpcrun_is_sampling_disabled()) { TMSG(ITIMER_HANDLER, "No itimer restart, due to disabled sampling"); } else { hpcrun_restart_timer(self, 1); } hpcrun_safe_exit(); return 0; /* tell monitor that the signal has been handled */ }
int hpcrun_is_handling_sample(void) { return (TD_GET(handling_sample) == 0xDEADBEEF); }
// // Returns: address of non-freeable region at the high end, // else NULL on failure. // void * hpcrun_malloc(size_t size) { hpcrun_meminfo_t *mi; void *addr; // Lush wants to ask for 0 bytes and get back NULL. if (size == 0) { return NULL; } mi = &TD_GET(memstore); size = round_up(size); // For a large request that doesn't fit within the existing // memstore, mmap a separate region for it. if (size > memsize/5 && allow_extra_mmap && (mi->mi_start == NULL || size > mi->mi_high - mi->mi_low)) { addr = hpcrun_mmap_anon(size); if (addr == NULL) { if (! out_of_mem_mesg) { EMSG("%s: out of memory, shutting down sampling", __func__); out_of_mem_mesg = 1; } hpcrun_disable_sampling(); num_failures++; return NULL; } TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr); total_non_freeable += size; return addr; } // See if we need to allocate a new memstore. if (mi->mi_start == NULL || mi->mi_high - mi->mi_low < low_memsize || mi->mi_high - mi->mi_low < size) { if (allow_extra_mmap) { hpcrun_make_memstore(mi, 0); } else { if (! out_of_mem_mesg) { EMSG("%s: out of memory, shutting down sampling", __func__); out_of_mem_mesg = 1; } hpcrun_disable_sampling(); } } // There is no memstore, for some reason. if (mi->mi_start == NULL) { TMSG(MALLOC, "%s: size = %ld, failure: no memstore", __func__, size); num_failures++; return NULL; } // Not enough space in existing memstore. addr = mi->mi_high - size; if (addr <= mi->mi_low) { TMSG(MALLOC, "%s: size = %ld, failure: out of memory", __func__, size); num_failures++; return NULL; } // Success mi->mi_high = addr; total_non_freeable += size; TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr); return addr; }
extern bool LUSHI_do_metric(uint64_t incrMetricIn, bool* doMetric, bool* doMetricIdleness, uint64_t* incrMetric, double* incrMetricIdleness) { lushPthr_t* pthr = &TD_GET(pthr_metrics); bool isWorking = pthr->is_working; if (isWorking) { #if (LUSH_PTHR_FN_TY == 1) // NOTE: pthr->idleness is only changed when this thread is not working *doMetric = true; *doMetricIdleness = (pthr->idleness > 0); *incrMetric = incrMetricIn; *incrMetricIdleness = pthr->idleness; pthr->idleness = 0; #elif (LUSH_PTHR_FN_TY == 2) bool is_working_lock = lushPthr_isWorking_lock(pthr); double num_working = *(pthr->ps_num_working); double num_working_lock = *(pthr->ps_num_working_lock); double num_idle_cond = MAX(0, *(pthr->ps_num_idle_cond)); // timing! // INVARIANT: Since this thread is working, it is either working // while locked or it is working as 'other' (within a condition // variable critical section or without a lock). double idleness = 0.0; if (is_working_lock) { // ----------------------------------------------------- // is_working_lock() : num_idle_lock / num_working_lock // ----------------------------------------------------- double num_idle = (*(pthr->ps_num_threads) - num_working); double num_idle_lock = MAX(0, num_idle - num_idle_cond); num_working_lock = MAX(1, num_working_lock); // timing windows idleness = (num_idle_lock / num_working_lock); } else { // ----------------------------------------------------- // is_working_cond() || is_working : num_idle_cond / num_working_othr // ----------------------------------------------------- // INVARIANT: (num_working - num_working_lock) should always be > 0 double num_working_othr = MAX(1, num_working - num_working_lock); idleness = (num_idle_cond / num_working_othr); } *doMetric = true; *doMetricIdleness = true; *incrMetric = incrMetricIn; *incrMetricIdleness = (double)incrMetricIn * idleness; #elif (LUSH_PTHR_FN_TY == 3) // Same as 1 *doMetric = true; *doMetricIdleness = (pthr->idleness > 0); *incrMetric = incrMetricIn; *incrMetricIdleness = pthr->idleness; pthr->idleness = 0; #else # error "agent-pthread.c!" #endif } else { #if (LUSH_PTHR_FN_TY == 1) *doMetric = true; *doMetricIdleness = true; *incrMetric = 0; *incrMetricIdleness = (double)incrMetricIn; #elif (LUSH_PTHR_FN_TY == 2) *doMetric = false; *doMetricIdleness = false; //*incrMetric = 0; //*incrMetricIdleness = 0.0; #elif (LUSH_PTHR_FN_TY == 3) if (pthr->syncObjData) { // INVARIANT: spin waiting on pthr->syncObjData hpcrun_atomicAdd(&pthr->syncObjData->idleness, incrMetricIn); } *doMetric = false; *doMetricIdleness = false; #else # error "agent-pthread.c!" #endif } return *doMetric; }
void hpcrun_dlclose(void *handle) { hpcrun_dlopen_write_lock(); TD_GET(inside_dlfcn) = true; }