部分文章内容为公开资料查询整理,原文出处可能未标注,如有侵权,请联系我,谢谢。邮箱地址:gnivor@163.com ►►►需要气球么?请点击我吧!

linux/C++笔记-查看线程详情-pstree/pstack/gdb调试多线程

参考资料

线程的查看以及利用gdb调试多线程 https://blog.csdn.net/zhangye3017/article/details/80382496
GDB教程 http://c.biancheng.net/view/8232.html
next step : https://blog.csdn.net/www_dong/article/details/117374370

简介

本文给出了常用的gdb命令解释,以及使用示例

查看主线程和新线程的关系

pstree -p 主线程id

查看线程栈结构

pstack pid

gdb命令目录

命令 用法 示例
info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,操作线程时可使用此ID。
前面有*的是当前调试的线程
thread ID(1,2,3…) 切换当前调试的线程为指定ID的线程
break thread_test.c:123 thread all 普通断点: 在所有线程中相应的行上设置断点 1. break pthread_run1 在进入pthread_run1函数时停住
2. break 20 在第20行停住
3. break +/-offset 在当前行号的前/后的offset行停住
4. break filename:linenum 在源文件filename的linenum行处停住。
l list的简写, 查看当前调试的代码上下文, 显示当前context的代码
i b info breakpoint 的简写查看断点(包括编号)
delete 删除所有断点 delete [breakpoints num] [range...] delete 1delete 1-10
d 1 删除指定编号的断点, 这里与delete 1 功能相同,指删除编号1的断点
n next的简写, 单步调试, 不过当遇到包含调用函数的语句时,无论函数内部包含多少行代码,next 指令都会一步执行完 例: next 1 单步执行1行代码
s step的简写, 单步调试,和next的区别: 当 step 命令所执行的代码行中包含函数时,会进入该函数内部,并在函数第一行代码处停止执行
pring val_1 打印val_1的值
pring val_1=10 修改val_1的值
display val_1 与print类似,display 用于调试阶段查看某个变量或表达式的值
区别: 使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动打印
而 print只打印一次
watch val_1 观察val_1的值,当有变化时,停止
clear 删除所在行的断点 删除函数的所有断点: clear list_insert
删除文件:函数的所有断点: clear list.c:list_delet
删除行号的所有断点: clear 12
删除文件:行号的所有断点: clear list.c:12
thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command thread apply 2 n 表示让线程2继续执行
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的行为)以外,
只有当前线程会执行

示例

代码

#include <stdio.h>
#include <unistd.h>
#include <thread>
#include <stdlib.h>
#include <string.h>

void my_print1(int i) {
    printf("Thread1,ID: %d, value:%d\n",pthread_self(), i);
}

void my_print2(int i) {
    printf("Thread2,ID: %d, value:%d\n",pthread_self(), i);
}

void thread_run1() {
  int i = 0;
  while (1) {
    my_print1(i);
    i++;
    sleep(1);
  }
}

void thread_run2() {
  int i = 0;
  while (1) {
    my_print2(i);
    i++;
    sleep(1);
  }
}

int main() {
  std::thread t1(thread_run1);
  std::thread t2(thread_run1);

  printf("main thread\n");
  t1.join();
  t2.join();
  return 0;
}

编译

g++ -g test_thread_gdb.cpp -o test -pthread

命令

ps -aux | grep test
gdb attach pid  // pid为你的test的进程id

1. 线程信息

info thread  // 查看全部线程
thread 2      // 切换到线程2
bt               // 

2. 设置断点

b thread_run1  // thread_run1设置断点
i b  // 查看当前断点 包括编号

3. 单步调试

n 1 
n
next    // 以上可用于持续的单步调试
print i  // 打印变量 i 的值

4. 打印上下文代码

l
list  // 查看代码上下文

5. 变量打印

display i // 之后每次单步调试都会显示i的变量值

6. 观察变量

watch i // 观察i的值,当有变化时,停止

7. 输出到文件

set print elements 0
来关闭掉显示长度限制。再使用print 就可以完全显示变量内容了。
set print repeats 0
对重复的字符进行打印

(gdb) set logging file gdb.log
(gdb) set logging on
(gdb) disassemble
(gdb) set logging off

扩展:https://zhuanlan.zhihu.com/p/594423089

gdb frame

** 参考资料 http://c.biancheng.net/view/8282.html **
任何一个被调用的函数,执行时都会生成一个存储必要信息的栈帧。对于 C、C++ 程序而言,其至少也要包含一个函数,即 main() 主函数,这意味着程序执行时至少会生成一个栈帧。
frame 命令的常用形式有 2 个:

根据栈帧编号或者栈帧地址,选定要查看的栈帧

语法格式如下:

(gdb) frame your_spec

该命令可以将 your_spec 参数指定的栈帧选定为当前栈帧。your_spec 参数的值,常用的指定方法有 3 种:

  1. 通过栈帧的编号指定。0 为当前被调用函数对应的栈帧号,最大编号的栈帧对应的函数通常就是 main() 主函数;
(gdb) frame 0
  1. 借助栈帧的地址指定。栈帧地址可以通过 info frame 命令(后续会讲)打印出的信息中看到;
  2. 通过函数的函数名指定。注意,如果是类似递归函数,其对应多个栈帧的话,通过此方法指定的是编号最小的那个栈帧。

查看程序栈内容

设置好栈帧后,就可以使用命令查看程序栈的内容
info frame 查看当前程序栈信息
info args 查看当前程序栈的参数
info locals 查看当前程序栈的局部变量
info registers 查看寄存器的值
info all-registers 查看寄存器的值(包括浮点寄存器)
info catch 查看程序栈的exception handlers

常见问题

问题1: No symbol table is loaded. Use the "file" command
使用list命令时,报以上错误
编译时加上 -g参数
g++编译

g++ -g test.cpp -o test

bazel编译

bazel build ... --compilation_mode=dbg
posted @ 2022-06-08 17:37  流了个火  阅读(1550)  评论(0编辑  收藏  举报
►►►需要气球么?请点击我吧!►►►
View My Stats