gdb之线程
来源:互联网 发布:java static是在类加载 编辑:程序博客网 时间:2024/06/05 17:55
gdb如何调试多线程呢??之前写了一个同步异步的例子,因为对异步来说通常采用的机制有两种,一是轮询,就是说每隔一段时间就过来询问一下,如果有就调用,对于这个机制,epoll比较适合,另一个就是回调,也就是说当我准备好了后直接通知你,基于这个,今天就顺着昨天的例子增加一些函数来理解异步回调,同时学习基础的gdb调试多线程。
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <pthread.h>typedef void (*CBK_FUN)();struct CBK_CALL{ void *data; CBK_FUN fn;};void printSync(const char *str){ sleep(1); // assume this will do lots of thing puts(str); sleep(1); // assume this also will do lots of thing}void *thread_proc(void *data){ struct CBK_CALL *cbk_call = data; sleep(1); puts((char *)(cbk_call->data)); sleep(1); cbk_call->fn(data); return (void *)0;}pthread_t tid;void printAsync(const char *str, CBK_FUN fn){ struct CBK_CALL cbk_call = {(void *)str, fn}; pthread_create(&tid, NULL, thread_proc, (void *)(&cbk_call)); }void callback(){ puts("After Sync Call");}int main(){ printSync("I am Sync call"); printAsync("I am a Async call", callback); puts("end"); pthread_join(tid, NULL); return 0;}
程序比较简单,基本思想就是先执行先同步输出, 接着printAsync会异步执行,且执行完后会回调callback函数,可是由于粗心大意,编译运行发现一直不成功,没办法,上gdb
# gdb a.out
(gdb) b printAsync // 函数断点, 输入print后按tab和Linux效果一样
Breakpoint 1 at 0x400696: file error.c, line 31.
(gdb) s // 进入函数
(gdb) n
(gdb) p cbk_call // 打印cbk_call的值,<和预期的一样>, 执行 p cbk_call.data, p (char *)cbk_call.data可以打印更多信息
$1 = {data = 0x400837, fn = 0x4006c3 <callback>}
(gdb) p cbk_call.fn() // gdb 直接打印函数执行的结果, <和预期的一样>
After Sync Call
$2 = void
$2 = void
(gdb) p &cbk_call
$3 = (struct CBK_CALL *) 0x7fffffffe590
(gdb) n // 下一步执行就会创建一个新的线程
[New Thread 0x7ffff7fe9710 (LWP 1716)]
(gdb) info thread // 打印当前进程的线程信息, <注,Thread前面标记*表示当前gdb正在调试>
2 Thread 0x7ffff7fe9710 (LWP 1716) 0x0000003c0f2e1501 in clone () from /lib64/libc.so.6
* 1 Thread 0x7ffff7feb700 (LWP 1702) printAsync (str=0x400837 "I am a Async call", fn=0x4006c3 <callback>) at error.c:33
* 1 Thread 0x7ffff7feb700 (LWP 1702) printAsync (str=0x400837 "I am a Async call", fn=0x4006c3 <callback>) at error.c:33
(gdb) thread 2 // 切换到标记为2的线程上去执行,
[Switching to thread 2 (Thread 0x7ffff7fe9710 (LWP 1716))]#0 0x0000003c0f2e1501 in clone () from /lib64/libc.so.6
(gdb) bt // 打印函数调用堆栈
#0 0x0000003c0f2e1501 in clone () from /lib64/libc.so.6
#1 0x0000003c0f607710 in ?? () from /lib64/libpthread.so.0
#2 0x00007ffff7fe9710 in ?? ()
#3 0x0000000000000000 in ?? ()
#1 0x0000003c0f607710 in ?? () from /lib64/libpthread.so.0
#2 0x00007ffff7fe9710 in ?? ()
#3 0x0000000000000000 in ?? ()
(gdb) thread apply all bt // 打印所以线程的函数调用堆栈
Thread 2 (Thread 0x7fb0d2a47710 (LWP 2136)):
#0 0x0000003c0f2e1501 in clone () from /lib64/libc.so.6
#1 0x0000003c0f607710 in ?? () from /lib64/libpthread.so.0
#2 0x00007ffff7fe9710 in ?? ()
#3 0x0000000000000000 in ?? ()
#0 0x0000003c0f2e1501 in clone () from /lib64/libc.so.6
#1 0x0000003c0f607710 in ?? () from /lib64/libpthread.so.0
#2 0x00007ffff7fe9710 in ?? ()
#3 0x0000000000000000 in ?? ()
Thread 1 (Thread 0x7fb0d2a49700 (LWP 2135)):
#0 0x0000003c0f60803d in pthread_join () from /lib64/libpthread.so.0
#1 0x00000000004006cc in main ()
#0 0x0000003c0f60803d in pthread_join () from /lib64/libpthread.so.0
#1 0x00000000004006cc in main ()
(gdb) s
(gdb) s // 进入到线程<就是函数>
(gdb) p data // 打印传进来的参数, 并没有发生改变,还没有找到错误的地方???
$4 = (void *) 0x7fffffffe590
(gdb) p *(struct CBK_CALL*)data // 期望和之前 p cbk_call的内容一样, 结果发现不同, 说明参数传进来后所指向地址的内容发生了变化
$6 = {data = 0x400520, fn = 0x7fffffffe690}
(gdb) q // 问题找到了, 退出调试
为什么会发生变化呢, 原来是因为main函数调用printAsync函数,而在printAsync函数的局部变量cbk_call在printAsync函数返回后由于栈空间被释放了所以值就不在了,而拷贝传给线程的只是一个指针,指针指向的内容却已经被重写了。
解决办法, 将cbk_call声明称全局变量.
pstack脚本分析
#!/bin/sh
### 参数判断, 如果不是 #pstack pid 形式则打印使用方式并退出if test $# -ne 1; then echo "Usage: `basename $0 .sh` <process-id>" 1>&2 exit 1fi
### $1为传进来的pid, 如果/proc/$pid这个目录不存在则打印进程没有找到的信息
### 当一个进程运行时,在/proc/$pid目录下会生成很多和该进程相关的文件,可以cd 到对应的进程该目录下查看,可以确定,进程执行时一定有该目录if test ! -r /proc/$1; then echo "Process $1 not found." 1>&2 exit 1fi# GDB doesn't allow "thread apply all bt" when the process isn't# threaded; need to peek at the process to determine if that or the# simpler "bt" should be used.
### gdb中打印堆栈的命令btbacktrace="bt"
### 如果是多线程可以看到/proc/$pid/task目录下有多个目录,且一个线程对应一个目录
### 就是判断这一时刻进程中是否有多个线程在执行来决定是否需要将bt替换为"thread apply all bt"if test -d /proc/$1/task ; then # Newer kernel; has a task/ directory. if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; thenbacktrace="thread apply all bt" fielif test -f /proc/$1/maps ; then # Older kernel; go by it loading libpthread.
### 同样,但此刻有多线程执行时,可以grep到调用了libpthread线程库 if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; thenbacktrace="thread apply all bt" fifi
### 如果GDB有值则不变,否则赋值为/usr/bin/gdb 类似 GDB=$GDB?$GDB:/usr/bin/gdbGDB=${GDB:-/usr/bin/gdb}if $GDB -nx --quiet --batch --readnever > /dev/null 2>&1; then readnever=--readneverelse readnever=fi# Run GDB, strip out unwanted noise.$GDB --quiet $readnever -nx /proc/$1/exe $1 <<EOF 2>&1 | set width 0set height 0set pagination no$backtraceEOF/bin/sed -n \ -e 's/^\((gdb) \)*//' \ -e '/^#/p' \ -e '/^Thread/p'
执行 bash -x /usr/bin/pstack $pid 可以看到shell脚本执行的全部过程
0 0
- gdb之线程
- gdb调试死锁线程
- gdb进入线程
- gdb 死锁线程
- gdb调试线程
- gdb调试线程锁
- 使用gdb调试死锁线程
- gdb多线程调试锁定线程
- 关于gdb调试线程死锁
- gdb调试多进程、线程
- GDB调试多进程/线程
- gdb 打印所有线程堆栈
- gdb调试多好几次&线程
- unix之gdb
- GDB调试之attach
- Linux之gdb学习
- gdb之signal
- gdb之x命令
- Linux知识点
- 保存图片到SD卡
- 黑马程序员——Java前述
- WatchOS2.0 自定义表盘元素
- hdu4277搜索
- gdb之线程
- Git 使用规范流程
- Linux 线程同步的三种方法
- 100条超实用微信营销技巧:公众号、朋友圈和微信营销
- TI-RTOS Sys-Bios操作系统无法获取Hwi,Swi的Name问题
- hdu 5400 Arithmetic Sequence
- POJ 3041 Asteroids HDU 1150 Machine Schedule (最小顶点覆盖)
- Dota2 on Ubuntu
- android常用的工具类——将图形变成圆形