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 moveif $argc != 1help moveelsetbreak $arg0jump $arg0end
enddocument movego to specific lineusage: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.mzph.cn/news/582633.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android ImageView如何使用.svg格式图片

我们知道imageview常用的图片格式是.jpg/.png或者drawable里的部分.xml文件。但有时UI会给过来.svg格式的文件,下面讲解如何使用.svg格式图片文件 step1:AS点击File -> New -> Vector Asset step2:选中要使用的.svg文件,按需要命名和调整&#x…

Mysql For Navicate (老韩)

Navicate创建数据库 先创建一个数据库;然后在数据库中创建一张表;在表格当中填入相应的属性字段;打开表, 然后填入相应的实例字段; – 使用数据库图形化App和使用指令来进行操作各有各的好处和利弊; 数据库的三层结构(破除MySQL神秘) 所谓安装Mysql数据库, 就是在主机安装一…

231227-9步在RHEL8.8配置本地yum源仓库

Seciton 1:参考视频 RHEL8配置本地yum源仓库-安徽迪浮_哔哩哔哩_bilibili Seciton 2:具体操作 🎯 第1步:查看光驱文件/dev/sr0是否已经挂载?此处已挂在 [lgklocalhost ~]$ df -h 🎯 第1步:查看…

在处理金额时用long还是BigDecimal,所引发的讨论

亲爱的小伙伴们,由于微信公众号改版,打乱了发布时间,为了保证大家可以及时收到文章的推送,可以点击上方蓝字关注测试工程师成长之路,并设为星标就可以第一时间收到推送哦! 周末在闲逛的时候 无意间看到一个…

Vue3数据交互axios

我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 本…

自定义html5中日期选取器的样式

自定义html5中日期选取器的样式 1. 前言1.1 关于 h5 的新特性1.2 使用浏览器 2. html5中日期选取器默认样式3. 自定义日期样式3.1 简单定义3.2 花式样式定义 4. 改变日期格式5. 参考 1. 前言 1.1 关于 h5 的新特性 可看下面的文章 HTML5 新特性之HTML5 的输入(inpu…

设计模式(4)--对象行为(6)--备忘录

1. 意图 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 这样以后可以将该对象恢复到原先保存的状态。 2. 三种角色 原发器(Originator)、备忘录(Memento)、负责人(Caretaker) 3. 优点 3.1 保持了封装边界。屏蔽了原发器的…

融汇贯通 —— 2023年技术与心灵的双重成长旅程

当我们站在2023年的岁末,回望这一年赋予我们的经历和挑战,心中涌动的感慨与启示像朝日初升的光芒,照亮脚下的路,亦照见心中的路。在此,我想分享几个方面的感悟和成长,愿它们能有所触动,成为您前…

一招搞定找不到vcruntime140_1.dll无法继续执行此代码

在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“找不到指定的模块”或“无法加载某某.dll文件”。而其中一个常见的问题就是vcruntime140_1.dll丢失。那么,vcruntime140_1.dll到底是什么?为什么会出现丢失的情…

免费代理IP:如何获取?有什么风险?有什么性价比高的代理IP?

您可能已经知道,生活中没有什么是真正免费的。代理IP虽然用于保护隐私,但也有非常多代理都是免费的,这对于不想使用付费替代方案的用户来说是一个巨大的优势。在这篇博文中,我们将深入研究免费代理的细节,并评估这把双…

【快速全面掌握 WAMPServer】04.人生初体验

网管小贾 / sysadm.cc 我们在前面的教程中为小伙伴们详细地介绍了 WampServer 的安装方法,相信大家对于如何安装应该已经有了一个比较完全的掌握。 在完全掌握安装方法之后,我们还可以更加便捷地使用我为大家提供的一键安装批处理程序来快速搞定安装部署…

java设计模式学习之【迭代器模式】

文章目录 引言迭代器模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用迭代器示例代码地址 引言 想象一下,你在一个书店里浏览各种书籍。你可能会从头到尾查看每一本书,或者可能跳过一些不感兴趣的部分。在这个过程中,你实…

K8S 全局架构图 —— 筑梦之路

kube-apiserver: Kubernetes API 服务器验证并配置 API 对象的数据, 这些对象包括 pods、services、replicationcontrollers 等。API 服务器为 REST 操作提供服务,并为集群的共享状态提供前端, 所有其他组件都通过该前端进行交互。…

3DMAX 中的 VR 渲染器如何设置局部区域渲染?

3DMAX 中的 VR 渲染器如何设置局部渲染? 首先我们要得打开渲染设置,在3damx里按F10,调出渲染设置。选定渲染器为Vary渲染器: 设置VR的局部渲染,需要打开帧缓冲,我们在V-ary项下,打开帧缓冲(点击…

使用 uiautomatorviewer 获取元素的定位信息

1. 使用 adb 连接设备(真机或模拟器) 连接夜神模拟器:adb connect 127.0.0.1:62001 连接MuMu模拟器:adb connect 127.0.0.1:7555 2. 打开 uiautomatorviewer 在 android-sdk --> tools 目录,找到 uiautomatorviewer.bat,双击打开: 3. 连接指定的设备(以连接夜神…

使用cmake配置matplotlibcpp生成VS项目

https://gitee.com/feboreigns/matplotlibcpp 这篇文章需要一些cmake基础,python基础,visualstudio基础 准备环境 注意如果在VS平台使用必须要手动下载python,不能使用conda里面的,比如3.8版本,因为conda里面没有py…

Checkpoint 执行机制原理解析

在介绍Checkpoint的执行机制前,我们需要了解一下state的存储,因为state是Checkpoint进行持久化备份的主要角色。Checkpoint作为Flink最基础也是最关键的容错机制,Checkpoint快照机制很好地保证了Flink应用从异常状态恢复后的数据准确性。同时…

Zblog主题模板:ZblogitseanPage博客主题模板

zblog主题模板:ZblogitseanPage博客主题模板 ZblogitseanPage博客主题模板主要是以文字内容为主导,将页面的设计杂乱的图片和元素进行最小化或者去除,从而使整个页面更加简洁、清晰,突出信息的呈现。 下面介绍一下zblog主题模板:Z…

Docker自建私人云盘系统

Docker自建私人云盘系统。 有个人云盘需求的人,主要需求有这几类: 文件同步、分享需要。 照片、视频同步需要,尤其是全家人都是用的同步。 影视观看需要(分为家庭内部、家庭外部) 搭建个人网站/博客 云端OFFICE需…

配置代理解决跨域(CORS)问题

一、跨域 ? 我们在完成前后端分离项目时(VueSpringBoot),有很多人会遇到跨域问题(CORS)。 跨域(Cross-Origin Resource Sharing,CORS)是浏览器的一项安全功能&#xff…