Linux下多线程调试以及查看信息

pstack

pstack用来跟踪进程栈,这个命令在排查进程问题时非常有用,比如我们发现一个服务一直处于work状态(如假死状态,好似死循环),使用这个命令就能轻松定位问题所在;可以在一段时间内,多执行几次pstack,若发现代码栈总是停在同一个位置,那个位置就需要重点关注,很可能就是出问题的地方;

示例:查看bash程序进程栈:

 

/opt/app/tdev1$ps -fe| grep bash
tdev1   7013  7012  0 19:42 pts/1    00:00:00 -bash
tdev1  11402 11401  0 20:31 pts/2    00:00:00 -bash
tdev1  11474 11402  0 20:32 pts/2    00:00:00 grep bash
/opt/app/tdev1$pstack 7013
#0  0x00000039958c5620 in __read_nocancel () from /lib64/libc.so.6
#1  0x000000000047dafe in rl_getc ()
#2  0x000000000047def6 in rl_read_key ()
#3  0x000000000046d0f5 in readline_internal_char ()
#4  0x000000000046d4e5 in readline ()
#5  0x00000000004213cf in ?? ()
#6  0x000000000041d685 in ?? ()
#7  0x000000000041e89e in ?? ()
#8  0x00000000004218dc in yyparse ()
#9  0x000000000041b507 in parse_command ()
#10 0x000000000041b5c6 in read_command ()
#11 0x000000000041b74e in reader_loop ()
#12 0x000000000041b2aa in main ()

strace

strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
示例:

 

$strace cat /dev/null
execve("/bin/cat", ["cat", "/dev/null"], [/* 22 vars */]) = 0
brk(0)                                  = 0xab1000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29379a7000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
...

每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。

跟踪可执行程序

 

strace -f -F -o ~/straceout.txt myserver

-f -F选项告诉strace同时跟踪fork和vfork出来的进程,-o选项把所有strace输出写到~/straceout.txt里 面,myserver是要启动和调试的程序。

跟踪服务程序

 

strace -o output.txt -T -tt -e trace=all -p 28979

跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面。



作者:杀破魂
链接:https://www.jianshu.com/p/d6686cb72f68
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

gcc test.c -o test -lpthread -g

1.gdb test

2.list(l):list命令显示多行源代码,从上次的位置开始显示,默认情况下,一次显示10行,第一次使用时,从代码其实位置显示。

list n显示已第n行未中心的10行代码
list functionname显示以functionname的函数为中心的10行代码

3.break(b):

break location:在location位置设置断点,该位置可以为某一行,某函数名或者其它结构的地址。gdb会在执行该位置的代码之前停下来.

使用delete breakpoints 断点号——————删除断点
这里的断点号表示的是第几个断点,刚才执行break 10返回 
breakpoint 1 at 0x40050a: file test.c, line 10.
中的1表示该断点的标号,因此使用 delete breakpoints 1表示删除第10行所定义的断点

clear n表示清除第n行的断点,因此clear 10等同于delete breakpoints 1
disable/enable n表示使得编号为n的断点暂时失效或有效

可使用info查看断点相关的信息
info breakpoints

c(continue),继续程序运行直到下一个断点

3.c(continue)

3.display命令
查看参数的值

4.step及next命令
step可使得程序逐条执行,即执行完一条语句然后在下一跳语句前停下来,等待用户的命令。一般使用step命令时,可使用display或者watch命令查看变量的变化,从而判断程序行为是否符合要求。当下一条指令为函数时,s进入函数内部,在其第一条语句前停下来。next单步执行,但不进入函数内部。
step n,next n 表示连续但不执行n条指令,如果期间遇到断点,则停下来。

5.print打印内部变量值
6.bt 查看堆栈信息
7.watch 监视变量值的变化

watch通常需要和break,run,continue联合使用。

8.set variable value=x 动态改变变量值

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

1.线程的查看

首先创建两个线程

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

void* pthread_run1(void* arg)
{
  (void)arg;
  
  while(1)
  {
    cout << "I am thread1,ID" << pthread_self() << endl;
    sleep(1);
  }
}

void* pthread_run2(void* arg)
{
  (void)arg;
  while(1)
  {
    cout << "I am thread2 ID:" << pthread_self() << endl;
    sleep(1);
  }
}

int main()
{
  pthread_t tid1;
  pthread_t tid2;

  pthread_create(&tid1,NULL,pthread_run1,NULL);
  pthread_create(&tid2,NULL,pthread_run2,NULL);

  cout << "I am main thread" << endl;

  pthread_join(tid1,NULL);
  pthread_join(tid2,NULL);
  return 0;
}

分析:在上面的程序中创建了两个线程,程序执行起来,main函数所在的程序为主线程,在这个主线程中有两个新的线程运行
命令行查看:pstree -p 进程ID 

//查看但钱运行的进程
ps -aux | grep 执行文件

//查看当前运行的轻量级进程
ps -aL | grep 执行文件

//查看主线程和新线程的关系
pstree -p 主线程ID

在这里插入图片描述

在这里插入图片描述

2.线程栈结构的查看

1.获取线程ID
2.通过命令查看栈结构 pstack 线程ID

在这里插入图片描述

在这里插入图片描述

3.利用gdb查看线程信息

1.将进程附加到gdb调试器当中,查看是否创建了新线程:gdb attach 主线程ID
在这里插入图片描述

在这里插入图片描述
2.查看线程的一些信息

//1.查看进程:info inferiors
//2.查看线程:info threads
//3.查看线程栈结构:bt
//4.切换线程:thread n(n代表第几个线程)

在这里插入图片描述

在这里插入图片描述

4.利用gdb调试多线程

当程序没有启动,线程还没有执行,此时利用gdb调试多程序和调试普通程序一样,通过设置断点,运行,查看信息等等,在这里不演示了,最后会加上调试线程的命令
1.设置断点:

//1.设置断点:break 行号/函数名
//2.查看断点:info b

在这里插入图片描述

在这里插入图片描述
2.执行线程2的函数,执行完毕继续运行到断点处

1.继续使某一线程运行:thread apply 1-n(第几个线程)n
2.重新启动陈故乡运行到断点处:r

在这里插入图片描述

在这里插入图片描述
3.只运行当前线程

1.设置:set scheduler-locking on
2.运行:n

在这里插入图片描述

在这里插入图片描述
4.所有线程并发执行:

1.设置:set scheduler-locking off
2.运行:n

在这里插入图片描述

在这里插入图片描述

**总结:**调试多线程的命令

命令    用法
info threads    显示当前可调试的所有线程,每个线程有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID 。前面的有*的是当前调试的线程
thread ID    切换但钱的线程为指定ID的线程
break thread_test.c:123 thread all(例:在相应函数的位置设置断点break pthread_run1)    在所有线程中相应的行上设置断点
thread apply ID1 ID2 command    让一个或者多个线程执行GDB命令command
thread apply all command    让所有被调试线程执行GDB命令command
set scheduler-locking 选项 command    设置线程是以什么方式来执行命令
set scheduler-locking off    不锁定任何线程,也就是所有线程都执行,这是默认值
set scheduler-locking on    只有当前被调试程序会执行
set scheduler-locking on step    在单步的时候,除了next过一个函数的情况(熟悉情况的人都知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行
————————————————
版权声明:本文为CSDN博主「_kean」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/C1029323236/article/details/97512823

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值