Oracle® Developer Studio 12.5:使用 dbx 调试程序

退出打印视图

更新时间: 2016 年 6 月
 
 

了解多线程调试

dbx 通过检测程序是否利用 libthread.so 来识别多线程程序。程序对 libthread.so 的使用方法有两种:一是通过 -lthread-mt 对其进行显式编译;二是通过 -lpthread 对其进行隐式编译。

检测到多线程程序时,dbx 会尝试装入 libthread_db.so,它是一个专门用于进行线程调试的系统库,位于 /usr/lib 目录下。

dbx 是同步式的;所以当某个线程或轻量级进程 (lightweight process, LWP) 停止时,所有其他线程和 LWP 也会相应停止。 我们有时将这种行为称作 "stop the world" 模型。


注 -  有关多线程编程和 LWP 的信息,请参见 Oracle Solaris《多线程编程指南》。

线程信息

dbx 中提供有以下线程信息示例。

(dbx) threads
    t@1 a l@1  ?()  running   in main()
    t@2      ?() asleep on 0xef751450  in_swtch()
    t@3 b l@2  ?()  running in sigwait()
    t@4     consumer()  asleep on 0x22bb0 in _lwp_sema_wait()
  *>t@5 b l@4 consumer()  breakpoint     in Queue_dequeue()
    t@6 b l@5 producer()     running       in _thread_start()
(dbx)

    本机代码的每行信息由以下内容组成:

  • *(星号)表示该线程内发生了需要用户处理的事件。该事件通常是断点。

    如果不是星号,而是 "o",则表示发生的是 dbx 内部事件。

  • >(箭头)表示为当前线程。

  • 线程 id t@number 指特定线程。numberthr_create 传回的 thread_t 值。

  • b l@numbera l@number 表示线程绑定到指定的 LWP 或在其上处于活动状态(即操作系统可实际运行该线程)。

  • 传递给 thr_create 的线程的“启动函数”。?() 表示启动函数未知。

  • 线程状态。

  • 线程当前执行的函数。

    Java 代码的每行信息由以下内容组成:

  • t@numberdbx 样式的线程 ID

  • 线程状态

  • 加单引号的线程名

  • 说明线程优先级的数字

线程和 LWP 状态

suspended(挂起)

线程已被显式挂起。

runnable(可运行)

线程可运行,并正等待作为计算资源的 LWP。

zombie(僵停)

分离的线程退出 (thr_exit)) 后,在通过使用 thr_join(). THR_DETACHED(它是在线程创建时,(thr_create()) 指定的标志)重新链接之前,将一直处于僵停状态。退出的非分离线程在被获得前将一直处于僵停状态。

asleep on syncobj(在 syncobj 上休眠)

线程在给定同步对象上被阻塞。视 libthreadlibthread_db 所提供的支持级别,syncobj 可能会如十六进制地址一般简单,也可能会包含更多的信息内容。

active(活动)

线程在某 LWP 中处于活动状态,但 dbx 无法访问该 LWP。

unknown(未知)

dbx 无法确定状态。

lwpstate

绑定或活动线程状态具有与之关联的 LWP 的状态。

running(正在运行)

LWP 正在运行,但因响应另一 LWP 而同步停止。

syscall num

LWP 进入给定系统调用 # 时停止。

syscall return num(syscall return num)

LWP 退出给定系统调用 # 时停止。

job control(作业控制)

LWP 因作业控制而停止。

LWP suspended(LWP 挂起)

LWP 在内核中被阻塞。

single stepped(单步递阶)

LWP 刚刚完成一步。

breakpoint(断点)

LWP 刚刚遇到断点。

fault num(故障 num)

LWP 招致给定错误 #。

signal name(信号 name)

LWP 招致给定信号。

process sync(进程同步)

此 LWP 所属进程刚刚开始执行。

LWP death(LWP 停止)

LWP 正在退出。

查看另一线程的上下文

要将查看上下文切换到另一线程,请使用 thread 命令。语法是:

thread [-blocks] [-blockedby] [-info] [-hide] [-unhide] [-suspend] [-resume] thread_id

要显示当前线程:

thread

要切换到线程 thread-ID

thread thread-ID

有关更多信息,请参见thread 命令

查看线程列表

要查看线程列表,请使用 threads 命令。语法是:

threads [-all] [-mode [all|filter] [auto|manual]]

要输出所有已知线程列表:

threads

要输出通常不输出的线程(僵停):

threads -all

有关线程列表的说明,请参见线程信息

有关 threads 命令的更多信息,请参见threads 命令

恢复执行

可以使用 cont 命令来恢复程序执行。 因为线程目前使用同步断点,所以所有线程都会恢复执行。不过,您可以使用带有 –resumeone 选项的 call 命令恢复单个线程。

    在调试多线程应用程序(其中许多线程会调用函数 lookup())时,请考虑以下两种情况:

  • 您设置条件断点:

    stop in lookup -if strcmp(name, "troublesome") == 0

    t@1 在对 lookup() 的调用处停止时,dbx 尝试对条件求值并调用 strcmp()

  • 您设置断点:

    stop in lookup

    t@1 在对 lookup() 的调用处停止时,发出以下命令:

    call strcmp(name, "troublesome")

调用 strcmp() 时,dbx 将会恢复调用期间内的所有线程,这与使用 next 命令进行单步执行时 dbx 所执行的操作类似。之所以这样做的原因是,如果 strcmp() 尝试抓取另一个线程拥有的锁,则仅恢复 t@1 可能会导致死锁。

在这种情况下恢复所有线程的缺点是,dbx 无法处理另一个线程(例如 t@2),从而会在调用 strcmp() 时在 lookup() 处遇到断点。它会发出类似以下之一的警告:

event infinite loop causes missed events in following handlers:

Event reentrancy first event BPT(VID 6, TID 6, PC echo+0x8) second event BPT(VID 10, TID 10, PC echo+0x8) the following handlers will miss events:

在这些情况下,如果可以确定条件表达式中调用的函数不会抓取互斥锁,则可使用 -resumeone 事件修饰符强制 dbx 只恢复 t@1

stop in lookup -resumeone -if strcmp(name, "troublesome") == 0

只恢复在 lookup() 中遇到断点的线程,以便对 strcmp() 进行求值。

    此方法在诸如以下示例中会无能为力:

  • 由于条件以递归方式调用 lookup()lookup() 上的第二个断点发生在同一个线程中

  • 运行条件的线程放弃控制权、休眠或以某种方式将控制权交给另一个线程