runtime/dyninst/runtime_context.h - systemtap

Global variables defined

Functions defined

Macros defined

Source code

/* -*- linux-c -*-
* Context Runtime Functions
* Copyright (C) 2014 Red Hat Inc.
*
* This file is part of systemtap, and is free software.  You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*/

#ifndef _STAPDYN_RUNTIME_CONTEXT_H_
#define _STAPDYN_RUNTIME_CONTEXT_H_

#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <asm/unistd.h>

/* Defined after stap_probes[] by translate.cxx */
static const char* stp_probe_point(size_t index);

/* Defined later in common_session_state.h */
static inline struct context* stp_session_context(size_t index);

static int _stp_runtime_num_contexts;

/* Locally-cached context pointer -- see _stp_runtime_entryfn_get_context()
* and _stp_runtime_entryfn_put_context().  */
static __thread struct context *tls_context;

static int _stp_runtime_contexts_init(void)
{
    _stp_runtime_num_contexts = sysconf(_SC_NPROCESSORS_ONLN);
    if (_stp_runtime_num_contexts < 1)
    _stp_runtime_num_contexts = 1;
    return 0;
}

static int _stp_runtime_contexts_alloc(void)
{
    int i;

    /* The allocation was already done in stp_session_init;
     * we just need to initialize the context data.  */
    for (i = 0; i < _stp_runtime_num_contexts; i++) {
    int rc;
    struct context *c = stp_session_context(i);
    c->data_index = i;
    rc = stp_pthread_mutex_init_shared(&c->lock);
    if (rc != 0) {
        _stp_error("context mutex initialization failed");
        return rc;
    }
    }
    return 0;
}

/* Free the context resources.
*
* NB: This should *not* be called by every process which has mmaped the shared
* memory.  Only the main process which created shm and originally called
* _stp_runtime_contexts_alloc should be the one to free it.
*/
static void _stp_runtime_contexts_free(void)
{
    int i;

    /* The context memory is managed elsewhere;
     * we just need to teardown the context locks. */
    for (i = 0; i < _stp_runtime_num_contexts; i++) {
    struct context *c = stp_session_context(i);
    (void)pthread_mutex_destroy(&c->lock);
    }
}

static int _stp_runtime_get_data_index(void)
{
    int data_index;

    /* If this thread has already gotten a context structure,
     * return the data index from it. */
    if (tls_context != NULL)
    return tls_context->data_index;

    /* This shouldn't happen. */
    /* FIXME: assert? */
    return 0;
}

/* Figure out with cpu we're on, which is our default data_index.
* Make sure the returned data index number is within the range of
* [0.._stp_runtime_num_contexts]. Be sure to handle a sched_getcpu()
* failure (it will return -1). */
static int _stp_context_index(void)
{
    /* The current cpu is the preferred index, because it will usually be
     * different for every concurrent thread.  (It is possible to be the same
     * though, if kernel scheduling is unkind to us.)  */
    int index = _stp_sched_getcpu();

    /* Failing cpu#, use the tid as a somewhat-random index.  */
    if (index < 0)
        index = syscall(SYS_gettid);

    return index % _stp_runtime_num_contexts;
}

static struct context * _stp_runtime_entryfn_get_context(void)
{
    struct context *c;
    int i, index, rc, data_index;

    /* If 'tls_context' (which is thread-local storage) is already set
     * for this thread, we are re-entrant, so just quit. */
    if (tls_context != NULL)
    return NULL;

    data_index = _stp_context_index();
    if (unlikely(data_index < 0))
    data_index = 0;

    /* Try to find a free context structure. */
    index = data_index;
    for (i = 0; i < _stp_runtime_num_contexts; i++, index++) {
    if (index >= _stp_runtime_num_contexts)
        index = 0;
    c = stp_session_context(index);
    if (pthread_mutex_trylock(&c->lock) == 0) {
        /* We found a free context structure. Now that it is
         * locked, set the TLS pointer and return the context. */
        tls_context = c;
        return tls_context;
    }
    }

    /* If we're here, we couldn't find a free context structure. Wait
     * on one. */
    c = stp_session_context(data_index);
    rc = pthread_mutex_lock(&c->lock);
    if (rc == 0) {
    tls_context = c;
    return tls_context;
    }
    return NULL;
}

static void _stp_runtime_entryfn_put_context(struct context *c)
{
    if (c && c == tls_context) {
    tls_context = NULL;
    pthread_mutex_unlock(&c->lock);
    }
    /* else, warn about bad state? */
    return;
}

static struct context *_stp_runtime_get_context(void)
{
    /* Note we don't call _stp_runtime_entryfn_get_context()
     * here. This function is called after
     * _stp_runtime_entryfn_get_context() and has no corresponding
     * "put" function. */
    return tls_context;
}

static void _stp_runtime_context_wait(void)
{
    struct timespec hold_start;
    int hold_index;
    int holdon;

    (void)clock_gettime(CLOCK_MONOTONIC_RAW, &hold_start);
    hold_index = -1;
    do {
    int i;
    holdon = 0;
    struct timespec now, elapsed;

    for (i = 0; i < _stp_runtime_num_contexts; i++) {
        struct context *c = stp_session_context(i);
        int ret = pthread_mutex_trylock(&c->lock);
        if (ret == 0)
        pthread_mutex_unlock(&c->lock);
        else if (ret == EBUSY) {
        holdon = 1;

        /* Just In case things are really stuck, let's print
         * some diagnostics. */
        (void)clock_gettime(CLOCK_MONOTONIC_RAW, &now);
        _stp_timespec_sub(&now, &hold_start, &elapsed);

        /* If its been > 1 second since we started and we
         * haven't already printed a message for this stuck
         * context, print one. */
        if (elapsed.tv_sec > 0 && (i > hold_index)) {
            /* NB: c->probe_point is local memory, invalid across processes,
             * so read it indirectly through c->probe_index instead.  */
            const char* pp = stp_probe_point(c->probe_index);
            if (pp)
            _stp_error("context[%d] stuck: %s", i, pp);
            else
            _stp_error("context[%d] stuck in unknown probe %zu",
                   i, c->probe_index);
            hold_index = i;
        }
        }
        /* else other pthread error == broken? what then? */
    }

#ifdef STAP_OVERRIDE_STUCK_CONTEXT
    /* In case things are really really stuck, we are going to
     * pretend/assume/hope everything is OK, and let the cleanup
     * finish. */
    (void)clock_gettime(CLOCK_MONOTONIC_RAW, &now);
    _stp_timespec_sub(&now, &hold_start, &elapsed);
    if (elapsed.tv_sec > 10) {
        _stp_warn("overriding stuck context to allow shutdown.");
        holdon = 0;            /* allow loop to exit */
    }
#endif

    if (holdon) {
        sched_yield();
    }
    } while (holdon);
}

#endif /* _STAPDYN_RUNTIME_CONTEXT_H_ */