GDB多线程调试分析
0x00: 在Linux系统上Gdb提供了一组多线程调试命令,如表所示:
多线程调试的主要任务是准确及时地捕捉被调试程序线程状态的变化的事件,并且GDB针对根据捕捉到的事件做出相应的操作,其实最终的结果就是维护一根叫thread list的链表。上面的调试命令都是基于thread list链表来实现的,后面会有讲到。
0x01:Gdb在linux平台多线程调试实现主要依赖下面三个文件
thread.c:文件它的任务非常简单,就是多线程调试命令子集的实现,比如info threads。
当用户在gdb命令行敲入多线程调试命令子集中的命令时,就会调用thread.c中对应的函数。Thread.c中的实现都是基于thread list的。而gdb对于thread list的维护工作主要在另外两个文件中实现。
Linux-nat.c:它实现了通常的调试功能,比如读写寄存器、读写内存、resume程序、wait程序、attach、detach等功能。更重要的是,在linux-nat.c中会维护一个lwp_list链表,表示当前进程所有的内核线程。
linux-thread-db.c:基于thread_db库的一组功能函数,用在多线程调试环境下的函数,比如to_get_thread_local_address。这些函数的任务就是获取用户态线程的产生、消亡事件,双及获取用户态线程相关的数据。Linux-thread-db.c获取用户线程的发生的事件和获取的信息、结合linux-nat.c中维护的lwp_list内核线程链表中提供的信息,以此维护一个完整的thread_list,该链表存放线程所有的信息。
0x02:Gdb功能函数分层:
Gdb中的功能函数使用struct target_ops数据结构来组织。不同功能的函数集抽象成不同的target_ops。比如用于处理coredump文件的”core” target_ops,而linux-nat.c中实现的linux应用程序本地调试功能也抽象成一个ops”child” target_ops,linux-thread-db.c中实现的基于libpthread库的调试功能抽象成”multi-thread”target_ops。
整个linux多线程应用程序本地调试的结构框架如下:
从上图可以看到当调试linux多线程程序时,就会使用thread_db_ops中的相应的函数。那么问题来了,对于resume和wait这些Linux_ops中也实现的函数,会调用哪个呢?Gdb中实现了很多的target_ops,有功能相近也有完全不同功能的,比如linux_ops和file_ops。那么对于功能相近的target_ops怎样使用呢?功能不同的target_ops之间又有怎样的关系呢?这些问题gdb分层机制能解释。
Gdb中把target_ops分为了7层,每一层负责不同的功能。如图所示:
0x03:GDB调试多线程
调试进程建立具体的流程下图所示:
在创建好被调试进程之后,gdb通过ptrace(PTRACE_SETOPTIONS)设置PTRACE_O_TRACECLONE,设置过后,当被调试进程创建线程的时候,就会给自己发送一个SIGTRAP信号,让被调试进程进入stop状态,使得gdb能够捕捉到这些事件,获取tid添加到lwp_list中后,gdb会让程序继续运行,直到被调试程序发生一些需要通知gdb用户的事件,比如触发了用户设置的断点,下面是流程图
Lwp_list链表
被调试进程创建线程最终是通过clone()系统调用实现的。要捕捉子线程的创建和死亡事件,这个捕捉事件由ptrace提供的机制实现。具体机制如下图所示。
Thread_list链表:
Thread_list是struct thread_info类型的一个链表,记录的是被调试进程的所有线程的信息,里面包含线程用户态和内核态的一些信息。线程用户态信息的捕获基于libthread_db库,该库提供了一组调试接口。这么一组libpthread_db调试接口在gdb中使用struct thread_db_info进行管理,该数据主要结构的具体信息如下表:
在被调试进程加载libpthread库时,会为该进程创建这么一个struct thread_db_info记录该进程要使用到的libthread_db提供的调试接口。其中比较重要的是:
td_create_bp_addr和td_death_bp_addr。这两个地址是对应libpthread库中的某个位置。当调用libpthread库创建线程或者线程死亡时,一定会分别调用这么两个addr处的代码。Gdb通过在这两个位置设置断点来捕获libpthread库的线程创建和死亡事件,断点的类型为bp_thread_event.
被调试程序创建子进程或者子进程死亡,会执行到libpthread库的td_create_bp_addr或td_death_bp_addr地址处,触发断点。线程进入stop状态
gdb 通过waitpid()监测到被调试进程的状态改变,分析子进程发生的事件,判断为bp_thread_event的断点触发。如果是create,获取新创建线程struct thread_info的相关的信息,并且加入到thread_list中;如果是death,从thread_list中删除该线程。
0x04:总结
GDB确定我们调试的程序是否为多线程, 通过判断被调试程序是否加载libpthread库来判断的。(捕捉装载libpthread库这个事件)也就是一旦被调试程序装在libpthread库,( Observer观察者模式)我们就应做初始化多线程调试功能。
GDB多线程调试分析的更多相关文章
- gdb 多线程调试
gdb 多线程调试 http://hi.baidu.com/hcq11/blog/item/9f5bfc6e696209d680cb4a25.html http://hi.baidu.com/lit ...
- GDB 多线程调试:只停止断点的线程,其他线程任然执行; 或只运行某些线程 其他线程中断
多线程调试之痛 调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程 ...
- [skill][gdb] gdb 多线程调试
中文快速入门: http://coolshell.cn/articles/3643.html (关于多线程的部署说的并不太对) 进阶: 多进程相关概念: inferiors 是什么? http://m ...
- GDB多线程调试
一.多线程调试1. 多线程调试,最重要的几个命令:info threads 查看当前进程的线程. ...
- gdbserver 移植与多线程调试
在嵌入式linux平台使用gdb调试进行远程调试需要安装gdbserver,gdbserver工作在目标板上,通过串口或者网线与主机上的gdb互联实现远程调试. Gdbserver需要根据不同的嵌入式 ...
- Linux学习——Gdb基本调试方法&&多线程调试
1.Gdb的基本调试 示例代码 //e.c #include <stdio.h> void debug(char *str) { printf("debug info :%s\n ...
- 通过gdb调试分析Linux内核的启动过程
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验流程 1.打开环境 执 ...
- 利用GDB进行多线程调试
一.多线程调试 多线程调试重要就是下面几个命令: info thread 查看当前进程的线程. thread <ID> 切换调试的线程为指定ID的线程. break file.c:100 ...
- gdb的多线程调试
info threads 可以查看当前进程有哪些线程 thread ID 可以切换到线程ID bt 查看当前线程堆栈 set scheduler-locking on多线程调试过程中, 线程会来回切换 ...
随机推荐
- P4841 城市规划 FFT+生成函数
\(\color{#0066ff}{ 题目描述 }\) 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使 ...
- P3379 【模板】最近公共祖先(LCA)(LCT)
\(\color{#0066ff}{ 题目描述 }\) 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. \(\color{#0066ff}{输入格式}\) 第一行包含三个正整数N.M. ...
- 最短路【洛谷P1606】 [USACO07FEB]荷叶塘Lilypad Pond
P1606 [USACO07FEB]荷叶塘Lilypad Pond 为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘.这个长方形的池子被分成了M行N列个方格(1≤M,N≤30).一些格子是坚固得令 ...
- 使用box-shadow 实现水波、音波的效果
用到的工具 animation box-shadow html: <div class="watersource"> </div> css: .waters ...
- J.U.C AQS(abstractqueuedssynchronizer--同步器)
J.U.C AQS(abstractqueuedssynchronizer--同步器) 同步器是用来构建锁和其他同步组件的基础框架,它的实现主要依赖一个int成员变量来表示同步状态以及通过一个FI ...
- kuangbin专题十六 KMP&&扩展KMP HDU3613 Best Reward(前缀和+manacher or ekmp)
After an uphill battle, General Li won a great victory. Now the head of state decide to reward him w ...
- 07. 如何实现移动端rem适配
如何实现移动端rem适配 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- STM32F030 定时器多次溢出才触发中断的问题
前言 最近在调试项目过程中,用了 STM32F030 的定时器 16 作为系统时钟,1ms 触发一次中断,过程中遇到一些值得记录的问题. 记录 STM32F030 中定时器 16 的初始化配置如下 v ...
- java重载equals和hashCode
class Employee { private int salary; private java.util.Date hireDay; private String name; public int ...
- django中关于静态文件的引入(这边是指边主要是jquery和bootstrap
一. 创建文件夹 首先在项目的根目录中新建一个文件夹,这个文件夹的名称最好以static命名 二. 修改配置 在项目的settings文件中,拉倒最下面,可以看到 STATICFILES_DIR ...