Beispiel #1
0
/*
 *  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);
}
Beispiel #2
0
/*
 *  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);
    }
}
Beispiel #3
0
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);
}
Beispiel #4
0
/*
 *  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;
}
Beispiel #5
0
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;
}
Beispiel #6
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;
}
Beispiel #7
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);
}
Beispiel #8
0
/*
 *  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);
}
Beispiel #10
0
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);
}
Beispiel #11
0
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);
}
Beispiel #12
0
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);
}
Beispiel #13
0
/*
 *  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;
}
Beispiel #14
0
/*
 *  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);
}
Beispiel #15
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;
}
Beispiel #16
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);
}
Beispiel #17
0
/*
 *  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);
}