관리-도구
편집 파일: runtime_context.h
/* -*- 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_ */