interface/thread, server/state: Support callback-chain backtraces in all threads
This commit is contained in:
parent
873213aae9
commit
0115364cbe
@ -2,11 +2,9 @@
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "interface/thread.h"
|
||||
#include "interface/mutex.h"
|
||||
//#include "interface/semaphore.h"
|
||||
#include "interface/debug.h"
|
||||
#include "core/log.h"
|
||||
#include <c55/os.h>
|
||||
#include <deque>
|
||||
#ifdef _WIN32
|
||||
#include "ports/windows_compat.h"
|
||||
#else
|
||||
@ -17,6 +15,13 @@
|
||||
|
||||
namespace interface {
|
||||
|
||||
ThreadLocalKey g_current_thread_key;
|
||||
|
||||
Thread* Thread::get_current_thread()
|
||||
{
|
||||
return (Thread*)g_current_thread_key.get();
|
||||
}
|
||||
|
||||
int g_thread_name_counter = 0;
|
||||
interface::Mutex g_thread_name_counter_mutex;
|
||||
|
||||
@ -29,12 +34,14 @@ struct CThread: public Thread
|
||||
ThreadedThing *m_thing;
|
||||
pthread_t m_thread;
|
||||
bool m_thread_exists = false;
|
||||
//interface::Semaphore m_thread_exit_sem;
|
||||
|
||||
// Debug data
|
||||
std::list<ThreadBacktrace> m_backtraces;
|
||||
Thread *m_caller_thread = nullptr;
|
||||
|
||||
CThread(ThreadedThing *thing)
|
||||
{
|
||||
m_thing = thing;
|
||||
//m_thread_exit_sem.post();
|
||||
}
|
||||
|
||||
~CThread()
|
||||
@ -46,15 +53,17 @@ struct CThread: public Thread
|
||||
log_e(MODULE, "Thread %p (%s) destructed but not stopped",
|
||||
this, cs(m_name));
|
||||
}
|
||||
/*request_stop();
|
||||
join();*/
|
||||
//m_thread_exit_sem.wait();
|
||||
delete m_thing;
|
||||
}
|
||||
|
||||
static void* run_thread(void *arg)
|
||||
{
|
||||
CThread *thread = (CThread*)arg;
|
||||
|
||||
// Set pointer in thread-local storage
|
||||
g_current_thread_key.set(thread);
|
||||
|
||||
// Get name from parameter
|
||||
ss_ thread_name;
|
||||
{
|
||||
interface::MutexScope ms(thread->m_mutex);
|
||||
@ -62,7 +71,7 @@ struct CThread: public Thread
|
||||
}
|
||||
log_d(MODULE, "Thread started: %p (%s)", thread, cs(thread_name));
|
||||
|
||||
// Set name
|
||||
// Set thread name
|
||||
if(!thread_name.empty()){
|
||||
ss_ limited_name = thread_name.size() <= 15 ?
|
||||
thread_name : thread_name.substr(0, 15);
|
||||
@ -85,19 +94,30 @@ struct CThread: public Thread
|
||||
} catch(std::exception &e){
|
||||
log_w(MODULE, "ThreadThing of thread %p (%s) failed: %s",
|
||||
arg, cs(thread_name), e.what());
|
||||
if(!thread->m_backtraces.empty()){
|
||||
ss_ ex_name;
|
||||
for(const interface::ThreadBacktrace &bt_step :
|
||||
thread->m_backtraces){
|
||||
if(!bt_step.bt.exception_name.empty())
|
||||
ex_name = bt_step.bt.exception_name;
|
||||
interface::debug::log_backtrace(bt_step.bt,
|
||||
"Backtrace in M["+bt_step.thread_name+"] for "+
|
||||
ex_name+"(\""+e.what()+"\")");
|
||||
}
|
||||
} else {
|
||||
interface::debug::StoredBacktrace bt;
|
||||
interface::debug::get_exception_backtrace(bt);
|
||||
interface::debug::log_backtrace(bt,
|
||||
"Backtrace in ThreadThing("+thread_name+") for "+
|
||||
bt.exception_name+"(\""+e.what()+"\")");
|
||||
}
|
||||
}
|
||||
|
||||
log_d(MODULE, "Thread %p (%s) exit", arg, cs(thread_name));
|
||||
{
|
||||
interface::MutexScope ms(thread->m_mutex);
|
||||
thread->m_running = false;
|
||||
}
|
||||
//thread->m_thread_exit_sem.post();
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
@ -115,7 +135,6 @@ struct CThread: public Thread
|
||||
|
||||
void start()
|
||||
{
|
||||
//m_thread_exit_sem.wait();
|
||||
if(m_name.empty()){
|
||||
// Generate a name
|
||||
interface::MutexScope ms(g_thread_name_counter_mutex);
|
||||
@ -168,6 +187,29 @@ struct CThread: public Thread
|
||||
m_thread_exists = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Debugging interface (not thread-safe; access only from thread itself)
|
||||
|
||||
ss_ get_name()
|
||||
{
|
||||
interface::MutexScope ms(m_mutex);
|
||||
return m_name;
|
||||
}
|
||||
|
||||
std::list<ThreadBacktrace>& ref_backtraces()
|
||||
{
|
||||
return m_backtraces;
|
||||
}
|
||||
|
||||
void set_caller_thread(Thread *thread)
|
||||
{
|
||||
m_caller_thread = thread;
|
||||
}
|
||||
|
||||
Thread* get_caller_thread()
|
||||
{
|
||||
return m_caller_thread;
|
||||
}
|
||||
};
|
||||
|
||||
Thread* createThread(ThreadedThing *thing)
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#pragma once
|
||||
#include "core/types.h"
|
||||
#include "interface/debug.h"
|
||||
#include <list>
|
||||
|
||||
namespace interface
|
||||
{
|
||||
@ -14,6 +16,11 @@ namespace interface
|
||||
virtual void run(interface::Thread *thread) = 0;
|
||||
};
|
||||
|
||||
struct ThreadBacktrace {
|
||||
ss_ thread_name;
|
||||
interface::debug::StoredBacktrace bt;
|
||||
};
|
||||
|
||||
struct Thread
|
||||
{
|
||||
virtual ~Thread(){}
|
||||
@ -23,6 +30,14 @@ namespace interface
|
||||
virtual void request_stop() = 0;
|
||||
virtual bool stop_requested() = 0;
|
||||
virtual void join() = 0;
|
||||
|
||||
// Debugging interface (not thread-safe; access only from thread itself)
|
||||
virtual ss_ get_name() = 0;
|
||||
virtual std::list<ThreadBacktrace>& ref_backtraces() = 0;
|
||||
virtual void set_caller_thread(Thread *thread) = 0;
|
||||
virtual Thread* get_caller_thread() = 0;
|
||||
|
||||
static Thread* get_current_thread();
|
||||
};
|
||||
|
||||
Thread* createThread(ThreadedThing *thing);
|
||||
|
@ -67,7 +67,6 @@ struct ModuleContainer
|
||||
// Allows directly executing code in the module thread
|
||||
const std::function<void(interface::Module*)> *direct_cb = nullptr;
|
||||
std::exception_ptr direct_cb_exception = nullptr;
|
||||
ModuleContainer *direct_cb_caller_mc = nullptr;
|
||||
// The actual event queue
|
||||
std::deque<Event> event_queue; // Push back, pop front
|
||||
// Protects direct_cb and event_queue
|
||||
@ -79,14 +78,10 @@ struct ModuleContainer
|
||||
// post() when direct_cb becomes free, wait() for that to happen
|
||||
interface::Semaphore direct_cb_free_sem;
|
||||
|
||||
// Holds the backtraces along the way of a direct callback chain initiated
|
||||
// by this module. Cleared when beginning to execute a direct callback. Read
|
||||
// when event() (and maybe something else) returns an uncatched exception.
|
||||
struct BacktraceStep {
|
||||
ss_ module_name; // From which thread this backtrace is
|
||||
interface::debug::StoredBacktrace bt;
|
||||
};
|
||||
std::list<ModuleContainer::BacktraceStep> direct_cb_exception_backtraces;
|
||||
// NOTE: thread-ref_backtraces() Holds the backtraces along the way of a
|
||||
// direct callback chain initiated by this module. Cleared when beginning to
|
||||
// execute a direct callback. Read when event() (and maybe something else)
|
||||
// returns an uncatched exception.
|
||||
|
||||
ModuleContainer(interface::Server *server = nullptr,
|
||||
interface::ThreadLocalKey *thread_local_key = NULL,
|
||||
@ -200,8 +195,8 @@ struct ModuleContainer
|
||||
cs(info.name));
|
||||
direct_cb = &cb;
|
||||
direct_cb_exception = nullptr;
|
||||
direct_cb_caller_mc = caller_mc;
|
||||
direct_cb_exception_backtraces.clear();
|
||||
thread->set_caller_thread(interface::Thread::get_current_thread());
|
||||
thread->ref_backtraces().clear();
|
||||
event_queue_sem.post();
|
||||
}
|
||||
log_t(MODULE, "execute_direct_cb[%s]: Waiting for execution to finish",
|
||||
@ -215,7 +210,7 @@ struct ModuleContainer
|
||||
// Grab execution result
|
||||
std::exception_ptr eptr = direct_cb_exception;
|
||||
direct_cb_exception = nullptr; // Not to be used anymore
|
||||
direct_cb_caller_mc = nullptr; // Not used anymore
|
||||
thread->set_caller_thread(nullptr);
|
||||
// Set direct_cb to be free again
|
||||
direct_cb_free_sem.post();
|
||||
// Handle execution result
|
||||
@ -323,21 +318,24 @@ void ModuleThread::handle_direct_cb(
|
||||
// then determines the final result of the exception.
|
||||
eptr = std::current_exception();
|
||||
|
||||
// If called from a module
|
||||
if(mc->direct_cb_caller_mc){
|
||||
// Find out the original MC that initiated this direct_cb chain
|
||||
ModuleContainer *orig_mc = mc->direct_cb_caller_mc;
|
||||
while(orig_mc->direct_cb_caller_mc){
|
||||
orig_mc = orig_mc->direct_cb_caller_mc;
|
||||
// If called from another thread
|
||||
interface::Thread *current_thread =
|
||||
interface::Thread::get_current_thread();
|
||||
if(current_thread->get_caller_thread()){
|
||||
// Find out the original thread that initiated this direct_cb chain
|
||||
interface::Thread *orig_thread =
|
||||
current_thread->get_caller_thread();
|
||||
while(orig_thread->get_caller_thread()){
|
||||
orig_thread = orig_thread->get_caller_thread();
|
||||
}
|
||||
|
||||
// Insert exception backtrace to original chain initiator's
|
||||
// backtrace list, IF the list is empty
|
||||
if(orig_mc->direct_cb_exception_backtraces.empty()){
|
||||
ModuleContainer::BacktraceStep bt_step;
|
||||
bt_step.module_name = mc->info.name; // Current module name
|
||||
if(orig_thread->ref_backtraces().empty()){
|
||||
interface::ThreadBacktrace bt_step;
|
||||
bt_step.thread_name = current_thread->get_name();
|
||||
interface::debug::get_exception_backtrace(bt_step.bt);
|
||||
orig_mc->direct_cb_exception_backtraces.push_back(bt_step);
|
||||
orig_thread->ref_backtraces().push_back(bt_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,14 +367,14 @@ void ModuleThread::handle_event(Event &event)
|
||||
"failed: "+e.what());
|
||||
log_w(MODULE, "M[%s]->event() failed: %s",
|
||||
cs(mc->info.name), e.what());
|
||||
if(!mc->direct_cb_exception_backtraces.empty()){
|
||||
if(!mc->thread->ref_backtraces().empty()){
|
||||
ss_ ex_name;
|
||||
for(const ModuleContainer::BacktraceStep &bt_step :
|
||||
mc->direct_cb_exception_backtraces){
|
||||
for(const interface::ThreadBacktrace &bt_step :
|
||||
mc->thread->ref_backtraces()){
|
||||
if(!bt_step.bt.exception_name.empty())
|
||||
ex_name = bt_step.bt.exception_name;
|
||||
interface::debug::log_backtrace(bt_step.bt,
|
||||
"Backtrace in M["+bt_step.module_name+"] for "+
|
||||
"Backtrace in M["+bt_step.thread_name+"] for "+
|
||||
ex_name+"(\""+e.what()+"\")");
|
||||
}
|
||||
} else {
|
||||
@ -1039,30 +1037,33 @@ struct CState: public State, public interface::Server
|
||||
bool ok = mc->execute_direct_cb(cb, eptr, caller_mc);
|
||||
(void)ok; // Unused
|
||||
if(eptr){
|
||||
// If not being called by a module thread, there's nowhere we can
|
||||
// store the backtrace (and it wouldn't make sense anyway as there
|
||||
// is no callback chain)
|
||||
if(caller_mc == nullptr){
|
||||
interface::Thread *current_thread =
|
||||
interface::Thread::get_current_thread();
|
||||
|
||||
// If not being called by a thread, there's nowhere we can store the
|
||||
// backtrace (and it wouldn't make sense anyway as there is no
|
||||
// callback chain)
|
||||
if(current_thread == nullptr){
|
||||
std::rethrow_exception(eptr);
|
||||
}
|
||||
|
||||
// NOTE: In each ModuleContainer there is a pointer to the
|
||||
// ModuleContainer that is currently doing a direct call, or
|
||||
// nullptr if a direct call is not being executed.
|
||||
// NOTE: In each Thread there is a pointer to the Thread that is
|
||||
// currently doing a direct call, or nullptr if a direct call
|
||||
// is not being executed.
|
||||
// NOTE: The parent callers in the chain cannot be deleted while
|
||||
// this function is executing so we can freely access them.
|
||||
|
||||
// Get the original MC that initiated this direct_cb chain
|
||||
ModuleContainer *orig_mc = caller_mc;
|
||||
while(orig_mc->direct_cb_caller_mc){
|
||||
orig_mc = orig_mc->direct_cb_caller_mc;
|
||||
// Find out the original thread that initiated this direct_cb chain
|
||||
interface::Thread *orig_thread = current_thread;
|
||||
while(orig_thread->get_caller_thread()){
|
||||
orig_thread = orig_thread->get_caller_thread();
|
||||
}
|
||||
|
||||
// Insert backtrace to original chain initiator's backtrace list
|
||||
ModuleContainer::BacktraceStep bt_step;
|
||||
bt_step.module_name = caller_mc->info.name; // Caller module name
|
||||
interface::ThreadBacktrace bt_step;
|
||||
bt_step.thread_name = current_thread->get_name();
|
||||
interface::debug::get_current_backtrace(bt_step.bt);
|
||||
orig_mc->direct_cb_exception_backtraces.push_back(bt_step);
|
||||
orig_thread->ref_backtraces().push_back(bt_step);
|
||||
|
||||
// NOTE: When an exception comes uncatched from module->event(), the
|
||||
// direct_cb backtrace stack can be logged after the backtrace
|
||||
|
Loading…
x
Reference in New Issue
Block a user