GDB扩展之Command File - 提高调试效率

工欲善其事,必先利其器。GDB的扩展可以通过command file和python脚本完成,这里针对Command file,一个简单高效的扩展方案。


一.简介

GDB Command File可以简单地理解为一串自定义的GDB指令,GDB同时允许用户使用define将一串GDB操作定义为一个指令。比如在命令行模式下将断点保存起来,下次执行时再加载进来,就可以通过两个自定义命令来完成:

define bsave

    shell rm -f brestore.txt

    set logging file brestore.txt

    set logging on

    info break

    set logging off

    # reformat on-the-fly to a valid gdb command file

    shell perl -n -e 'print "break $1\n" if /^\d+.+?(\S+)$/g' brestore.txt >brestore.gdb

end


define brestore

  source brestore.gdb

end


在使用时就可以使用initGDB来代替执行其中的一组指令了。bsave可以把现有的断点以设定断点的指令存放到brestore.gdb中,加载时相当于直接执行brestore.gdb中的断点指令完成断点设定。 其中每一行都是有效的GDB指令或者注释。


如果这些指令定义在一个command文件中并由GDB在执行时自动加载,就可以简化操作。


二.GDB Command File的加载与使用

GDB Command File是一个纯文本文件,包含一串GDB指令,也包括define定义的自定义指令。在加载时使用GDB指令:

   source [-s] [-v] filename

  

   -s 表示在系统的PATH中搜索指定的文件,找到后加载。

   -v 表示打开verbose模式,会显示每条指令的执行。

  *-s和-v并不是所有的gdb都能支持。


在使用时,可以在GDB的命令行里手动执行,也可以在gdb的启动文件里(.gdbinit)定义。如果使用Eclipse调试时,还可以在Debugger里的Command File中指定:

   


2.1 为新指令添加帮助


使用document可以方便地为已通过define定义的指令添加注释,注释的内容也是以end结束。

比如:

document showKURL

  显示KRURL的内容,直接传入KRUL变量即可。

  *Horky

end


在gdb里分别使用show显示函数说明如下:

(gdb) help user-defined

showkurl -- 显示KRURL的内容

(gdb) help showkurl

显示KRURL的内容,直接传入KRUL变量即可。

*Horky

(gdb)



三.自定义指令的撰写


一个自定义指令可以理解为一个函数,基本结构如下:

define command_name

  #GDB指令或注释

end


几个要点:

  1. 函数的参数可以使用$arg0,$arg1,..来取得。

  2. 变量使用GDB指令set赋值,如set $var=2

  3. 返回值  因为各个自定义指令中的变量是共享的,就可以直接前面函数中定义的变量。

  4. 注释使用#打头即可。

  5. 一个代码块都是以end结束的。

  6. 指令间可以相互调用。

  


3.1 流程控制指令

在自定义指令中可以使用如下的控制语句,做一些比较复杂的操作。

1. 条件判断语句if

if {expression} 

else

end

  条件判断语句, if 后面所带的表达式为一般的GDB表达式, 可以使用==,&&,||之类的逻辑操作符。如:

   if $a==5

     #do something

   else 

     #do something else

   end


 2.循环语句while

   while {expression}

   end

 对应于C++循环中的break和continue, 可以使用loop_break和loop_continue指令。



3.2 补充指令

1. 变量赋值

  对于一变量直接赋值即可。字串变量可以使用下面的指令:

     set $string="value is %d",5

  详见参考1. 


2. 输出内容

程序中的输出可以使用以下几个指令:

echo

  echo用于输出字串, 不会自换行, 字串的引号会同样输出,适用输出一些提示信息。比如:

  echo "ouput is:"

  echo output\n


  输出的结果会是: "output is:"output

  

output {expression}

  用于输出表达式的结果。与print的区别在于,它的输出结果不会生成新的变量,且不会自动换行。比如:

  (gdb) output index

    2 (gdb)

  而print则会是下面的效果:

  (gdb) print index

   $1=2

  (gdb)


printf

  输出格式化字串. 比如printf "Value will be 0x%x",value


最后一个是最常用的print了, 前面已经分析过它与output的区别了,print还有一个好处,可以在后面的语句使用$取出上次print的结果。这是因为GDB定义了一组内部变量:

 $ : 取出上次的值

 $_ : 取出上次x指令最后输出的位置地址


   详见参考2.


3. 内存输出

内存输出操作x, 功能强大。基本格式为

   x/FMT address


FMT的详细格式为:nfu

  n : 是输出的个数

  f : 以什么格式输出, 比如 s:字串,i:机器指令,  x:16进制

  u : 输出数据的单位长度,比如b:单字节,h:双字节,  w: Words,四字节,g: Gian words, 八字节


 比如,下面示例中会用的,x/s 是输出ASCII字串,而x/hs则是输出UTF-16字串。

 

其它不在这里赘述。详见参考6.



四. 应用

4.1. 打印出JSDOMWindowShell关系列表

这个简单,就是基于一个值,打出它的上下依赖关系

#Print Hierarchy of JSDOMWindowShell

define showDOMWindowShell

  p $arg0

  p $arg0->world()

  p $arg0->window()

  p $arg0->window()->impl()

  p $arg0->window()->shell()

  p $arg0->impl()

  p $arg0->impl()->frame()

  p $arg0->impl()->document()

end


4.2. 打印出WTF::String的内容

在调用WebKit代码时,字串内容没办法直接显示出来。可以使用下面的指令完成:

define showStringVar

  showStringContent $arg0

 

  echo String:\n 

  if $flag&(1<<6)||$flag==0

       echo [ASCII]:

       x/s *(int *)((char *)($arg0.m_impl.m_ptr)+8)

  end

 

  echo [Unicode]:

  x/hs *(int *)((char *)($arg0.m_impl.m_ptr)+8)

 

   if $flag&(1<<2)

       print "*This is a identifier."

   end

    

   if $flag&(1<<1)

        print "*Masked Buffer Ownership."

   end

end


一开始先调用了一个指令showStringContent,内容如下:

#Paramter: String

define showStringContent

  echo Reference Count:

  p *(int *)($arg0.m_impl.m_ptr)

 

  echo Length:

  p *(int *)((char *)($arg0.m_impl.m_ptr)+4)

 

  echo Flag:

  set $flag = *(int *)((char *)($arg0.m_impl.m_ptr)+16)

  p/x $flag

end


转载请注明出处:http://blog.youkuaiyun.com/horkychen


参考:

  1. Convenience Variables

  2. Expressions

  3. Commands for Controlled Output

  4. [WebKit]C++类的数据结构及在反汇编上的应用

  5. 使用LLDB脚本简化打印复杂数据的操作

  6. Examine Memory

  7. User defined commands

   8. 转换GDB堆栈到流程图

<think>嗯,用户想了解如何使用GDB调试线程。首先,我需要回忆一下自己之前学过的GDB知识,特别是关于线程调试的部分。可能得从基本概念开始,比如GDB是什么,线程调试的常见场景,然后再进入具体的命令和步骤。 首先,GDB是GNU调试器,用于调试C/C++等程序。线程调试的话,用户可能遇到多线程问题,比如竞争条件、死锁或者线程间通信的问题。这时候需要知道如何在GDB中管理线程,查看线程状态,设置断点,切换线程,以及查看堆栈信息等。 接下来,我应该列出常用的GDB线程命令。比如info threads显示当前所有线程的信息,thread <id>切换线程,break <位置> thread <id>在特定线程设置断点,还有thread apply对所有线程执行命令。还有像set scheduler-locking选项来控制调试时线程的执行,这对单步调试时避免其他线程干扰很重要。 然后,调试步骤可能需要分步骤说明。比如启动程序,查看线程列表,切换线程,设置线程专属断点,控制线程执行,检查变量和堆栈。这部分需要结构清晰,让用户能一步步跟着操作。 还要注意一些注意事项,比如线程ID是GDB内部分配的,可能和程序中的不同;死锁检测需要检查线程堆栈和锁状态;竞争条件可能需要结合断点和观察点。此外,可能提到一些高级技巧,比如结合TUI模式或使用Python脚本自动化调试。 需要确保使用中文,术语正确,比如“断点”、“堆栈”、“互斥锁”等。同时,按照用户的要求,行内公式用$...$,独立公式用$$...$$,不过这个问题可能不涉及数学公式,所以可能不需要。但需要检查是否在举例时涉及到了数学表达式,如果有的话要正确格式化。 最后,确保回答真实可靠,结构清晰,分步骤讲解,可能还需要举例说明某些命令的使用场景,比如如何用thread apply all bt查看所有线程的堆栈,帮助用户定位死锁问题。 总之,整理思路应该是先介绍GDB线程调试的重要性,然后列出常用命令,再分步骤说明调试流程,最后给出注意事项和技巧,确保用户能逐步理解和应用。</think>GDB调试线程全流程指南(附实用技巧) GDB作为强大的调试工具,在多线程调试方面提供了完整的支持体系。以下从5个关键维度详细解析线程调试技术: 一、核心调试指令集 1. 线程信息查看 $$info\ threads$$ ▸ 输出示例:带*号的为当前活动线程 ▸ 字段说明:线程ID(GDB内部编号)、系统级TID、函数调用栈 2. 线程切换控制 $$thread\ <thread\_id>$$ ▸ 支持tab补全线程编号 ▸ 切换后断点上下文自动更新 3. 线程敏感断点 $$break\ <location>\ thread\ <thread\_id>$$ ▸ 示例:在3号线程设置main函数断点 $$break\ main\ thread\ 3$$ 4. 批量线程操作 $$thread\ apply\ all\ <command>$$ ▸ 典型应用:打印全线程堆栈 $$thread\ apply\ all\ bt$$ 二、执行控制进阶技巧 1. 调度锁定模式 $$set\ scheduler-locking\ [on|off|step]$$ ▸ step模式:单步执行时冻结其他线程 ▸ 适用场景:精确调试竞态条件 2. 非停止模式 $$set\ target-async\ on$$ ▸ 允许后台线程持续运行 ▸ 配合`interrupt`命令实现异步调试 三、典型调试场景实战 1. 死锁检测流程 (1) 捕获程序挂起状态 (2) 执行`thread apply all bt`获取全量堆栈 (3) 交叉分析各线程锁持有情况 (4) 使用`p mutex_var`检查锁状态 2. 竞态条件调试 (1) 在共享资源访问点设置条件断点 $$break\ file.c:42\ if\ shared_var > 10$$ (2) 启用硬件观察点 $$watch\ shared_var$$ (3) 结合反向调试技术 $$record\ full$$ 四、性能调优辅助功能 1. 线程执行统计 $$info\ threads\ --all-statistics$$ ▸ 显示各线程CPU时间占比 ▸ 识别资源消耗热点 2. 实时状态监控 $$set\ pagination\ off$$ ▸ 配合脚本实现持续状态输出 ▸ 示例监控脚本: ``` while true do info threads sleep 1 done ``` 五、高级调试技巧 1. Python扩展应用 ▸ 自定义线程状态可视化 ▸ 自动化锁依赖分析 ▸ 示例脚本框架: ```python import gdb class ThreadAnalyzer(gdb.Command): def __init__(self): super().__init__("thread-analyze", gdb.COMMAND_USER) def invoke(self, arg, from_tty): # 自定义分析逻辑 ``` 2. 核心转储分析 ▸ 加载核心文件 $$gdb\ -c\ core.file\ ./program$$ ▸ 事后分析线程状态 ▸ 结合`info registers`检查寄存器值 注意事项: 1. 线程本地存储(TLS)访问需指定上下文 $$p\ var\@tls(<thread_id>)$$ 2. 实时调试时优先使用`-p`附加模式 $$gdb\ -p\ $(pidof\ program)$$ 3. 建议编译时添加调试增强选项: $$gcc\ -g\ -Og\ -fno-omit-frame-pointer$$ 通过掌握这些技术要点,开发者可以有效应对多线程环境下的复杂调试场景。建议结合`gdb-dashboard`等可视化工具提升调试效率
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值