Your IP : 18.223.241.235
/*
* OXT - OS eXtensions for boosT
* Provides important functionality necessary for writing robust server software.
*
* Copyright (c) 2008-2017 Phusion Holding B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include "tracable_exception.hpp"
#include "backtrace.hpp"
#include "initialize.hpp"
#include "macros.hpp"
#include "thread.hpp"
#include "spin_lock.hpp"
#include "detail/context.hpp"
#ifdef __linux__
#include <sys/syscall.h>
#endif
#ifdef OXT_THREAD_LOCAL_KEYWORD_SUPPORTED
#include <cassert>
#include <sstream>
#include <cstring>
#endif
#include <cstring>
namespace oxt {
using namespace std;
using namespace boost;
static global_context_t *global_context = NULL;
/*
* boost::thread_specific_storage is pretty expensive. So we use the __thread
* keyword whenever possible - that's almost free.
*/
#ifdef OXT_THREAD_LOCAL_KEYWORD_SUPPORTED
static __thread thread_local_context_ptr *local_context = NULL;
__thread void *thread_signature = NULL;
static void
init_thread_local_context_support() {
/* Do nothing. */
}
void
set_thread_local_context(const thread_local_context_ptr &ctx) {
local_context = new thread_local_context_ptr(ctx);
}
static void
free_thread_local_context() {
delete local_context;
local_context = NULL;
}
thread_local_context *
get_thread_local_context() {
if (OXT_LIKELY(local_context != NULL)) {
return local_context->get();
} else {
return NULL;
}
}
#else
/*
* This is a *pointer* to a thread_specific_ptr because, once
* we've created a thread_specific_ptr, we never want to destroy it
* in order to avoid C++'s global variable destruction. All kinds
* of cleanup code may depend on local_context and because global
* variable destruction order is undefined, we just want to keep
* this object alive until the OS cleans it up.
*/
static thread_specific_ptr<thread_local_context_ptr> *local_context = NULL;
static void
init_thread_local_context_support() {
local_context = new thread_specific_ptr<thread_local_context_ptr>();
}
void
set_thread_local_context(const thread_local_context_ptr &ctx) {
if (local_context != NULL) {
local_context->reset(new thread_local_context_ptr(ctx));
}
}
static void
free_thread_local_context() {
if (local_context != NULL) {
local_context->reset();
}
}
thread_local_context *
get_thread_local_context() {
if (OXT_LIKELY(local_context != NULL)) {
thread_local_context_ptr *pointer = local_context->get();
if (OXT_LIKELY(pointer != NULL)) {
return pointer->get();
} else {
return NULL;
}
} else {
return NULL;
}
}
#endif
#ifdef OXT_BACKTRACE_IS_ENABLED
trace_point::trace_point(const char *_function, const char *_source, unsigned short _line,
const char *_data)
: function(_function),
source(_source),
line(_line),
m_detached(false),
m_hasDataFunc(false)
{
thread_local_context *ctx = get_thread_local_context();
if (OXT_LIKELY(ctx != NULL)) {
spin_lock::scoped_lock l(ctx->backtrace_lock);
ctx->backtrace_list.push_back(this);
} else {
m_detached = true;
}
u.data = _data;
}
trace_point::trace_point(const char *_function, const char *_source, unsigned short _line,
DataFunction _dataFunc, void *_userData, bool detached)
: function(_function),
source(_source),
line(_line),
m_detached(detached),
m_hasDataFunc(true)
{
if (!detached) {
thread_local_context *ctx = get_thread_local_context();
if (OXT_LIKELY(ctx != NULL)) {
spin_lock::scoped_lock l(ctx->backtrace_lock);
ctx->backtrace_list.push_back(this);
} else {
m_detached = true;
}
}
u.dataFunc.func = _dataFunc;
u.dataFunc.userData = _userData;
}
trace_point::trace_point(const char *_function, const char *_source, unsigned short _line,
const char *_data, const detached &detached_tag)
: function(_function),
source(_source),
line(_line),
m_detached(true),
m_hasDataFunc(false)
{
u.data = _data;
}
trace_point::~trace_point() {
if (OXT_LIKELY(!m_detached)) {
thread_local_context *ctx = get_thread_local_context();
if (OXT_LIKELY(ctx != NULL)) {
spin_lock::scoped_lock l(ctx->backtrace_lock);
assert(!ctx->backtrace_list.empty());
ctx->backtrace_list.pop_back();
}
}
}
void
trace_point::update(const char *source, unsigned short line) {
this->source = source;
this->line = line;
}
tracable_exception::tracable_exception() {
thread_local_context *ctx = get_thread_local_context();
if (OXT_LIKELY(ctx != NULL)) {
spin_lock::scoped_lock l(ctx->backtrace_lock);
vector<trace_point *>::const_iterator it, end = ctx->backtrace_list.end();
backtrace_copy.reserve(ctx->backtrace_list.size());
for (it = ctx->backtrace_list.begin(); it != end; it++) {
trace_point *p;
if ((*it)->m_hasDataFunc) {
p = new trace_point(
(*it)->function,
(*it)->source,
(*it)->line,
(*it)->u.dataFunc.func,
(*it)->u.dataFunc.userData,
true);
} else {
p = new trace_point(
(*it)->function,
(*it)->source,
(*it)->line,
(*it)->u.data,
trace_point::detached());
}
backtrace_copy.push_back(p);
}
}
}
tracable_exception::tracable_exception(const tracable_exception &other)
: std::exception()
{
vector<trace_point *>::const_iterator it, end = other.backtrace_copy.end();
backtrace_copy.reserve(other.backtrace_copy.size());
for (it = other.backtrace_copy.begin(); it != end; it++) {
trace_point *p;
if ((*it)->m_hasDataFunc) {
p = new trace_point(
(*it)->function,
(*it)->source,
(*it)->line,
(*it)->u.dataFunc.func,
(*it)->u.dataFunc.userData,
true);
} else {
p = new trace_point(
(*it)->function,
(*it)->source,
(*it)->line,
(*it)->u.data,
trace_point::detached());
}
backtrace_copy.push_back(p);
}
}
tracable_exception::tracable_exception(const no_backtrace &tag)
: std::exception()
{
// Do nothing.
}
tracable_exception::~tracable_exception() throw() {
vector<trace_point *>::iterator it, end = backtrace_copy.end();
for (it = backtrace_copy.begin(); it != end; it++) {
delete *it;
}
}
template<typename Collection>
static string
format_backtrace(const Collection &backtrace_list) {
if (backtrace_list.empty()) {
return " (empty)";
} else {
backtrace_list.rbegin();
stringstream result;
typename Collection::const_reverse_iterator it;
for (it = backtrace_list.rbegin(); it != backtrace_list.rend(); it++) {
const trace_point *p = *it;
result << " in '" << p->function << "'";
if (p->source != NULL) {
const char *source = strrchr(p->source, '/');
if (source != NULL) {
source++;
} else {
source = p->source;
}
result << " (" << source << ":" << p->line << ")";
if (p->m_hasDataFunc) {
if (p->u.dataFunc.func != NULL) {
char buf[64];
memset(buf, 0, sizeof(buf));
if (p->u.dataFunc.func(buf, sizeof(buf) - 1, p->u.dataFunc.userData)) {
buf[63] = '\0';
result << " -- " << buf;
}
}
} else if (p->u.data != NULL) {
result << " -- " << p->u.data;
}
}
result << endl;
}
return result.str();
}
}
string
tracable_exception::backtrace() const throw() {
return format_backtrace< vector<trace_point *> >(backtrace_copy);
}
const char *
tracable_exception::what() const throw() {
return "oxt::tracable_exception";
}
#endif /* OXT_BACKTRACE_IS_ENABLED */
void
initialize() {
global_context = new global_context_t();
init_thread_local_context_support();
thread_local_context_ptr ctx = thread_local_context::make_shared_ptr();
ctx->thread_number = 1;
ctx->thread_name = "Main thread";
set_thread_local_context(ctx);
ctx->thread = pthread_self();
global_context->registered_threads.push_back(ctx);
ctx->iterator = global_context->registered_threads.end();
ctx->iterator--;
}
void shutdown() {
free_thread_local_context();
delete global_context;
global_context = NULL;
}
global_context_t::global_context_t()
: next_thread_number(2)
{ }
thread_local_context_ptr
thread_local_context::make_shared_ptr() {
// For some reason make_shared() crashes here when compiled with clang 3.2 on OS X.
// Clang bug? We use 'new' to work around it.
return thread_local_context_ptr(new thread_local_context());
}
thread_local_context::thread_local_context()
: thread_number(0)
{
thread = pthread_self();
#ifdef __linux__
tid = syscall(SYS_gettid);
#endif
syscall_interruption_lock.lock();
#ifdef OXT_BACKTRACE_IS_ENABLED
backtrace_list.reserve(50);
#endif
}
string
thread::make_thread_name(const string &given_name) {
if (given_name.empty()) {
if (OXT_LIKELY(global_context != NULL)) {
stringstream str;
str << "Thread #";
{
boost::lock_guard<boost::mutex> l(global_context->thread_registration_mutex);
str << global_context->next_thread_number;
}
return str.str();
} else {
return "(unknown)";
}
} else {
return given_name;
}
}
void
thread::thread_main(const boost::function<void ()> func, thread_local_context_ptr ctx) {
set_thread_local_context(ctx);
if (OXT_LIKELY(global_context != NULL)) {
boost::lock_guard<boost::mutex> l(global_context->thread_registration_mutex);
ctx->thread = pthread_self();
global_context->next_thread_number++;
global_context->registered_threads.push_back(ctx);
ctx->iterator = global_context->registered_threads.end();
ctx->iterator--;
// Set this after setting 'iterator' to indicate
// that push_back() has succeeded.
ctx->thread_number = global_context->next_thread_number;
}
try {
func();
} catch (const thread_interrupted &) {
// Do nothing.
}
// We don't care about other exceptions because they'll crash the process anyway.
if (OXT_LIKELY(global_context != NULL)) {
boost::lock_guard<boost::mutex> l(global_context->thread_registration_mutex);
thread_local_context *ctx = get_thread_local_context();
if (ctx != 0 && ctx->thread_number != 0) {
global_context->registered_threads.erase(ctx->iterator);
ctx->thread_number = 0;
}
}
free_thread_local_context();
}
std::string
thread::name() const throw() {
return context->thread_name;
}
std::string
thread::backtrace() const throw() {
#ifdef OXT_BACKTRACE_IS_ENABLED
spin_lock::scoped_lock l(context->backtrace_lock);
return format_backtrace(context->backtrace_list);
#else
return " (backtrace support disabled during compile time)";
#endif
}
string
thread::all_backtraces() throw() {
#ifdef OXT_BACKTRACE_IS_ENABLED
if (OXT_LIKELY(global_context != NULL)) {
boost::lock_guard<boost::mutex> l(global_context->thread_registration_mutex);
list<thread_local_context_ptr>::const_iterator it;
std::stringstream result;
for (it = global_context->registered_threads.begin();
it != global_context->registered_threads.end();
it++)
{
thread_local_context_ptr ctx = *it;
result << "Thread '" << ctx->thread_name <<
"' (" << hex << showbase << ctx->thread << dec;
#ifdef __linux__
result << ", LWP " << ctx->tid;
#endif
result << "):" << endl;
spin_lock::scoped_lock l(ctx->backtrace_lock);
std::string bt = format_backtrace(ctx->backtrace_list);
result << bt;
if (bt.empty() || bt[bt.size() - 1] != '\n') {
result << endl;
}
result << endl;
}
return result.str();
} else {
return "(OXT not initialized)";
}
#else
return "(backtrace support disabled during compile time)";
#endif
}
string
thread::current_backtrace() throw() {
#ifdef OXT_BACKTRACE_IS_ENABLED
thread_local_context *ctx = get_thread_local_context();
if (OXT_LIKELY(ctx != NULL)) {
spin_lock::scoped_lock l(ctx->backtrace_lock);
return format_backtrace(ctx->backtrace_list);
} else {
return "(OXT not initialized)";
}
#else
return "(backtrace support disabled during compile time)";
#endif
}
void
thread::interrupt(bool interruptSyscalls) {
int ret;
boost::thread::interrupt();
if (interruptSyscalls && context->syscall_interruption_lock.try_lock()) {
do {
ret = pthread_kill(native_handle(),
INTERRUPTION_SIGNAL);
} while (ret == EINTR);
context->syscall_interruption_lock.unlock();
}
}
} // namespace oxt