守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 2829|回复: 0
打印 上一主题 下一主题

[GCC/GDB/cygwin] 使用gdb调试多线程FlasCC应用程序

[复制链接]
  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    52903
    钢镚
    1422

    开源英雄守望者

    跳转到指定楼层
    楼主
    发表于 2014-12-29 19:32:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    多线程C/C++程序使用pthread库可以被FlasCC编译,在Flash Player11.5或以上版本中运行。

    我们开始吧!

    我们将使用一个非常简单的多线程C程序,文件名sample.c,它来自这个优秀的pthreads多线程教程


    1. #include <pthread.h>
    2. #include <stdio.h>
    3. #define NUM_THREADS     5

    4. void *PrintHello(void *threadid)
    5. {
    6.    long tid;
    7.    tid = (long)threadid;
    8.    printf("Hello World! It's me, thread #%ld!\n", tid);
    9.    pthread_exit(NULL);
    10. }

    11. int main (int argc, char *argv[])
    12. {
    13.    pthread_t threads[NUM_THREADS];
    14.    int rc;
    15.    long t;
    16.    for(t=0; t<NUM_THREADS; t++){
    17.       printf("In main: creating thread %ld\n", t);
    18.       rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
    19.       if (rc){
    20.          printf("ERROR; return code from pthread_create() is %d\n", rc);
    21.          exit(-1);
    22.       }
    23.    }

    24.    /* Last thing that main() should do */
    25.    pthread_exit(NULL);
    26. }
    复制代码
    这段代码创建一些线程将字符输出到屏幕上。首先使用gcc将代码编译到SWF。
    1. .../flascc/sdk/usr/bin/gcc -g -O0 -pthread sample.c -emit-swf -o sample.swf
    复制代码
    现在使用gdb运行这个SWF。
    1. .../flascc/sdk/usr/bin/gdb sample.swf
    复制代码
    在PrintHello函数前增加一个breakpoint,并运行程序:
    1. (gdb) break PrintHello
    2. No symbol table is loaded.  Use the "file" command.
    3. Make breakpoint pending on future shared library load? (y or [n]) y

    4. Breakpoint 1 (PrintHello) pending.
    5. (gdb) run
    6. Starting program: sample.swf
    7. 0xdddddddd in ?? ()
    复制代码
    注意PrintHello函数仍被FlashPlayer执行了,这是因为在默认情况下,gdb只在主线程执行,为了对其他线程进行操作,我们需要打开gdb的多线程支持。为此,我们需要启用gdb的"non-stop"模式。"non-stop"模式是指当程序中一个线程停止时,其他线程不受影响。当插入或在后台执行新的命令行,gdb会提示你输入下一个命令而原来的代码执行不受影响。当后台代码执行结束,gdb会通知你。在此模式下,FlasCC允许你单独控制所有线程。

    "non-stop"模式与gdb默认的"all-stop"模式略有不同。在"all-stop"模式中,gdb同时执行所有命令,这意味着只有在所有命令执行结束,才可以输入新命令。在此模式下,gdb会忽略主线程以外的其他线程。注意这是gdb的特定行为,转而言之,"all-stop"模式是指程序中的一个线程停止,其他所有线程也将停止。

    我们按CTRL+C退出gdb会话,选择Y。
    1. ^C
    2. Program received signal SIGTRAP, Trace/breakpoint trap.
    3. 0x00000000 in ?? ()
    4. (gdb) q
    5. A debugging session is active.

    6.         Inferior 1 [Remote target] will be killed.

    7. Quit anyway? (y or n) y
    复制代码
    重新启动gdb,我们尝试使用"non-stop"模式。
    1. .../flascc/sdk/usr/bin/gdb sample.swf
    复制代码
    "non-stop"模式调试程序

    "non-stop"模式必须在开运行程序前启用,为此你需要调用3个命令:
    1. (gdb) set pagination off
    2. (gdb) set target-async on
    3. (gdb) set non-stop on
    复制代码
    技巧:如果你讨厌每次输入这些命令,你可以在a.gdbinit文件中自定义一个gdb命令。
    1. define myCustomCommand
    2.     set pagination off
    3.     set target-async on
    4.     set non-stop on
    5. end
    复制代码
    .gdbinit文件会在gdb每次启动时自动加载,它可以放在根目录或当前目录,这样下次启动gdb时,你只需要输入一行代码就可以启用"non-stop"模式。
    1. (gdb) myCustomCommand
    复制代码
    现在我们已经准备好调试程序,首先设置一个breakpoint在main:
    1. (gdb) break main
    复制代码
    运行SWF:
    1. (gdb) run
    复制代码
    程序需要几秒加载,然后在main这中断了。你可以忽略[Worker 1]#1的输出,直到确认程序在main这中断了。
    1. Breakpoint 1, 0xf0000083 in main (argc=0, argv=0x200ff0) at sample.c:18
    2. 18          for(t=0; t<NUM_THREADS; t++){
    3. (gdb)
    复制代码
    目前为止这与默认模式的gdb很相似,但这也是事情发生变化的地方。也许你想让代码继续执行,当你这样做的时候你会看到错误提示:
    1. (gdb) step
    2. Cannot execute this command while the selected thread is running.
    复制代码
    gdb报告说虽然线程被中断,可是该线程仍然在运行。你可能不理解为什么会有2个线程?毕竟在这段代码中,我们没有使用多线程。原因在于起用多线程支持后,FlasCC会创建后台线程执行程序,这允许你对C代码进行多种操作而不影响UI。你可以使用info threads命令查看运行程序的线程:
    1. (gdb) info threads
    2.   Id   Target Id         Frame
    3.   2    Worker 2          0xf0000083 in main (argc=0, argv=0x200ff0) at sample.c:18
    4. * 1    Worker 1          (running)
    复制代码
    你可以看到线程2,代码在18行处中断了,而线程1,UI线程仍然在运行。为了管理UI事件,UI线程负责管理其他较低级别的线程。由于这个原因,如果你想其他线程能够顺畅运行,最好不要中断UI线程。

    *表示当前gdb中选择的程序。可以使用线程命令改变gdb当前选择的线程。我们来选择线程2。
    1. (gdb) thread 2
    2. [Switching to thread 2 (Worker 2)]
    3. #0  0xf0000083 in main (argc=0, argv=0x200ff0) at sample.c:18
    4. 18          for(t=0; t<NUM_THREADS; t++){
    复制代码
    现在再试一下step,输入2次:
    1. (gdb) step
    2. 19              printf("In main: creating thread %ld\n", t);
    3. (gdb) step
    4. 20              rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
    复制代码
    在这里我们使用pthread_create函数创建一个线程。该线程将调用PrintHello函数。如果你注意观察sample.c的源代码,会发现我们实际是在一个for循环中创建大量的线程。我们中断了PrintHello函数,所以我们可以完全控制每一个线程,创建或调用PrintHello函数。
    1. (gdb) break PrintHello
    2. Breakpoint 2 at 0xf000004e: file sample.c, line 8.
    复制代码
    现在所处的线程是运行步进的,我们来看看会发生什么:
    1. (gdb) step
    2. 21              if (rc){
    3. (gdb) [New Worker 4]

    4. Breakpoint 2, 0xf000004f in PrintHello (threadid=0x0) at sample.c:8
    5. 8           tid = (long)threadid;
    复制代码
    如果你仔细看会觉得有点怪。当你跳过执行这一行代码,gdb提示一切正常,同时另一个线程开始调用PrintHello ,很快它执行到中断处,输出到屏幕上。别担心一切正常。你会发现你仍然可以输入gdb命令,即使你没有看见任何提示符。


    如果你想找回提示符,使用CTRL+C即可。CTRL+C,然后使用info threads命令查看线程:
    1. Quit
    2. (gdb) info threads
    3.   Id   Target Id         Frame
    4.   3    Worker 4          0xf000004f in PrintHello (threadid=0x0) at sample.c:8
    5. * 2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    6.   1    Worker 1          (running)
    7. (gdb)
    复制代码
    使用step命令

    切换到Id3线程,使用2次step命令:
    1. (gdb) thread 3
    2. [Switching to thread 3 (Worker 4)]
    3. #0  0xf000004f in PrintHello (threadid=0x0) at sample.c:8
    4. 8           tid = (long)threadid;
    5. (gdb) step
    6. 9           printf("Hello World! It's me, thread #%ld!\n", tid);
    7. (gdb) step
    8. 10          pthread_exit(NULL);
    9. (gdb)
    复制代码
    我们看到PrintHello函数的最后一行pthread_exit()被执行。如果我们多执行一次step命令,意味着这个线程执行结束,我们将看不到任何输出:
    1. (gdb) step
    复制代码
    不用担心这也是正常的。step命令会gdb跳过当前行,直接运行下一行代码。然而在这种情况下,pthread_exit()会导致调用进程终止,没有返回值也不会继续运行下一行(线程已运行结束)。gdb会等待是否有新命令输入。你可以按下CTRL+C中断gdb,再按下CTRL+C获得提示符,然后使用info threads命令查看线程:
    1. (gdb) info threads
    2.   Id   Target Id         Frame
    3.   2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    4.   1    Worker 1          (running)

    5. The current thread  has terminated.  See `help thread'.
    6. (gdb)
    复制代码
    注意Id3线程已终止,没有显示在线程列表里,也没有*当前选择的线程。现在我们回到被挂起的线程2:
    1. (gdb) thread 2
    2. [Switching to thread 2 (Worker 2)]
    3. #0  0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    4. 21              if (rc){
    复制代码
    如果我们执行4次step命令,将创建1个新线程调用PrintHello函数。
    1. (gdb) step
    2. 18          for(t=0; t<NUM_THREADS; t++){
    3. (gdb) step
    4. 19              printf("In main: creating thread %ld\n", t);
    5. (gdb) step
    6. 20              rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
    7. (gdb) step
    8. 21              if (rc){
    9. (gdb) [New Worker 5]

    10. Breakpoint 2, 0xf000004f in PrintHello (threadid=0x1) at sample.c:8
    11. 8           tid = (long)threadid;
    复制代码
    CTRL+C,然后选择新线程:
    1. Quit
    2. (gdb) info threads
    3.   Id   Target Id         Frame
    4.   4    Worker 5          0xf000004f in PrintHello (threadid=0x1) at sample.c:8
    5. * 2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    6.   1    Worker 1          (running)
    7. (gdb) thread 4
    8. [Switching to thread 4 (Worker 5)]
    9. #0  0xf000004f in PrintHello (threadid=0x1) at sample.c:8
    10. 8           tid = (long)threadid;
    11. (gdb)
    复制代码
    使用continue命令

    这次我们试试continue命令:
    1. (gdb) continue
    2. Continuing.
    复制代码
    当gdb continue线程,会在中断处一直等待,直到你输入新的命令。在这里线程2被挂起,线程4已结束,除非我们恢复线程2,否则程序不会再运行到中断处。为了继续调试程序,我们需要中断gdb并恢复线程2。CTRL+C中断gdb,再按下CTRL+C获得提示符,然后使用info threads命令查看线程:
    1. (gdb) info threads
    2.   Id   Target Id         Frame
    3.   2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:
    4. 21
    5.   1    Worker 1          (running)

    6. The current thread  has terminated.  See `help thread'.
    7. (gdb)
    复制代码
    返回主线程,然后执行4次step命令,创建新线程。
    1. (gdb) thread 2
    2. [Switching to thread 2 (Worker 2)]
    3. #0  0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    4. 21              if (rc){
    5. (gdb) step
    6. 18          for(t=0; t<NUM_THREADS; t++){
    7. (gdb) step
    8. 19              printf("In main: creating thread %ld\n", t);
    9. (gdb) step
    10. 20              rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
    11. (gdb) step
    12. 21              if (rc){
    13. (gdb) [New Worker 6]

    14. Breakpoint 2, 0xf000004f in PrintHello (threadid=0x2) at sample.c:8
    15. 8           tid = (long)threadid;
    复制代码
    按下CTRL+C获得提示符,然后切换到Id5线程:
    1. Quit
    2. (gdb) info threads
    3.   Id   Target Id         Frame
    4.   5    Worker 6          0xf000004f in PrintHello (threadid=0x2) at sample.c:8
    5. * 2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:
    6. 21
    7.   1    Worker 1          (running)
    8. (gdb) thread 5
    9. [Switching to thread 5 (Worker 6)]
    10. #0  0xf000004f in PrintHello (threadid=0x2) at sample.c:8
    11. 8           tid = (long)threadid;
    12. (gdb)
    复制代码
    使用continue&命令
    再次执行continue命令,与上次稍有不同,使用的命令是continue&。&符号表示这是一个异步命令,gdb会提示你输入下一个命令。
    1. (gdb) continue&
    2. Continuing.
    3. (gdb)
    复制代码
    这个命令允许我们继续长时间运行线程,同时允许我们继续输入新命令及调试其他线程。本教程最后附有一个表格,显示所有&支持的命令。

    如果现在执行info threads,我们会看到这个线程运行完毕。
    1. (gdb) info threads
    2.   Id   Target Id         Frame
    3.   2    Worker 2          0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:
    4. 21
    5.   1    Worker 1          (running)

    6. The current thread  has terminated.  See `help thread'.
    复制代码
    这种情况下使用continue&很方便,因我们不需要在线程结束后按CTRL+C。

    我们回到 主线程,执行continue&:
    1. (gdb) thread 2
    2. [Switching to thread 2 (Worker 2)]
    3. #0  0xf00000a2 in main (argc=0, argv=0x200ff0) at sample.c:21
    4. 21              if (rc){
    5. (gdb) continue&
    6. Continuing.
    7. (gdb)
    8. Breakpoint 2, 0xf000004f in PrintHello (threadid=0x3) at sample.c:8
    9. 8           tid = (long)threadid;

    10. Breakpoint 2, 0xf000004f in PrintHello (threadid=0x4) at sample.c:8
    11. 8           tid = (long)threadid;
    复制代码
    仔细观察你会发现当其他线程运行到中断处,提示符就会出现。按下CTRL+C获得提示符。

    使用thread apply命令
    在调试多线程应用时,可能有很多线程,你需要经常在线程之间切换。thread apply是一个很方便的命令。它允许你为一系列线程调用相同的命令。举例,我们来backtrace2个线程到PrintHello中断处。
    1. (gdb) info threads
    2.   Id   Target Id         Frame
    3.   7    Worker 8          0xf000004f in PrintHello (threadid=0x4) at sample.c:8
    4.   6    Worker 7          0xf000004f in PrintHello (threadid=0x3) at sample.c:8
    5.   1    Worker 1          (running)

    6. The current thread  has terminated.  See `help thread'.
    7. (gdb) thread apply 6 7 backtrace

    8. Thread 6 (Worker 7):
    9. #0  0xf000004f in PrintHello (threadid=0x3) at sample.c:8
    10. #1  0xf000ce95 in _thread_start () from remote:6.elf
    11. #2  0xf000152e in _thread_run () from remote:2.elf
    12. #3  0x00000000 in ?? ()

    13. Thread 7 (Worker 8):
    14. #0  0xf000004f in PrintHello (threadid=0x4) at sample.c:8
    15. #1  0xf000ce95 in _thread_start () from remote:6.elf
    16. #2  0xf000152e in _thread_run () from remote:2.elf
    17. #3  0x00000000 in ?? ()
    18. (gdb)
    复制代码
    你也可以通过thread apply all将命令适用于所有线程。

    1. (gdb) thread apply all backtrace

    2. Thread 7 (Worker 8):
    3. #0  0xf000004f in PrintHello (threadid=0x4) at sample.c:8
    4. #1  0xf000ce95 in _thread_start () from remote:6.elf
    5. #2  0xf000152e in _thread_run () from remote:2.elf
    6. #3  0x00000000 in ?? ()

    7. Thread 6 (Worker 7):
    8. #0  0xf000004f in PrintHello (threadid=0x3) at sample.c:8
    9. #1  0xf000ce95 in _thread_start () from remote:6.elf
    10. #2  0xf000152e in _thread_run () from remote:2.elf
    11. #3  0x00000000 in ?? ()

    12. Thread 1 (Worker 1):
    13. Target is executing.
    14. (gdb)
    复制代码
    结论
    现在你知道怎样在gdb中调试多线程的FlasCC程序。如果你有任何问题请在下面留言。但最好还是FlasCC论坛提交你的问题。如果你还没有注册,请点击这里

    命令摘要
    以下是常用的调试命令

    • info   threads   显示线程列表
    • thread X  设置选定线程的Id
    • thread apply X  Y  对指定线程X执行指定命令Y   例子:thread apply 3  4 step
    • thread apply all  Y  对所有线程X执行指定命令Y  例子:thread apply all step


    以下命令支持& 异步

    • step&   跳过本行代码(进行子程序调用)
    • next&   将程序移动到下一行代码(进行子程序调用)
    • continue&  继续执行当前选定线程


    更多参考资料


    本文来自:http://bbs.9ria.com/thread-164454-1-1.html



    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
    收藏收藏 分享分享 支持支持 反对反对 微信
    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2026-1-25 07:57 , Processed in 0.044766 second(s), 30 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表