/* * Called from real_pthread_create(), it's where the newly-created * thread begins. */ static void * monitor_begin_thread(void *arg) { struct monitor_thread_node *tn = arg; void *ret; MONITOR_ASM_LABEL(monitor_thread_fence1); MONITOR_DEBUG1("\n"); /* * Wait for monitor_init_thread_support() to finish in the main * thread before this thread runs. * * Note: if this thread is created before libc_start_main (OpenMP * does this), then this will wait for both init_process and * thread_support to finish from libc_start_main. And that has * the potential to deadlock if the application waits for this * thread to accomplish something before it finishes its library * init. (An evil thing for it to do, but it's possible.) */ while (! monitor_thread_support_done) usleep(MONITOR_POLL_USLEEP_TIME); /* * Don't create any new threads after someone has called exit(). */ tn->tn_self = (*real_pthread_self)(); tn->tn_stack_bottom = alloca(8); strncpy(tn->tn_stack_bottom, "stakbot", 8); if ((*real_pthread_setspecific)(monitor_pthread_key, tn) != 0) { MONITOR_ERROR1("pthread_setspecific failed\n"); } if (monitor_link_thread_node(tn) != 0) { MONITOR_DEBUG1("warning: trying to create new thread during " "exit cleanup: thread not started\n"); return (NULL); } PTHREAD_CLEANUP_PUSH(monitor_pthread_cleanup_routine, tn); MONITOR_DEBUG("tid = %d, self = %p, start_routine = %p\n", tn->tn_tid, (void *)tn->tn_self, tn->tn_start_routine); MONITOR_DEBUG("calling monitor_init_thread(tid = %d, data = %p) ...\n", tn->tn_tid, tn->tn_user_data); tn->tn_user_data = monitor_init_thread(tn->tn_tid, tn->tn_user_data); tn->tn_appl_started = 1; MONITOR_ASM_LABEL(monitor_thread_fence2); ret = (tn->tn_start_routine)(tn->tn_arg); MONITOR_ASM_LABEL(monitor_thread_fence3); PTHREAD_CLEANUP_POP(1); MONITOR_ASM_LABEL(monitor_thread_fence4); return (ret); }
/* * Run in the main thread on the first call to pthread_create(), * before any new threads are created. Only called once from the * pthread_create() override. */ static void monitor_thread_list_init(void) { struct monitor_thread_node *main_tn; int ret; /* * Give main.c functions a chance to initialize in the case that * some library calls pthread_create() before main(). */ monitor_early_init(); monitor_thread_name_init(); MONITOR_DEBUG1("\n"); LIST_INIT(&monitor_thread_list); LIST_INIT(&monitor_free_list); monitor_tn_array_pos = 0; ret = (*real_pthread_key_create)(&monitor_pthread_key, NULL); if (ret != 0) { MONITOR_ERROR("pthread_key_create failed (%d)\n", ret); } /* * main_tn's thread-specific data. */ main_tn = monitor_get_main_tn(); if (main_tn == NULL || main_tn->tn_magic != MONITOR_TN_MAGIC) { MONITOR_ERROR1("monitor_get_main_tn failed\n"); } main_tn->tn_self = (*real_pthread_self)(); ret = (*real_pthread_setspecific)(monitor_pthread_key, main_tn); if (ret != 0) { MONITOR_ERROR("pthread_setspecific failed (%d)\n", ret); } }
void MONITOR_WRAP_NAME(mpi_finalize__)(int *ierror) { int count; MONITOR_DEBUG1("\n"); MONITOR_GET_REAL_NAME_WRAP(real_mpi_finalize, mpi_finalize__); count = monitor_mpi_fini_count(1); if (count == 1) { MONITOR_DEBUG("calling monitor_fini_mpi(), size = %d, rank = %d ...\n", monitor_mpi_comm_size(), monitor_mpi_comm_rank()); monitor_fini_mpi(); } (*real_mpi_finalize)(ierror); if (count == 1) { MONITOR_DEBUG1("calling monitor_mpi_post_fini() ...\n"); monitor_mpi_post_fini(); } monitor_mpi_fini_count(-1); }
/* * Call monitor_thread_support. */ static inline void monitor_call_thread_support(void) { if (monitor_thread_support_done) { MONITOR_WARN1("attempted to call thread support twice\n"); return; } MONITOR_DEBUG1("calling monitor_init_thread_support() ...\n"); monitor_init_thread_support(); monitor_thread_support_done = 1; }
void monitor_enable_new_threads(void) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL) { MONITOR_DEBUG1("unable to find thread node\n"); return; } tn->tn_ignore_threads = 0; }
void monitor_unblock_shootdown(void) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL) { MONITOR_DEBUG1("unable to find thread node\n"); return; } tn->tn_block_shootdown = 0; }
/* * Returns: an address inside the thread's bottom stack frame, * or else NULL if an error occurred. */ void * monitor_stack_bottom(void) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL) { MONITOR_DEBUG1("unable to find thread node\n"); return (NULL); } return (tn->tn_stack_bottom); }
/* * Returns: the client's data pointer from monitor_init_process() or * monitor_init_thread(), or else NULL on error. */ void * monitor_get_user_data(void) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL) { MONITOR_DEBUG1("unable to find thread node\n"); return (NULL); } return (tn->tn_user_data); }
void MONITOR_WRAP_NAME(mpi_init_thread_)(int *required, int *provided, int *ierror) { int argc, count; char **argv; MONITOR_DEBUG1("\n"); MONITOR_GET_REAL_NAME_WRAP(real_mpi_init_thread, mpi_init_thread_); count = monitor_mpi_init_count(1); if (count == 1) { MONITOR_DEBUG1("calling monitor_mpi_pre_init() ...\n"); monitor_mpi_pre_init(); } (*real_mpi_init_thread)(required, provided, ierror); if (count == 1) { MONITOR_DEBUG1("calling monitor_init_mpi() ...\n"); monitor_get_main_args(&argc, &argv, NULL); monitor_init_mpi(&argc, &argv); } monitor_mpi_init_count(-1); }
int MONITOR_WRAP_NAME(MPI_Init)(int *argc, char ***argv) { int ret, count; MONITOR_DEBUG1("\n"); MONITOR_GET_REAL_NAME_WRAP(real_mpi_init, MPI_Init); count = monitor_mpi_init_count(1); if (count == 1) { MONITOR_DEBUG1("calling monitor_mpi_pre_init() ...\n"); monitor_mpi_pre_init(); } ret = (*real_mpi_init)(argc, argv); if (count == 1) { MONITOR_DEBUG1("calling monitor_init_mpi() ...\n"); monitor_init_mpi(argc, argv); } monitor_mpi_init_count(-1); return (ret); }
int MONITOR_WRAP_NAME(PMPI_Init_thread)(int *argc, char ***argv, int required, int *provided) { int ret, count; MONITOR_DEBUG1("\n"); MONITOR_GET_REAL_NAME_WRAP(real_pmpi_init_thread, PMPI_Init_thread); count = monitor_mpi_init_count(1); if (count == 1) { MONITOR_DEBUG1("calling monitor_mpi_pre_init() ...\n"); monitor_mpi_pre_init(); } ret = (*real_pmpi_init_thread)(argc, argv, required, provided); if (count == 1) { MONITOR_DEBUG1("calling monitor_init_mpi() ...\n"); monitor_init_mpi(argc, argv); } monitor_mpi_init_count(-1); return (ret); }
int MONITOR_WRAP_NAME(PMPI_Finalize)(void) { int ret, count; MONITOR_DEBUG1("\n"); MONITOR_GET_REAL_NAME_WRAP(real_pmpi_finalize, PMPI_Finalize); count = monitor_mpi_fini_count(1); if (count == 1) { MONITOR_DEBUG("calling monitor_fini_mpi(), size = %d, rank = %d ...\n", monitor_mpi_comm_size(), monitor_mpi_comm_rank()); monitor_fini_mpi(); } ret = (*real_pmpi_finalize)(); if (count == 1) { MONITOR_DEBUG1("calling monitor_mpi_post_fini() ...\n"); monitor_mpi_post_fini(); } monitor_mpi_fini_count(-1); return (ret); }
/* * Block and unblock receiving a thread shootdown signal. This is * used in hpctoolkit to block receiving the fini-thread callback * (via shootdown signal) while in the middle of a sample. * * Returns: 1 if already at process exit. */ int monitor_block_shootdown(void) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL) { MONITOR_DEBUG1("unable to find thread node\n"); return 0; } tn->tn_block_shootdown = 1; return monitor_in_exit_cleanup; }
/* * Pthread_exit() from the main thread exits the process, and this * bypasses our other exit-catching methods, so we have to override * it here. */ void MONITOR_WRAP_NAME(pthread_exit)(void *data) { struct monitor_thread_node *tn; tn = monitor_get_tn(); if (tn == NULL || tn->tn_is_main) { MONITOR_DEBUG1("pthread_exit called from main thread\n"); monitor_end_process_fcn(MONITOR_EXIT_NORMAL); } MONITOR_GET_REAL_NAME_WRAP(real_pthread_exit, pthread_exit); (*real_pthread_exit)(data); /* Never reached, but silence a compiler warning. */ exit(0); }
/* * Reset the thread list in the child after fork(). * * If a threaded program forks, then the child has only one running * thread. So, reset the thread node for the main thread, and free * the thread list and the pthread key. */ void monitor_reset_thread_list(struct monitor_thread_node *main_tn) { struct monitor_thread_node *tn; if (! monitor_has_used_threads) return; MONITOR_DEBUG1("\n"); /* * The thread that fork()ed is now the main thread. */ tn = monitor_get_tn(); if (tn == NULL) { MONITOR_WARN1("tn should not be NULL here\n"); } else if (tn != main_tn) { memset(main_tn, 0, sizeof(struct monitor_thread_node)); main_tn->tn_magic = MONITOR_TN_MAGIC; main_tn->tn_tid = 0; main_tn->tn_user_data = tn->tn_user_data; main_tn->tn_stack_bottom = tn->tn_stack_bottom; main_tn->tn_is_main = 1; } /* * Free the thread list and the pthread key. Technically, this * could leak memory, but only if the process spawns more than 150 * threads before fork()ing. */ LIST_INIT(&monitor_thread_list); LIST_INIT(&monitor_free_list); monitor_tn_array_pos = 0; if ((*real_pthread_key_delete)(monitor_pthread_key) != 0) { MONITOR_WARN1("pthread_key_delete failed\n"); } monitor_has_used_threads = 0; }
/* * Called from main.c at end process time for possible thread cleanup. * Note: main doesn't know if application is threaded or not. */ void monitor_thread_shootdown(void) { struct timeval last, now; struct monitor_thread_node *tn, *my_tn; struct sigaction my_action; sigset_t empty_set; pthread_t self; int num_started, num_unstarted, last_started; int num_finished, num_unfinished, old_state; if (! monitor_has_used_threads) { MONITOR_DEBUG1("(no threads)\n"); return; } (*real_pthread_setcancelstate)(PTHREAD_CANCEL_DISABLE, &old_state); MONITOR_THREAD_LOCK; monitor_in_exit_cleanup = 1; MONITOR_THREAD_UNLOCK; MONITOR_DEBUG1("(threads)\n"); /* * Install the signal handler for thread shootdown. * Note: the signal handler is process-wide. */ shootdown_signal = monitor_shootdown_signal(); MONITOR_DEBUG("using signal: %d\n", shootdown_signal); sigemptyset(&empty_set); my_action.sa_handler = monitor_shootdown_handler; my_action.sa_mask = empty_set; my_action.sa_flags = SA_RESTART; if ((*real_sigaction)(shootdown_signal, &my_action, NULL) != 0) { MONITOR_ERROR1("sigaction failed\n"); } /* * Walk through the list of unfinished threads, send a signal to * force them into their fini_thread functions, and wait until * they all finish. But don't signal ourself. * * Add a timeout: if we make no progress for 10 consecutive * seconds, then give up. Progress means receiving the signal in * the other thread, the fini thread callback can take as long as * it likes. */ self = (*real_pthread_self)(); my_tn = NULL; gettimeofday(&last, NULL); last_started = 0; for (;;) { num_started = 0; num_unstarted = 0; num_finished = 0; num_unfinished = 0; for (tn = LIST_FIRST(&monitor_thread_list); tn != NULL; tn = LIST_NEXT(tn, tn_links)) { if (PTHREAD_EQUAL(self, tn->tn_self)) { my_tn = tn; continue; } if (tn->tn_appl_started) { if (tn->tn_fini_started) { num_started++; } else { (*real_pthread_kill)(tn->tn_self, shootdown_signal); num_unstarted++; } if (tn->tn_fini_done) num_finished++; else num_unfinished++; } } MONITOR_DEBUG("started: %d, unstarted: %d, finished: %d, unfinished: %d\n", num_started, num_unstarted, num_finished, num_unfinished); if (num_unfinished == 0) break; gettimeofday(&now, NULL); if (num_started > last_started) { last = now; last_started = num_started; } else if (now.tv_sec > last.tv_sec + MONITOR_SHOOTDOWN_TIMEOUT && num_unstarted > 0) { MONITOR_WARN("timeout exceeded (%d): unable to deliver " "monitor_fini_thread() to %d threads\n", MONITOR_SHOOTDOWN_TIMEOUT, num_unstarted); break; } usleep(MONITOR_POLL_USLEEP_TIME); } monitor_fini_thread_done = 1; /* * See if we need to run fini_thread from this thread. */ if (my_tn != NULL && !my_tn->tn_fini_started) { my_tn->tn_fini_started = 1; MONITOR_DEBUG("calling monitor_fini_thread(data = %p), tid = %d ...\n", my_tn->tn_user_data, my_tn->tn_tid); monitor_fini_thread(my_tn->tn_user_data); my_tn->tn_fini_done = 1; } (*real_pthread_setcancelstate)(old_state, NULL); }
/* * Override pthread_create(). */ int MONITOR_WRAP_NAME(pthread_create)(PTHREAD_CREATE_PARAM_LIST) { struct monitor_thread_node *tn; pthread_attr_t default_attr; int ret, restore, destroy; size_t old_size; MONITOR_DEBUG1("\n"); /* * There is no race condition to get here first because until now, * there is only one thread. */ if (! monitor_has_used_threads) { monitor_thread_list_init(); monitor_has_used_threads = 1; } /* * If we are ignoring this thread, then call the real * pthread_create(), don't put it on the thread list and don't * give any callbacks. */ tn = monitor_get_tn(); if (tn != NULL && tn->tn_ignore_threads) { MONITOR_DEBUG1("ignoring this new thread\n"); return (*real_pthread_create)(thread, attr, start_routine, arg); } /* * Normally, we run thread_support here, on the first call to * pthread_create(). But if we're here early, before * libc_start_main, then defer thread_support until after * init_process in libc_start_main. */ if (! monitor_thread_support_done) { if (monitor_has_reached_main) { monitor_call_thread_support(); } else { MONITOR_DEBUG1("deferring thread support\n"); } } /* * Always launch the thread. If we're in pthread_create() too * early, then the new thread will spin-wait until init_process * and thread_support are done. */ tn = monitor_make_thread_node(); tn->tn_start_routine = start_routine; tn->tn_arg = arg; MONITOR_DEBUG("calling monitor_thread_pre_create(start_routine = %p) ...\n", start_routine); tn->tn_user_data = monitor_thread_pre_create(); /* * Allow the client to change the thread stack size. Note: we * need to restore the original size in case the application uses * one attribute struct for several threads (so we don't keep * increasing its size). */ attr = monitor_adjust_stack_size((pthread_attr_t *)attr, &default_attr, &restore, &destroy, &old_size); ret = (*real_pthread_create)(thread, attr, monitor_begin_thread, (void *)tn); if (restore) { (*real_pthread_attr_setstacksize)((pthread_attr_t *)attr, old_size); } if (destroy) { (*real_pthread_attr_destroy)(&default_attr); } if (ret != 0) { MONITOR_DEBUG("real_pthread_create failed: start_routine = %p, ret = %d\n", start_routine, ret); } MONITOR_DEBUG("calling monitor_thread_post_create(start_routine = %p) ...\n", start_routine); monitor_thread_post_create(tn->tn_user_data); return (ret); }