linux调试笔记

文章目录

  • 基本
    • 启动调试与附加进程
    • 断点
    • 程序运行控制
    • tui模式
    • 查看堆栈与变量
    • 监视变量
    • 多线程调试
  • 扩展
    • 自定义跳转命令
    • 解析自定义类型
    • 禁用动态库自动加载
    • 设置源码路径
    • 断点时执行命令
    • gdbserver远程调试
  • gdb脚本
  • QtCreator调试
  • Linux下处理编译、运行时的一些问题
    • undefined symbol问题-`nm`命令
    • 系统环境变量问题

基本

启动调试与附加进程

  • 转到程序运行目录下,执行gdb ./程序名即可
    只是附加了一个调试文件,并没有启动这个程序,需要执行run命令——简写r,来运行这个程序;启动前可以set args arg1 arg2 ...来设置启动参数,运行中可以通过show args查看启动参数
  • 如果要附加运行,可以执行gdb attach 进程ID即可;脱离程序可以执行detach
  • 在linux查看进程ID——一种方式直接在任务管理器里查看(如果是图形界面);或者在命令行里打印进程查找ps -ef | grep 程序名
  • 调试core文件——linux下的core文件就像windows下的dump文件,用于分析崩溃,命令很简单gdb ./程序名 core文件名,之后就可以用普通gdb调试用的命令查看切换堆栈、查看变量等
  • core文件的生成
    1. 首先去除生成core文件大小限制。只需要解除linux下core文件大小限制ulimit -c unlimited
      ulimit是限制linux下每个用户使用系统资源的命令,比如core文件大小限制、最大内存、最大堆栈大小、单次CPU最大占用时间等等,可以通过ulimit -a来查看当前所有的限制;在命令行中设置ulimit只是临时的,想要永久生效,需要修改/etc/security/limits.conf文件,如下:
    *               soft    core            unlimited
    root            hard    core            unlimited
    
    1. 然后运行程序直到崩溃,就会在/tmp文件夹下生成core开头的文件
      设置core文件生成的路径,可以修改/proc/sys/kernel/core_pattern文件内容为/tmp/core-%p-%e-%t;或者执行 sysctl -w kernel.core_pattern=/tmp/core-%p-%e-%t

    %p -进程ID
    %e -进程名称
    %t -生成时间
    %u -用户ID
    %g -组ID
    %s -导致coredump生成的信号名
    %h -主机名

断点

断点的命令是break,简写b,基本用法:

  • info break,查看当前所有的断点信息——其实info(简写i)本身还可以搭配其他指令,也是查看信息的意思,比如info thread查看当前所有线程信息
  • break line,在行号处打断点
  • break function,在当前文件指定函数开头处断点。需要注意的是,gdb会为所有该名称的函数打上断点,可以b class::method给指定类的方法打上断点,或者b function(int)给指定重载形式的函数打断点;更进一步,有rb 正则表达式来给正则表达式能够匹配上的函数打上断点
  • break file:line,在指定文件指定行断点
  • break file:function,在指定文件指定函数断点
  • break +/-offset,在当前行前/后几行位置处断点
  • break ... if cond,设置条件断点
  • tbreak ...,设置一个临时的断点(临时断点断过一次后就会失效)
    通过i b查看所有断点,可以
  • enable 断点编号,启用断点
  • disable 断点编号,禁用断点
  • delete 断点编号,删除断点,简写d

程序运行控制

  1. 单步(step over)——next单步执行,简写n;执行一次n之后可以直接enter继续执行n命令,因为gdb里没有输入命令就回车会执行上一次一样的命令
  2. 步进(step into)——step单步进入,简写s
  3. 运行到——until,执行到指定行
  4. 跳出当前函数——finish,继续执行完当前函数退出到堆栈上一级暂停
  5. 返回——return,直接跳出当前函数,不执行剩余内容
  6. 继续运行与中断——continue(简写c)继续运行程序;ctrl+c中断程序运行

tui模式

因为设置断点与单步执行,只能看到当前运行行附近几行,可以通过list命令显示更多行(默认显示10行),用法如下:

  • list lineNo,显示行号附近行
  • list file:lineNo,显示文件的第几行附近行
  • list function,显示当前文件中函数附近代码
  • list file:function,显示指定文件中函数附近代码
  • list from,to,显示从from到to的代码
    即使通过list多显示几行,仍然非常不友好,可以启用gdb的tui(text user interface)模式,只需要在启动gdb的时候加上-tui参数;或者在运行的时候ctrl+x+a进入,界面如下:
    在这里插入图片描述

查看堆栈与变量

  1. 查看堆栈命令backtrace(简写bt),显示的堆栈前会显示每一帧的帧号
  2. frame 帧号切换帧,简写f 帧号up/down n基于当前帧上下切换帧
  3. info信息——info args查看当前函数调用参数,info locals查看函数局部变量,info registers查看寄存器,info mem查看内存
  4. print命令——打印变量值,p top打印上一个变量的值
  5. whatis val,打印变量val的类型。有意思的是,在模板函数/模板类的成员函数断点断住后,whatis T也能获取到当前的泛型类型
  6. ptype val,打印变量类型,会打印出类型的定义,包括成员变量和成员函数等,更详细
  7. print val,打印显示变量的值;需要注意的是,print param=val可以在调试过程中修改变量的值;print *this输出当前对象各成员变量值

监视变量

  1. display val,可以在中断过程中始终显示某一个变量;info display查看当前有哪些实时display的变量;undisplay,取消指定序号变量的实时显示
  2. watch val,监视某一个变量,当发生变化时中断,即数据断点

多线程调试

  • info thread,查看都有哪些线程
  • thread index,切换到指定线程

扩展

自定义跳转命令

如果想要实现VS中一样的,拖动当前运行位置到其他行并执行,gdb里也有jump命令(简写j),使用格式如下:

  • jump 行号——跳转到指定行
  • jump +10——跳转到当前代码下面10行处
  • jump *0x12345678——跳转到0x12345678地址的代码处,地址前要加*
    跳转有两个注意事项——
  1. jump到指定位置,中间这些代码是直接跳过的(与VS是一样的)
  2. jump到指定位置后,如果该位置没有断点,是会继续往下运行的
    鉴于以上第2点,如果我们需要跳转到指定行后暂停,然后我们单步调试的话,需要先打一个临时断点
(gdb)tbreak lineNum
(gdb)jump lineNum

以上跳转命令能否一次到位?gdb支持脚本,可以定义如下脚本,这样在gdb中可以使用自定义命令,move了

define move
  if $argc != 1
    help move
  else
    tbreak $arg0
    jump $arg0
  end
end

document move
  go to specific line
  usage:move line_number
end

以上:

  1. define定义了自定义命令,argc代表输入参数个数,arg0~arg9代表输入参数(最多十个),其中可以使用gdb的命令以及自定义命令,语法与一般语言差不多,if...else...endwhile...end等,continue对应loop_continue,break对应loop_break
  2. document代表给命令写文档,当执行help 命令的时候会输入这里面的内容
  3. 将以上脚本写入/etc/gdb/gdbinit文件即可在gdb启动的时候加载

解析自定义类型

在gdb中打印std::map,std::vector以及QString等自定义类型非常不友好,显示如下:

是否有方法优化下显示?

禁用动态库自动加载

gdb启动调试的时候,因为要加载的动态库符号表太多了,所以附加上去会卡很久,禁用启动时自动加载动态库符号表,等到需要时再手动加载可加速启动速度;另外,按需加载动态库符号表的话,使用步进命令也会快得多,因为原来步进需要查找进入的函数在哪个动态库里,现在禁用自动加载了之后,本来加载的符号表就不多,查找自然很快

  1. 禁用方式
    /etc/gdb/gdbinit文件里,加上如下设置即可:
set auto-solib-add off

然后附加调试之后,可以info sharedlibrary查看加载的动态库,Sym列为No的表示未加载(Read列标*的表示没有调试信息)
在这里插入图片描述

  1. 如何手动加载
  • 执行bt查看当前堆栈,看下是当前是哪个动态库符号表未加载导致看不到

  • 然后使用add-symbol-file命令加载该动态库文件到From内存地址即可
    在这里插入图片描述

  • 接下来再bt就可看到这个动态库的符号了

  1. 加载动态库符号还可以直接执行sharedlibrary regex(或者shared regex),按照正则表达式regex匹配需要加载的动态库

设置源码路径

调试时之所以能显示源代码,是因为查找到了源代码位置;如果调试时找不到源码,那可能是因为没有设置源码路径

  • show directory,显示当前源码路径,默认会带cdircwdcdir表示编译路径,cwd表示当前工作路径
  • directory path,添加path到源码查找路径路径,如果直接directory为空,则会清除当前所有的源码路径,但会加上默认的cdir,cwd

断点时执行命令

  1. 先下断点——b xxx
  2. 然后可以通过i b查看刚刚下的断点的编号
  3. 然后可以commands 编号来给指定编号的断点设置执行的命令,比如
    在这里插入图片描述

以上给断点2设置了命令,当命中断点2时,会打印一个“hide”字符串到命令行,然后continue(也就是继续程序的执行),这样像界面刷新一类频繁调到的函数我们可以不中断直接continue,又可以知道是否调过这个函数,甚至打印调用时候它的局部变量等信息
4. 如上处理后,如果输出内容太多的话,会触发gdb的分页显示,需要用户enter继续显示或者q退出显示,这样实际断点还是会中断,可以在/etc/gdb/gdbinit中设置关闭分页显示确认(也就是直接默认就显示,不需要确认)——set pagination off

gdbserver远程调试

  1. 远端运行gdbserver监听端口
    • 没有gdbserver则sudo apt install gdbserver即可
    • gdbserver IP:端口 程序名-监听指定IP主机,监听端口,IP可省略
    • 或者gdbserver IP:端口 --attach PID-附加到指定进程
  2. 本地端连接调试
    运行编译好的gdb,执行target remote IP:端口
  3. 像本地一样gdb调试
  4. 交叉编译gdb
    如果要调试的目标主机架构与本机不一致,本地需要交叉编译,生成目标架构的gdb程序
    • 下载gdb源码
    • 编译对应架构平台的gdb程序——解压源码,进入源码根目录,mkdir build && cd build && ../configure --target=aarch64-linux-gnu && make -j4,其中target参数指定调试目标平台(aarch-linux-gnu为ARM,mips为MIPS);--host指定编译出来的gdb将要运行于哪个平台,不需要指定,因为就是运行于当前平台(如果编译gdbserver,因为要运行于待调试的目标平台,要指定这个host参数)
    • 交叉编译这一点暂未验证

gdb脚本

gdb支持自己的脚本(调用gdb命令或者自定义的命令),也支持一些脚本语言(比如python),还可以通过shell命令调本地命令行,可以通过脚本的方式把一些重复繁琐的调试任务自动化
有待摸索…

QtCreator调试

Qt的调试基本和VS是一样的F5运行,F10步进,F11是step into,shift F11跳出等,不做赘述

  1. 禁用启动调试时自动加载所有符号文件
    在这里插入图片描述

修改~/.gdbinit文件,禁用自动加载符号,Qt Creator启动会读取这个配置文件
2. 手动加载用到的动态库符号
在这里插入图片描述

  • 未加载符号的动态库,符号列显示No,release版本也可以加载符号,只不过符号类型列会显示buildid(debug版本的是plain),plain表示动态库中就包含了符号的定义,而buildid表示动态库so文件中的符号是个链接到外部的引用,实际符号文件已经剥离
  • 未加载符号的话,断点打不上,断点上会显示一个沙漏(意思是等待未来加上符号后生效),正常是一个纯粹的红点
    在这里插入图片描述
  1. 拷贝源码到指定搜索路径
    未加载符号时中断程序,堆栈是看不到任何信息的,可以右键“创建完整回溯”,查看对应线程堆栈底下哪个动态库符号未识别,加载这个动态库符号,就能看到更多的堆栈的信息,继续以上步骤加载其他动态库符号就能将堆栈完整显示出来
    在这里插入图片描述

加载了动态库符号后,堆栈的帧可能仍然是灰色的,说明源码没找到,可以双击帧在编辑区查看对应源码路径,然后把源码拷贝到这个路径下(或者创建软链接映射过去):
在这里插入图片描述

需要注意的是,
1)这个路径如果太长可能显示不全,需要鼠标移动到堆栈的帧上悬停一会查看完整路径
2)路径中的每一个层级都必须要有,比如如果有个路径是/home/cbb-xn/work/cbbcode/GLD/../Glodon/src/GLD/Qt/ThemeEngine/GMPRibbonStyle.cpp,源码拷贝到路径后实际为/home/cbb-xn/work/cbbcode/Glodon/src/GLD/Qt/ThemeEngine/GMPRibbonStyle.cpp,路径/home/cbb-xn/work/cbbcode下没有GLD文件夹(这个是编译过程中生成的,直接clone下来的源码是没有这个文件夹的),但是这里源码路径里有,也必须创建一个,不然即使把源码拷贝到正确的路径也找不到
4. 设置定位器
源码拷贝到指定的搜索路径下,只是从堆栈中点击可以跳转过去,无法直接在qtcreator中搜索对应文件直接打开。其实qtcreator的Locator本身就像内置的一个everything一样,可以搜索打开文件,比如简单的用法可以通过点击Locator的编辑框,查看上方的提示——a 文件名定位所有项目中的文件,c 类名定位所有项目中的c++类,m 函数名定位所有项目内所有函数
在这里插入图片描述

定位器本身可以扩展,点击编辑框左侧的放大镜,在上拉框里点击Configure...,然后增加自己的定位器。我增加了GMP、GLD、GCP、GGDB、Qt等源码的路径,并设置搜索搜索前缀为a,这样输入a 文件名后不仅能搜索当前所有项目下所有文件,还能搜索外部GMP,GCP,GLD,GGDB,Qt的源码文件直接打开
在这里插入图片描述

Linux下处理编译、运行时的一些问题

undefined symbol问题-nm命令

==>一般在编译时就会报出来,但是如果动态库是通过QLibrary在程序中显式加载(不是通过代码依赖隐式加载)的话,可能会在运行时报这个错,可以在QLibrary::load失败后面加一句QLibrary::errorString来获取加载时的错误,就可以看到undefined symbol之类的提示了
==>这个问题原因如提示所言,是符号未定义,

  1. 如果是同一个模块内,调用出现undefined symbol,检查是否调用的函数未定义
  2. 如果是跨模块调用出现undefined symbol,可以使用nm命令,判断是调用方的问题,还是依赖库的问题——nm命令用来查看动态库导出了哪些符号,如果导出符号中确实没有指定符号,则是依赖库问题(该文件没有编译,如没有该文件、cmake中指定了删除该文件、cmake中指定了不编译该文件等;或者该文件编译了但是没有正确导出该函数,如该函数cpp中未实现、该函数没有export导出等);如果动态库导出符号中有该符号,则是调用方使用不正确(cmake中没有配置依赖库等)
    如A模块调B模块的func函数,执行命令nm -D B.so | grep func,类似如下(一般T表示符号在文本区,正常,U的话是未定义,则不正常):
    在这里插入图片描述

或者用objdumpreadelf也是差不多的作用:
在这里插入图片描述
在这里插入图片描述

注:应该还有一个链接问题,如果调用库调用方法和依赖库的符号导出都没问题,还有可能是调用库压根调的不是这个依赖库(比如本地有多个版本,调用的时候找错了),可以ldd -s xxx.so,可以列出动态库依赖哪些动态库,以及其实际找的链接的是哪个动态库

系统环境变量问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/284152.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C# 技术】 C# 常用排序方式——自定义数据排序

C# 常用排序方式——自定义数据排序 前言 在最近的项目中经常会对C#中的数据进行排序,对于基本数据类型,其排序方式比较简单,只需要调用内置算法即可实现,但对于自定义数据类型以及自定义排序规则的情况实现起来就比较麻烦&…

.NET Core中灵活使用反射

前言 前段时间有朋友问道一个这样的问题,.NET Core中如何通过Attribute的元数据信息来调用标记的对应方法。我第一时间想到的就是通过C#反射获取带有Custom Attribute标记的类,然后通过依赖注入(DI)的方式获取对应服务的方法并通…

D47|动态规划-子序列part2

392.判断子序列: 初始思路: 左为判断公共子序列,右为判断子序列,感觉代码完全可以套用,如果公共子序列的长度是较短的字符串的长度的话即输出true,如果不是即输出false。 class Solution {public boolean…

【openlayers-5】地图点、线、面等要素添加

1、添加点 //创建一个点 var point new ol.Feature({geometry: new ol.geom.Point([117.2, 35.8] ),}) //设置点的样式信息 point.setStyle(new ol.style.Style({//填充色fill: new ol.style.Fill({color: rgba(255, 255, 255, 0.2),}),//边线颜色stroke: new ol.style.Strok…

Unity坦克大战开发全流程——游戏场景——游戏界面——设置界面复用

游戏场景——游戏界面——设置界面复用 先将开始场景当中的设置面板复制过来 由于设置面板挂载的脚本都是相同的,在BeginScene中关闭设置面板时不会报空,而在GameScene中关闭设置面板时却会报空,这是因为监听事件中的单例模式调用的实例是Beg…

68内网安全-域横向PTHPTKPTT哈希票据传递

今天讲PTH&PTK&PTT, PTH(pass the hash) #利用 lm 或 ntlm 的值进行的渗透测试 PTT(pass the ticket) #利用的票据凭证 TGT 进行的渗透测试 用的Kerberos 协议 PTK(pass the key) #利用的 ekeys aes256 进行的渗透测试 lm加密算法是2003以前的老版&…

动画墙纸:将视频、网页、游戏、模拟器变成windows墙纸——Lively Wallpaper

文章目录 前言下载github地址:网盘 关于VideoWebpagesYoutube和流媒体ShadersGIFs游戏和应用程序& more:Performance:多监视器支持:完结 前言 Lively Wallpaper是一款开源的视频壁纸桌面软件,类似 Wallpaper Engine,兼容 Wal…

按照故障码类型分类的API接口

随着汽车的普及,车辆故障也成为了一个不可忽视的问题。对于车主来说,及时了解故障码的含义以及解决方案十分重要。挖数据平台为解决这一问题,提供了一套按照故障码类型分类的API接口,用于查询车辆故障、故障码适用品牌以及提供相应…

当 Redis 遇上 Serverless

亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术,观点,和项目,并将中国优秀开发者或技术推荐给全球云社区。如果你还没有关注/收藏…

杂文月刊投稿方式论文发表要求

《杂文月刊》是由国家新闻出版总署批准的正规文学类期刊。主要内容取向:杂文、散文、小说、诗歌、漫画、文学评论、艺术评论、戏剧文化、地方文化、非遗文化、美学艺术、教育等历史、文化、文学、艺术类的文章。是广大专家、学者、教师、学子发表论文、交流信息的重…

docker 在线安装mysql 8.0.21版本

1、拉取mysql 8.0.21版本镜像 2、查看镜像 docker images 3、在宿主机 /usr/local/mysql 下的 conf 文件夹下,创建 my.cnf 文件,并编辑内容 [mysql] default-character-setutf8 [client] port3306 default-character-setutf8 [mysqld] port3306 se…

Win10取消开机密码

1、首先确认你的 Windows 系统帐户中添加了密码。您可以在设置 - 帐户 - 登录选项中查看电脑是否有添加本机密码。 2、按 WinR 打开运行窗口,在窗口中输入 netplwiz 并按确定。 3、在弹出的用户帐户控制中,我们可以看到当前已添加密码的帐户。把要使用本…

可拖拽流程图组件开发

效果 说在前面 流程图在技术领域是一种常见的可视化工具,用于展示系统、应用或业务流程的各个步骤以及它们之间的关系。它们可以帮助开发人员和项目团队更好地理解和规划复杂的流程,从而提高工作效率和准确性。但是,传统的静态流程图有时无法…

【Vulnhub 靶场】【Looz: 1】【简单】【20210802】

1、环境介绍 靶场介绍:https://www.vulnhub.com/entry/looz-1,732/ 靶场下载:https://download.vulnhub.com/looz/Looz.zip 靶场难度:简单 发布日期:2021年08月02日 文件大小:2.1 GB 靶场作者:mhz_cyber &…

侯捷C++ 2.0 新特性

关键字 nullptr and std::nullptr_t auto 一致性初始化:Uniform Initialization 11之前,初始化方法包括:小括号、大括号、赋值号,这让人困惑。基于这个原因,给他来个统一,即,任何初始化都能够…

文献速递:人工智能医学影像分割---高效的MR引导CT网络训练,用于CT图像中前列腺分割

01 文献速递介绍 如今,根据国家癌症研究所的报告,美国约有9.9%的男性患有前列腺癌。1 此外,根据美国癌症协会的数据,预计2019年将有174,650个新病例被诊断出前列腺癌,与此同时大约有31,620名男性将死于前列腺癌。因此…

YOLOv8改进 | 检测头篇 | ASFF改进YOLOv8检测头(全网首发)

一、本文介绍 本文给大家带来的改进机制是利用ASFF改进YOLOv8的检测头形成新的检测头Detect_ASFF,其主要创新是引入了一种自适应的空间特征融合方式,有效地过滤掉冲突信息,从而增强了尺度不变性。经过我的实验验证,修改后的检测头…

计算机组成原理——中央处理器cpu21-40

18、某计算机的指令流水线由4个功能段组成,指令流经各功能段的时间(忽略各功能段之间的缓存时间)分别为90ns、80ns、70ns和60ns,则该计算机的CPU时钟周期至少是多少。A A、 90ns     B、 80ns C、 70ns     D、 60ns …

匿名短信发送网站搭建教程

​​​​​​​​​​​​​​详细教程链接 软件功能: 主要功能是可以去帮助一些用户发送匿名短信,不带你的真实号码,比如热恋中闹脾气的小情侣,短信的成本0.1,卖别人假设价格设置1元/条,利润就有80% 当…

Debezium日常分享系列之:Debezium 通知

Debezium日常分享系列之:Debezium 通知 一、概论二、Debezium通知格式三、Debezium 有关初始快照状态的通知四、Debezium 有关增量快照进度的通知五、启用 Debezium 通知六、访问 Debezium JMX 通知七、自定义通知渠道八、应用案例 一、概论 Debezium 通知提供了一…
最新文章