精华内容
下载资源
问答
  • 多线程调试

    2019-09-29 18:31:44
    多线程调试必杀技 - GDB的non-stop模式 作者:破砂锅 开源的GDB被广泛使用在Linux、OSX、Unix和各种嵌入式系统(例如手机),这次它又带给我们一个惊喜。 多线程调试之痛 调 试器(如VS2008和老版GDB)往往只...

    多线程调试必杀技 - GDB的non-stop模式

    作者:破砂锅

    开源的GDB被广泛使用在Linux、OSX、Unix和各种嵌入式系统(例如手机),这次它又带给我们一个惊喜。

    多线程调试之痛

    调 试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序 freeze,直到你continue这个线程,程序中的其他线程才会继续运行。这个限制使得被调试的程序不能够像真实环境中那样运行--当某个线程断在 一个断点上,让其他线程并行运行。

    GDBv7.0引入的non-stop模式使得这个问题迎刃而解。在这个模式下,

    • 当某个或多个线程断在一个断点上,其他线程仍会并行运行
    • 你可以选择某个被断的线程,并让它继续运行

    让我们想象一下,有了这个功能后

    • 当其他线程断在断点上时,程序里的定时器线程可以正常的运行了,从而避免不必要得超时 
    • 当其他线程断在断点上时,程序里的watchdog线程可以正常的运行了,从而避免嵌入式硬件以为系统崩溃而重启
    • 可以控制多个线程运行的顺序,从而重现deadlock场景了。由于GDB可以用python脚本驱动调试,理论上可以对程序在不同的线程运行顺序下进行自动化测试。

    因此,non-stop模式理所当然成为多线程调试“必杀技”。这2009年下半年之后发布的Linux版本里都带有GDBv7.0之后的版本。很好奇,不知道VS2010里是不是也支持类似的调试模式了。

    演示GDB的non-stop模式

    让破砂锅用一个C++小程序在Ubuntu Linux 09.10下demo这个必杀技。虽然我的demo使用命令行版gdb,如果你喜欢图形化的调试器,Eclipse2009年5月之后的版本可以轻松的调 用这个功能,详情参见Eclipse参见http://live.eclipse.org/node/723

    1. 编译以下程序nonstop

    1 // gdb non-stop mode demo
    2 // build instruction: g++ -g -o nonstop nonstop.cpp -lboost_thread
    3
    4 #include <iostream>
    5 #include <boost/thread/thread.hpp>
    6
    7 struct op
    8 {
    9         op(int id): m_id(id) {}
    10
    11 void operator()()
    12         {
    13                 std::cout << m_id << " begin" << std::endl;
    14                 std::cout << m_id << " end" << std::endl;
    15         }
    16
    17 int m_id;
    18 };
    19
    20 int main(int argc, char ** argv)
    21 {
    22         boost::thread t1(op(1)), t2(op(2)), t3(op(3));
    23         t1.join(); t2.join(); t3.join();
    24 return 0;
    25 }
    26

    2. 把一下3行添加到~/.gdbinit来打开non-stop模式

    set target-async 1
    set pagination off
    set non-stop on

    3. 启动gdb,设断点,运行.可以看到主线程1是running,3个子线程都断在断点上,而不是只有一个子线程断在断点上.

    ~/devroot/nonstop$ gdb ./nonstop
    GNU gdb (GDB) 7.0-ubuntu
    Reading symbols from /home/frankwu/devroot/nonstop/nonstop...done.
    (gdb) break 14
    Breakpoint 1 at 0x402058: file nonstop.cpp, line 14.
    (gdb) break 24
    Breakpoint 3 at 0x401805: file nonstop.cpp, line 24.
    (gdb) run
    Starting program: /home/frankwu/devroot/nonstop/nonstop
    [Thread debugging using libthread_db enabled]
    [New Thread 0x7ffff6c89910 (LWP 2762)]
    [New Thread 0x7ffff6488910 (LWP 2763)]
    1 begin
    Breakpoint 1, op::operator() (this=0x605118) at nonstop.cpp:14
    14                  std::cout << m_id << " end" << std::endl;
    2 begin
    Breakpoint 1, op::operator() (this=0x605388) at nonstop.cpp:14
    14                  std::cout << m_id << " end" << std::endl;
    [New Thread 0x7ffff5c87910 (LWP 2764)]
    3 begin
    Breakpoint 1, op::operator() (this=0x605618) at nonstop.cpp:14
    14                  std::cout << m_id << " end" << std::endl;
    (gdb) info threads
    4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
    3 Thread 0x7ffff6488910 (LWP 2763)  op::operator() (this=0x605388) at nonstop.cpp:14
    2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
    * 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

    4. 让线程3继续运行,注意我顾意把主线程1也continue,这是我发现的workaround,否则gdb不能切回thread 1.

    (gdb) thread apply 3 1 continue
    Thread 3 (Thread 0x7ffff6488910 (LWP 2763)):
    Continuing.
    Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
    Continuing.
    Cannot execute this command while the selected thread is running.
    2 end
    [Thread 0x7ffff6488910 (LWP 2763) exited]
    warning: Unknown thread 3.
    Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
    Continuing.
    Cannot execute this command while the selected thread is running.
    (gdb) info threads
    4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
    2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
    * 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

    5. 让另外两个线程继续运行而结束,主线程断在第24行,最后结束.

    (gdb) thread apply 4 2 1 continue
    Thread 4 (Thread 0x7ffff5c87910 (LWP 2764)):
    Continuing.
    Thread 2 (Thread 0x7ffff6c89910 (LWP 2762)):
    Continuing.
    Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
    Continuing.
    Cannot execute this command while the selected thread is running.
    3 end
    1 end
    [Thread 0x7ffff5c87910 (LWP 2764) exited]
    [Thread 0x7ffff6c89910 (LWP 2762) exited]
    Breakpoint 3, main (argc=1, argv=0x7fffffffe348) at nonstop.cpp:24
    24          return 0;
    (gdb) continue
    Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
    Continuing.
    Program exited normally.

    参考资料

    Debugging with GDB

    Reverse Debugging, Multi-Process and Non-Stop Debugging Come to the CDT

     

    线程有自己的寄存器,运行时堆栈或许还会有私有内存。
    gdb提供了以下供调试多线程的进程的功能:
    * 自动通告新线程。
    * \ "thread THREADNO\ ",一个用来在线程之间切换的命令。
    * \ "info threads\ ",一个用来查询现存线程的命令。
    * \ "thread apply [THREADNO] [ALL] ARGS\ ",一个用来向线程提供命令的命令。
    * 线程有关的断点设置。
    注意:这些特性不是在所有gdb版本都能使用,归根结底要看操作系统是否支持。
    如果你的gdb不支持这些命令,会显示出错信息:
        (gdb) info threads
        (gdb) thread 1
        Thread ID 1 not known. Use the \ "info threads\ " command to
        see the IDs of currently known threads.
    gdb的线程级调试功能允许你观察你程序运行中所有的线程,但无论什么时候
    gdb控制,总有一个“当前”线程。调试命令对“当前”进程起作用。
    一旦gdb发现了你程序中的一个新的线程,它会自动显示有关此线程的系统信
    息。比如:
        [New process 35 thread 27]
    不过格式和操作系统有关。
    为了调试的目的,gdb自己设置线程号。
    `info threads\ "
    显示进程中所有的线程的概要信息。gdb按顺序显示:
    1.线程号(gdb设置)
    2.目标系统的线程标识。
    3.此线程的当前堆栈。
    一前面打\ "*\ "的线程表示是当前线程。
    例如:
    (gdb) info threads
    3 process 35 thread 27 0x34e5 in sigpause ()
    2 process 35 thread 23 0x34e5 in sigpause ()
    * 1 process 35 thread 13 main (argc=1, argv=0x7ffffff8)
    at threadtest.c:68
    `thread THREADNO\ "
    把线程号为THREADNO的线程设为当前线程。命令行参数THREADNO是gdb内定的
    线程号。你可以用\ "info threads\ "命令来查看gdb内设置的线程号。gdb显示该线程
    的系统定义的标识号和线程对应的堆栈。比如:
    (gdb) thread 2
    [Switching to process 35 thread 23]
    0x34e5 in sigpause ()
    \ "Switching后的内容取决于你的操作系统对线程标识的定义。
    `thread apply [THREADNO] [ALL] ARGS\ "
    此命令让你对一个以上的线程发出相同的命令\ "ARGS\ ",[THREADNO]的含义同上。
    如果你要向你进程中的所有的线程发出命令使用[ALL]选项。
    无论gdb何时中断了你的程序(因为一个断点或是一个信号),它自动选择信号或
    断点发生的线程为当前线程。gdb将用一个格式为\ "[Switching to SYSTAG]\ "的消息
    来向你报告。

    对于多线程程序,你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。
    break <linespec> thread <threadno>
    break <linespec> thread <threadno> if ...
    linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定thread <threadno>则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:
    (gdb) break frik.c:13 thread 28 if bartab > lim
    当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。

    转载于:https://www.cnblogs.com/stephen-init/p/3498944.html

    展开全文
  • gdb多线程调试

    2014-07-31 18:24:37
    gdb多线程调试
  • VC编译选项 多线程(/MT) 多线程调试(/MTd) 多线程 DLL (/MD) 多线程调试 DLL (/MDd)
    

     VC编译选项 多线程(/MT)

    多线程调试(/MTd)
    多线程 DLL (/MD)
    多线程调试 DLL (/MDd)
    C 运行时库                        库文件
    Single thread(static link) ML            libc.lib
    Debug single thread(static link) MLd        libcd.lib
    MultiThread(static link) MT            libcmt.lib
    Debug multiThread(static link) MTd            libcmtd.lib
    MultiThread(dynamic link) MD            msvert.lib
    Debug multiThread(dynamic link) MDd        msvertd.lib 
    3. 各种 C 运行时库的区别
    ( 1 )静态链接的单线程库
    静态链接的单线程库只能用于单线程的应用程序, C 运行时库的目标代码最终被编译在应用程序的二进制文件中。通过 /ML 编译选项可以设置 Visual C++ 使用静态链接的单线
    程库。
    ( 2 )静态链接的多线程库
    静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过 /MT 编译选项可以设置 Visual C++ 使用静态链接的多线程库。
    ( 3 )动态链接的运行时库
    动态链接的运行时库将所有的 C 库函数保存在一个单独的动态链接库 MSVCRTxx.DLL 中, MSVCRTxx.DLL 处理了多线程问题。使用 /MD 编译选项可以设置 Visual C++ 使用动态
    链接的运行时库。
    /MDd 、 /MLd 或 /MTd 选项使用 Debug runtime library( 调试版本的运行时刻函数库 ) ,与 /MD 、 /ML 或 /MT 分别对应。 Debug 版本的 Runtime Library 包含了调试信息
    ,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上 Release 版本。 
    下面是msdn关于Visual C++ 编译器选项的说明:
    这些选项选择单线程或多线程运行时例程,指示多线程模块是否为 DLL,并选择运行时库的发布版本或调试版本。
    选项     说明
    /MD     定义 _MT 和 _DLL 以便同时从标准 .h 文件中选择运行时例程的多线程特定版本和 DLL 特定版本。此选项还使编译器将库名 MSVCRT.lib 放入 .obj 文件中。
    用此选项编译的应用程序静态链接到 MSVCRT.lib。该库提供允许链接器解析外部引用的代码层。实际工作代码包含在 MSVCR71.DLL 中,该库必须在运行时对于与 MSVCRT.lib 链
    接的应用程序可用。
    当在定义了 _STATIC_CPPLIB (/D_STATIC_CPPLIB) 的情况下使用 /MD 时,它将导致应用程序通过静态多线程标准 C++ 库 (libcpmt.lib) 而非动态版本 (msvcprt.lib) 进行链接
    ,同时仍通过 msvcrt.lib 动态链接到主 CRT。
    /MDd     定义 _DEBUG、_MT 和 _DLL,以便从标准 .h 文件中选择运行时例程的调试多线程特定版本和 DLL 特定版本。它还使编译器将库名 MSVCRTD.lib 放入 .obj 文件中。
    /ML     使编译器将库名 LIBC.lib 放入 .obj 文件中,以便链接器使用 LIBC.lib 解析外部符号。这是编译器的默认操作。LIBC.lib 不提供多线程支持。
    /MLd     定义 _DEBUG 并使编译器将库名 LIBCD.lib 放入 .obj 文件中,以便链接器使用 LIBCD.lib 解析外部符号。LIBCD.lib 不提供多线程支持。
    /MT     定义 _MT,以便从标准头 (.h) 文件中选择运行时例程的多线程特定版本。此选项还使编译器将库名 LIBCMT.lib 放入 .obj 文件中,以便链接器使用 LIBCMT.lib 解析
    外部符号。创建多线程程序需要 /MT     或 /MD(或它们的调试等效选项 /MTd 或 /MDd)。
    /MTd     定义 _DEBUG 和 _MT。定义 _MT 会导致从标准 .h 文件中选择运行时例程的多线程特定版本。此选项还使编译器将库名 LIBCMTD.lib 放入 .obj 文件中,以便链接器使
    用 LIBCMTD.lib 解析外部符号。创    建多线程程序需要 /MTd 或 /MDd(或它们的非调试等效选项 /MT 或 MD)。
    /LD     创建 DLL。
    将 /DLL 选项传递到链接器。链接器查找 DllMain 函数,但并不需要该函数。如果没有编写 DllMain 函数,链接器将插入返回 TRUE 的 DllMain 函数。
    链接 DLL 启动代码。
    如果命令行上未指定导出 (.exp) 文件,则创建导入库 (.lib);将导入库链接到调用您的 DLL 的应用程序。
    将 /Fe 解释为命名 DLL 而不是 .exe 文件;默认程序名成为基名称.dll 而不是基名称.exe。
    如果还未显式指定 /M 选项之一,则将默认运行时库支持更改为 /MT。
    /LDd     创建调试 DLL。定义 _DEBUG。
        警告   不要混合使用运行时库的静态版本和动态版本。在一个进程中有多个运行时库副本会导致问题,因为副本中的静态数据不与其他副本共享。链接器禁止在 .exe 文件内
    部既使用静态版本又使用动态版本链接,但您仍可以使用运行时库的两个(或更多)副本。例如,当与用动态 (DLL) 版本的运行时库链接的 .exe 文件一起使用时,用静态(非 
    DLL)版本的运行时库链接的动态链接库可能导致问题。(还应该避免在一个进程中混合使用这些库的调试版本和非调试版本)。
    展开全文
  • 原创 VS中的多线程(/MT)、多线程调试(/MTd)、多线程DLL(/MD)、多线程调试DLL(/MDd)的区别 ...
    原创

    VS中的多线程(/MT)、多线程调试(/MTd)、多线程DLL(/MD)、多线程调试DLL(/MDd)的区别

    对于MSVC的运行库(CRT),按照静态/动态链接,可以分为静态版和动态版;按照调试/发布,可以分为调试版本和发布版本;按照单线程/多线程,可以分为单线程版本和多线程版本(但是目前VS中已不提供单线程版本了)

    在调试模式下,使用调试运行库:多线程调试(/MTd)、多线程调试DLL(/MDd)

    在发布模式下,使用发布运行库:多线程(/MT)、多线程DLL(/MD)

    调试模式下运行库带d,但发布模式不带。调试与发布的区别在于,发布模式省略了程序的调试信息,简单来说就是删除了调试模式下的用于调试的内容,所以一般情况下,发布模式下生成的可执行文件的大小比调试模式下生成的要小

    静态链接:多线程(/MT)、多线程调试(/MTd)

    动态链接:多线程DLL(/MD)、多线程调试DLL(/MDd)

    动态链接为D,静态链接为T。两者的区别在于,静态链接将程序所依赖的运行库集成到了可执行文件中,可执行文件运行时不再需要运行库;动态链接没有把程序所依赖的运行库集成到可执行文件中,可执行文件运行时需要运行库。由于静态链接将程序所依赖的运行库集成到了可执行文件中,一般情况下,生成的可执行文件的大小大于动态链接生成的

    对这四种运行库的选在位于VS的项目属性-》C/C++-》代码生成-》运行库:

     

    实例

    源码:

    1. #include <iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. cout << "Hello World!" << endl;
    6. system("pause");
    7. return 0;
    8. }

    (以下实验均在Win10+VS2017下完成)

    Win32、debug模式下:

    Win32、release模式下:

    我们可以发现,在同种模式下,动态链接小于静态链接;在同种链接下,release模式小于debug模式

    在x64、debug模式下:

    在x64、release模式下:

    同种模式和链接下,x64下生成的可执行文件大小大于Win32下生成的

    展开全文
  • gdb--多线程调试

    2016-10-16 07:54:38
    多线程调试

    GDB线程调试的命令:

    info threads:
    显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程。
    (gdb) info threads
    4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
    3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
    2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
    * 1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21

    thread ID :
    切换当前调试的线程为指定ID的线程。
    (gdb) thread 4
    [Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0 0xffffe002 in ?? ()

    break thread_test.c:123 thread all :
    break f.c:13 thread 28 if sum > 5:
    在所有线程中相应的行上设置断点

    thread apply ID1 ID2 command :
    让一个或者多个线程执行GDB命令command。

    thread apply all command :
    让所有被调试线程执行GDB命令command。

    set scheduler-locking off|on|step :
    估计是实际使用过多线程调试的人都可以发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
    off 不锁定任何线程,也就是所有线程都执行,这是默认值。
    on 只有当前被调试程序会执行。
    step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。

    参考文档:
    1.https://typecodes.com/cseries/multilprocessthreadgdb.html

    展开全文
  • 多线程的那点儿事 之多线程调试
  • 多线程调试 (一)多线程调试命令 shell的命令: (1)查看当前运行的进程:ps aux | grep book (2)查看当前运行的轻量级进程:ps -aL | grep book (3)查看主线程和子线程的关系:pstree -p 主线程id gdb的命令...
  • GDB多线程调试

    2018-06-22 18:32:17
    GDB多线程调试的基本命令 info threads:显示当前可调试的所有线程,GDB会给每个线程分配一个Id。前面有“ * ”号的表示当前正在调试的线程。 thread Id:切换当前调试的线程为指定线程。 break FileName.cpp:...
  • gdb多进程多线程调试

    2015-08-24 01:11:15
    gdb 多线程调试 多进程调试
  • gdb 多线程调试

    2016-05-17 15:28:52
    gdb 多线程调试 先介绍一下GDB多线程调试的基本命令。 info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程。 thread ID...
  • gdb多线程调试原理

    2020-03-11 20:05:12
    1: 在Linux系统上Gdb提供了一组多线程调试命令,如表所示: 多线程调试的主要任务是准确及时地捕捉被调试程序线程状态的变化的事件,并且GDB针对根据捕捉到的事件做出相应的操作,其实最终的结果就是维护一根叫...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,390
精华内容 2,556
关键字:

多线程调试