思考了一下,决定单独记录一下这个话题。
因为这里我花费了许多时间来知道,原来线程名是有长度限制的。
也是为了以后来完善。
线程命名的重要性
作为系统工程师,我们的任务,不仅是调优,实际上更多时修是在面对系统性的bug,责任的界定是吃力不讨好的事。
所以,我们往往不得不采取守势,也是我给起的名字:划格子。
这个容易理解,程序员,大多喜欢格子衫。。。
当然我的意思是,划清边界。
线程是很重要一种边界。所以,给线程起名,是第一件我们要做的事。
一方面,当然要指出哪些没有命名,另一方面,最好制定Policy,要求所有的主动对象,必须继承自我们自己的线程框架的类。
如果这样的情况,线程无名,则你可以直接拒绝启动。
线程名的合法性检查
除了空名称,线程名,不能超过15个字符。
那么,这里的成员函数,原来写的就不是很严谨:
void Thread::setName(const std::string& name_in)
{
name = name_in;
}
void Thread::start()
{
pthread_setname_np(thread.native_handle(), name.c_str());
}
正确的应当如下。
void Thread::setName(const std::string& name_in)
{
//If thread name>15 characters, will be truncated
if (name_in.length() > 15) {
name = name_in.substr(0, 15);
} else {
name = name_in;
}
}
void Thread::start()
{
int rc = pthread_setname_np(thread.native_handle(), name.c_str());
if (0!=rc) {
std::cerr << "ERROR: Set thread name failed for thread " << name << ": " << std::strerror(errno) << std::endl;
}
}
错误捕获中,得到线程名
pthread_getname_np
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc)
{
printf("Breakpad: Receive signo: %u, pid: %d, tid: %ld, sender pid: %d, sender uid: %d, fault addr: %p\n",
info->si_signo, getpid(), syscall(SYS_gettid), info->si_pid, info->si_uid, info->si_addr);
if (info->si_signo == SIGINT) {
printf("Breakpad: Do not deal user int!\n"); return;
}
char thread_name[16];
int ret = pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name));
if (ret == 0) {
printf("[SignalHandler]Current thread name: %s\n", thread_name);
} else {
printf("[SignalHandler]Failed to get thread name, error: %d\n", ret);
}
}
得到线程pid
注意前述代码中,得到线程pid的位置:
syscall(SYS_gettid)
而不是使用C++的threadid:
std::thread thread;
thread = std::move(std::thread(f));
std::thread::id threadid = thread.get_id();
综合
代码这样修改后,在 htop中,能看到所有的线程的名称和pid,如果这里再结合perf或lttng,就可以将这些信息关联起来。
如果某个线程崩溃,在错误捕获中,一般能得到该线程的 pid,当然可以将这个pid也写入到日志中。
当然,这不是万能的,OS的错误捕获,一般的确是在出错的线程,但如果该线程处理Z或T或其它特殊的异常状态,OS会接管,然后复制出一个一模一样的新的线程栈,这里虽然我们得到的还是该线程的名字,调用栈也类似,但并不是真正的出错的第一现场。这里要稍加注意。
但一般的情况,还是可以基本达到目标的。
例如,这时,可以在程序,进入所谓的巡航状态后,
先用
ps -eo pid,tid,pri,rtprio,comm -L
将当时的所有的线程的信息存盘。
在崩溃时,就能知道,是哪个线程出的错。如果这个线程ID是凭空出现的,并且与之有重名的,那么这个可能就是OS干预,导致的转世重生的那个线程。
当然,这时,也能得到一些信息。