Linux Debugging(八): core真的那么难以追踪吗?
本周遇到了好几个core都很有典型性。在这里和大家分享下。
相信有过Linux编程经验的人,肯定都遇到过。感觉周围人很多对core有天然的恐惧感,尤其对刚入行不久的同学来说。当然了,也有工作好几年看到core也束手无策的。今天就分析一下,core,其实大部分都是很容易解决的。如果一个core很难以复现,那么说明还是很复杂的,算是Corner case,可能需要很长时间,脑子里要有很好的运行时状态才可以(阅读源码,学习的是逻辑;将源码对应到运行时的状态,分析一些状态机的转换,再去分析可能会发生的情况)。相信前几篇文章会对这种Corner case的分析与解决打下比较好的基础。
相反,那种每次必现,或者复现比率非常高的case,是非常容易解决的。
多线程必然出core?
如果是你新加入的代码引入的core,实际上非常容易解决的,简单的对比一下修改的diff,然后看一下是否有比较低级的错误。如果发现不了,看是否是多线程的问题?单线程如果没有出core,改成多线程就出core,那么就说明多线程竞争某些变量了:同时修改某些变量导致出问题。这个时候你可能第一反应会加锁。我本人非常反感加锁;即使你加锁的粒度很小,作用域也够小,但是只要是加锁,就代表有阻塞,就代表维护起来会很麻烦。这些共享变量真的那么值得加锁吗?可否换成局部变量?如果他是一块动态内存,为了调用某个接口时不要频繁申请释放内存(比如这个接口每秒几千次的调用),那么初始化时候申请一块内存是绝对合理的:请把它设置为线程变量吧。每个线程初始化时候申请这块内存。
当然了你如果实现的是一个框架或者架构调用的接口,这个接口要做到线程安全的。那么看起来你并不能控制这个线程什么时候启动;线程数目会是多少个,那么就没有办法了吗?
实际上,方法有很多,比如,你可以在一个map中维护一个“线程”变量的对应关系
__gnu_cxx::hash_map< pid_t, void *> thread_data_map;
void * thread_buffer;
std::map<pid_t, void *>::iterator it;
lock
it = thread_data_map.find(pid);
if (it == thread_data_map.end()) {
//init "thread data"
thread_data_map[pid] = create_buffer();
} else {
thread_buffer = it->second;
}
unlock
这里不得不使用了一个锁。实际上由于线程数是有限的,因此这个效率还好。我本周实现了一个qps可以达到2000+的在线应用,基本上锁的代价在整个的call stack中可以忽略不计。
当然了,比较好的框架可能会提供OnThreadInit这种接口,那么在这边申请线程变量吧:
int pthread_setspecific (pthread_key_t key, const void *value);
在实现逻辑的函数获取该变量即可:
void *pthread_getspecific (pthread_key_t key);
什么时候要使用线程变量?看多线程下是否对该变量有写操作,如果有就要申请线程变量(或者加锁),否则必然出core。
不要给自己埋下一个坑:
今天一个同学的core看起来是做了一个“优化”,节省了申请变量的时间。
void init() {
    my_struct * some_var;
    ...
    some_var->res = new some_res;
    some_var->res->set_value1(some_common_value1);
    some_var->res->set_value2(some_common_value2);
}
void * thread_func(my_struct * some_var) {
    some_var->res->set_value3(value_3);
   ...
}
set_value3就是一个出core的原因。这个也是一个典型的多线程必然出core的case。实际上,res没必要提前申请吧。把它改成一个局部变量,性能几乎没有的损耗。当然了,如果这个资源很大,那么就当成线程变量吧。
那么如何分析core?
实际上,上述的场景出现的core如果仅从core本身,可能一时不好排查问题出在哪里;而且core的位置可能还不一样,不可避免的出现替罪羊;比如调用第三方的模块,实际上是自己的全局变量导致到了第三方的调用栈时出core了。因此一定要排查自己的处理是否正确。
确定调用的接口是否线程安全;如果是你的同事,你得确定他说的是对的。就像今天又另外的一个core,调用者坚称他写的是线程安全的,仿佛不是线程安全的就像是代码写的不好似得;最后排查出他写的代码不是线程安全时,他问你什么是线程安全的。
那么回答一下,如何分析core?
首先了解清楚core出现的应用场景,比如长句子处理会有core;多线程处理会有core;特殊字符会有core;这个QA会给你一个比较清楚的说明。然后通过core的call stack去大概确定大概的源码位置。
源码面前,了无秘密。
你读源码,它展现的是逻辑;但是你脑中,要有个runtime的运行时调用栈,多线程的调度;天马行空。
当然了,还是解决不了,开始从core得到更详细的信息吧!看看调用栈的参数是什么,切换一个线程看看其他的几个线程的frame是什么。
还解决不了?
那么它是Corner case,只要把应用在core后立即启动就行了。记得EMC有个bug的选项是unable to root cause, 形容的很贴切。同时不要忘记自我安慰:码的码多了,肯定有bug,谁能保证服务100%呢?
Linux Debugging(八): core真的那么难以追踪吗?的更多相关文章
- Linux Debugging(五): coredump 分析入门
		作为工作几年的老程序猿,肯定会遇到coredump,log severity设置的比较高,导致可用的log无法分析问题所在. 更悲剧的是,这个问题不好复现!所以现在你手头唯一的线索就是这个程序的尸体: ... 
- 在Linux上利用core dump和GDB调试
		段错误(segfault) "段错误"是程序试图操作不允许访问或试图访问的不允许内存的情况.可能导致段错误的原因主要有: 1.试图解引用空指针(你不允许访问内存地址0) 2.试图解 ... 
- 在Linux上利用core dump和GDB调试segfault
		时常会遇到段错误(segfault),调试非常费劲,除了单元测试和基本测试外,有些时候是在在线环境下,没有基本开发和测试工具,这就需要调试的技能.以前介绍过使用strace进行系统调试和追踪<l ... 
- debian(kali Linux) 安装net Core
		debian(kali Linux) 安装net Core curl -sSL https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-previ ... 
- linux下生成core dump文件方法及设置
		linux下生成core dump文件方法及设置 from:http://www.cppblog.com/kongque/archive/2011/03/07/141262.html core ... 
- 什么是core dump   linux下用core和gdb查询出现"段错误"的地方
		什么是core dump linux下用core和gdb查询出现"段错误"的地方 http://blog.chinaunix.net/uid-26833883-id-31932 ... 
- linux下用core和gdb查询出现"段错误"的地方【转】
		转自:http://blog.chinaunix.net/uid-30091091-id-5754288.html 原文地址:linux下用core和gdb查询出现"段错误"的地方 ... 
- 发布项目到 Linux 上运行 Core 项目
		发布项目到 Linux 上运行 Core 项目 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win ... 
- Visual Studio 2017 通过SSH 调试Linux 上.NET Core
		Visual Studio 2017 通过SSH 调试Linux 上.NET Core 应用程序. 本文环境 开发环境:Win10 x64 Visual Studio 2017 部署环境:Ubuntu ... 
随机推荐
- 设置元素text-overflow: ellipsis后引起的文本对齐问题
			.ellipsis { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } 给元素设置了这个属性之后,该行内元素和旁边的 ... 
- jmeter录制APP时不能登录的问题
			问题描述: 录制APP时,其他一切挺顺利的,但在登录的时候提示"服务器发生未知错误,请稍后重试".一开始以为是接口问题,用python脚本调用了下,是可以登录的,排除接口问题.百度 ... 
- python的模块与包的导入
			类似于C语言的包含头文件去引用其他文件的函数,python也有类似的机制,常用的引入方法有以下 import 模块名 #模块名就是py文件名 #使用这种方法以后调用函数的时候要使用模块名.函数名()这 ... 
- 13.QT-QMainWindow组件使用
			QMainWindow介绍 主窗口是与用户进行长时间交互的顶层窗口,比如记事本 主窗口通常是应用程序启动后显示的第一个窗口 QMainWindow是Qt中主窗口的基类,继承于QWidget,如下图所示 ... 
- 不能执行已经释放掉的Script代码!(已解决)
			发生原因: 当页面关闭时 会释放掉该页面的JS 解决方法: 在 js onload方法中执行容错判断, 如果该JS文件中任意对象为空的话,则动态加载该JS文件. window.onload = fun ... 
- Rabbitmq集群
			分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 Rabbitmq集群高 ... 
- FJUT寒假作业第三周数蚂蚁(记录第一道并查集)
			http://210.34.193.66:8080/vj/Contest.jsp?cid=162#P7 思路:用并查集合并集合,最后遍历,找到集合的根的个数. 并查集是森林,森林中的每一颗树是一个集合 ... 
- JQuery写的一个常见的banner
			大致的布局如下: <div class="banner" > <div class="pic"> ... 
- 关于基因组注释文件GTF的解释
			GTF文件的全称是gene transfer format,主要是对染色体上的基因进行标注.怎么理解呢,其实所谓的基因名,基因座等,都只是后来人们给一段DNA序列起的名字而已,还原到细胞中就是细胞核里 ... 
- Unity3D各平台Application.xxxPath的路径
			前几天我们游戏在一个同事的Android手机上启动时无法正常进入,经查发现Application.temporaryCachePath和Application.persistentDataPath返回 ... 
