调试多线程程序

在某些操作系统(例如GNU / Linux和Solaris)中,单个程序可能具有多个执行线程。线程的精确语义因一个操作系统而异,但通常,一个程序的线程类似于多个进程,只是它们共享一个地址空间(也就是说,它们都可以检查和修改相同的变量)。另一方面,每个线程都有自己的寄存器和执行堆栈,也许还有私有内存。

GDB提供了以下调试多线程程序的工具:

  • 自动通知新线程
  • '线程thread-id',在线程之间切换的命令
  • '信息线程',用于查询现有线程的命令
  • '线程适用[ thread-id-list | 全部] args',将命令应用于线程列表的命令
  • 线程特定的断点
  • '设置打印线程事件',它控制线程启动和退出时消息的打印。
  • '设置libthread-db-search-path 路径',libthread_db如果默认选项与程序不兼容,用户可以指定要使用的选项。

在GDB线程调试工具使你可以观察到所有线程,而你的程序运行,但每当GDB需要控制,尤其是一个线程是调试的焦点。该线程称为当前线程。调试命令从当前线程的角度显示程序信息。

每当GDB在您的程序中检测到新线程时,它都会以“[新系统标签 ]',其中systag是线程标识符,其形式取决于特定系统。例如,在 GNU / Linux上,您可能会看到

[新线程0x41e02940(LWP 25582)]

当GDB注意到一个新线程时。相反,在其他系统上,systag只是类似“过程368',没有其他预选赛。

出于调试目的,GDB将其自己的线程号(总是一个整数)与下级的每个线程相关联。此数字在下级的所有线程之间是唯一的,但在不同下级的线程之间不是唯一的。

您可以使用合格的劣质编号引用劣质中的给定线程 。线程NUM语法,也称为 合格线程ID,与劣-NUM是劣质数目和线程NUM是给定的低劣线程编号。例如,线程2.3是指劣等2的线程号3。如果省略了劣等数(例如thread 3),则GDB会推断您是指当前劣等的线程。

在创建第二个下级编号之前,即使您始终可以使用完整的下级编号,GDB不会显示线程ID 的 下级编号部分。thread-num形式引用下级1(初始下级)的线程。

有些命令接受以空格分隔的线程ID列表作为参数。列表元素可以是:

  1. 线程ID,如“信息线程显示,带有或不带有劣等的预选赛。例如,2.1' 要么 '1个'。
  2. 一系列的线程号,同样带有或不带有劣等的限定符,如inf所示。thr1 - thr2或 thr1 - thr2。例如,1.2-4' 要么 '2-4'。
  3. 下等的所有线程,用星号通配符指定,带有或不带有下限定词,如inf所示。*(例如,“1. *')或*。前者是指给定下级的所有线程,而后者没有下等限定符的形式是指当前下级的所有线程。

例如,如果当前下级为1,且下级7有一个ID为7.1的线程,则线程列表'1 2-3 4.5 6.7-9 7. *'包括劣等1的线程1至3,劣等4的线程5,劣等6的线程7至9以及劣等7的所有线程。也就是说,以扩展限定形式与'1.1 1.2 1.3 4.5 6.7 6.8 6.9 7.1'。

除了每个低级编号之外,每个线程还分配有唯一的全局编号,也称为全局线程ID,即单个整数。与线程ID的线程号组件不同,即使调试多个下级,也不会有两个线程具有相同的全局ID。

从GDB的角度来看,一个进程始终至少具有一个线程。换句话说,即使程序不是多线程的,GDB也会将线程号分配给程序的“主线程”。

调试器便利变量'$ _thread'和'$ _gthread分别包含当前线程的每个劣等线程号和全局线程号。您可能会发现这对于编写断点条件表达式,命令脚本等很有用。有关便利性变量的一般信息,请参见便利性变量。

如果GDB检测到程序是多线程的,它会用击中该断点的线程的ID和名称来增加有关在断点处停止的常规消息。

线程2“客户端”在client.c:68处击中断点1,send_message()

同样,当程序收到信号时:

线程1“主”接收到信号SIGINT,中断。

info threads [thread-id-list]

显示有关一个或多个线程的信息。不带参数时,显示有关所有线程的信息。您可以使用线程ID列表语法指定要显示的线程列表(请参阅线程ID列表)。

GDB为每个线程显示(按此顺序):

  1. GDB分配的每个劣等线程号
  2. GDB分配的全局线程号,如果'-gid'选项已指定
  3. 目标系统的线程标识符(systag)
  4. 线程的名称(如果已知)。线程可以由用户命名(请参见thread name下面的),或者在某些情况下可以由程序本身命名。
  5. 该线程的当前堆栈框架摘要

星号'*GDB线程号左侧的' 表示当前线程。

例如,

(gdb)信息线程
  ID目标ID框架
* 1个进程35个线程13主线程(argc = 1,argv = 0x7ffffff8)
  sigpause()中有2个进程35线程23 0x34e5
  sigpause()中的3个进程35线程27 0x34e5
    在threadtest.c:68

如果要调试多个下级,则GDB使用合格的下级num显示线程ID 。线程数格式。否则,仅显示线程号。

如果您指定“-gid'选项,GDB将显示一列,指示每个线程的全局线程ID:

(gdb)信息线程
  Id GId目标Id框架
  1.1 1进程35线程13主线程(argc = 1,argv = 0x7ffffff8)
  sigpause中的1.2 3进程35线程23 0x34e5()
  1.3 4进程35线程27 0x34e5在sigpause()中
* 2.1 2进程65线程1主线程(argc = 1,argv = 0x7ffffff8)

在Solaris上,可以使用特定于Solaris的命令显示有关用户线程的更多信息:

maint info sol-threads

显示有关Solaris用户线程的信息。

thread thread-id

将线程ID thread-id设置为当前线程。命令参数thread-id是GDB线程ID,如“信息线程'显示,带有或不带有劣等的限定词(例如,'2.1' 要么 '1个')。

GDB通过显示所选线程的系统标识符及其当前堆栈框架摘要作为响应:

(gdb)线程2
[切换到线程2(线程0xb7fdab70(LWP 12747))
#0 some_function(ignore = 0x0)在example.c:8
8 printf(“ hello \ n”);

[切换到线程2(线程0xb7fdab70(LWP 12747))
#0 some_function(ignore = 0x0)在example.c:8
8 printf(“ hello \ n”);

与“[新…]'消息,'之后的文本形式切换到'取决于系统用于识别线程的约定。

thread apply [thread-id-list | all [-ascending]] [flag]… command

thread apply命令允许您将命名 命令应用于一个或多个线程。使用线程ID列表语法(请参阅线程ID列表)指定要影响的线程,或指定all要应用于所有线程。要按降序对所有线程应用命令,请键入。要将命令按升序应用于所有线程,请键入。 thread apply all commandthread apply all -ascending command

该标志参数控制在申请时有什么输出端产生以及如何处理错误提出命令的线程。 标志 必须以开头,-后跟一个字母 qcs。如果提供了几个标志,则必须分别给它们-c -q

默认情况下,GDB在命令产生的输出之前显示一些线程信息,并且在执行命令期间引发的错误将中止thread apply。以下标志可用于微调此行为:

-c

标志-c,它表示“继续',导致显示命令中的任何错误,thread apply然后继续执行 。

-s

标志-s,它表示“无声',导致命令产生的任何错误或空输出都将被静默忽略。即,继续执行,但是不打印线程信息和错误。

-q

标志-q('安静')禁用打印线程信息。

标志-c-s不能一起使用。

taas [option]… command

的快捷方式。在所有线程上应用命令,忽略错误并清空输出。 thread apply all -s [option]… command

taas命令接受与该命令相同的选项thread apply all。参见线程全部应用。

tfaas [option]… command

的快捷方式。在所有线程的所有帧上应用命令,忽略错误和空输出。请注意,该标志被指定了两次:第一个确保仅显示产生某些输出的线程的线程信息。第二个需要确保仅在命令成功产生某些输出时才显示帧的帧信息 。 thread apply all -s -- frame apply all -s [option]… command-s-sthread applyframe apply-sframe apply

例如,可以使用以下命令打印局部变量或函数参数,而无需知道该变量或参数所在的线程或框架:

(gdb)tfaas p some_local_var_i_do_not_remember_where_it_is

tfaas命令接受与该命令相同的选项frame apply。参见框架适用。

thread name [name]

该命令为当前线程分配一个名称。如果未提供任何参数,则将删除任何现有的用户指定名称。线程名称显示在“信息线程显示。

在某些系统上,例如GNU / Linux,GDB能够确定操作系统给定的线程名称。在这些系统上,用'线程名'将覆盖系统名称,删除用户指定的名称将使 GDB再次显示系统指定的名称。

thread find [regexp]

搜索并显示名称或systag 与提供的正则表达式匹配的线程ID 。

以及作为“线程名”命令,该命令还允许您通过目标systag识别线程 。例如,在GNU / Linux上,目标systag 是LWP ID。

(GDB)线程查找26688
线程4具有目标ID'线程0x41e02940(LWP 26688)'
(GDB)信息线程4
  ID目标ID框架 
  选择()中的4线程0x41e02940(LWP 26688)0x00000031ca6cd372

线程4具有目标ID'线程0x41e02940(LWP 26688)'
(GDB)信息线程4
  ID目标ID框架 
  选择()中的4线程0x41e02940(LWP 26688)0x00000031ca6cd372

set print thread-events

set print thread-events on

set print thread-events off

set print thread-events当GDB注意到有新线程启动或线程退出时,该命令允许您启用或禁用消息打印。默认情况下,如果目标支持检测到这些事件,则将打印这些消息。请注意,不能在所有目标上禁用这些消息。

show print thread-events

显示当GDB检测到线程已启动和退出时是否打印消息。

有关停止和启动多线程程序时GDB行为的更多信息,请参见停止和启动多线程程序。

有关具有多个线程的程序中的监视点的信息,请参见设置监视点。

set libthread-db-search-path [path]

如果设置了此变量,则path是GDB将用于搜索的目录的冒号分隔列表libthread_db。如果您省略路径,libthread-db-搜索路径'将重置为默认值($sdir:$pdir在GNU / Linux和Solaris系统上)。在内部,默认值来自LIBTHREAD_DB_SEARCH_PATH 宏。

在GNU / Linux和Solaris系统上,GDB使用“帮助程序” libthread_db库来获取有关下级进程中线程的信息。 GDB将使用“libthread-db-搜索路径寻找libthread_db。 如果通过'启用了劣质的线程调试库加载功能,GDB还将首先进行咨询。设置自动加载libthread-db'(请参阅libthread_db.so.1文件)。

特殊条目“$ sdir“为”libthread-db-搜索路径'是指通常在系统默认目录中搜索以加载共享库的目录。'$ sdir'条目是唯一不需要由'启用的类型设置自动加载libthread-db'(请参阅libthread_db.so.1文件)。

特殊条目“$ pdir“为”libthread-db-搜索路径'是指libpthread 在下级进程中从中加载的目录。

对于GDB在上述目录中找到的任何libthread_db库, GDB都会尝试使用当前的劣等进程对其进行初始化。如果此初始化失败(可能由于和之间版本不匹配而发生),则GDB 将卸载,并继续下一个目录。如果没有一个库成功初始化,则 GDB将发出警告,并且线程调试将被禁用。 libthread_dblibpthreadlibthread_dblibthread_db

libthread-db-search-path目前仅在某些平台上实现设置。

show libthread-db-search-path

显示当前的libthread_db搜索路径。

set debug libthread-db

show debug libthread-db

打开或关闭libthread_db相关事件的显示。使用1启用,0以禁用。

你可能感兴趣的:(调试多线程程序)