段错误的调试方法(printf输出、GDB)

参考:段错误产生原因及简单的调试方法
参考:如何解决段错误

参考:C语言gdb调试之精髓(常用命令、多进程、多线程、程序日志)
网址:https://www.bilibili.com/video/BV1ei4y1V758?from=search&seid=4037367131025904962
以及:http://www.freecplus.net/b72113dda88a43b48728e0552fd8a74c.html

参考:Linux的调试器–GDB等

目录

  • 段错误产生原因
    • 1.访问不存在的内存地址
    • 2.访问只读的内存地址
    • 3.访问系统保护的内存地址
    • 4.栈溢出
    • 5.数组越界
  • 两种方法快速定位断错误
    • 使用printf定位
    • 使用GDB定位
  • GDB详细学习
    • 概念
    • 安装GDB增强工具 (gef)
      • 简单的安装方法(我的Linux是Ubuntu18.04)
    • 尝试使用gdb进行小程序的调试
    • GDB的命令
    • GDB的基本命令
  • GDB的简单使用
    • 0.编译hello.c
    • 1.启动GDB
    • 2.下断点
    • 3.让程序运行起来
    • 4.此时需要往下运行
    • 5.查看运行中程序的变量
    • 6.运行时程序的打印会在最上面显示
    • 7.退出gdb
    • 8.补充一点(设置参数)
  • GDB多进程调试
    • 1.编译
    • 2.启动gdb
    • 3.下断点(我们以man函数为例)
    • 4.运行
    • 5.选择是走子进程还是父进程
  • GDB多线程调试
    • 1.编译
    • 2.开始调试
    • 3.选择线程

段错误产生原因

1.访问不存在的内存地址

如下面代码,ptr没有申请空间就直接拷贝数据:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{char *ptr = NULL;//This is the wrong implementation:strncpy(ptr, "abc", 3);//ptr没有申请空间就直接使用//The right way://ptr = (char *)malloc(sizeof(char) * 10);//memset(ptr, 0, 10);//strncpy(ptr, "abc", 3);return 0;
}

2.访问只读的内存地址

错误做法:往字符串常量空间拷贝新的数据

#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{char *ptr = "test";strcpy(ptr, "TEST1");return 0;
}

3.访问系统保护的内存地址

如:

#include <stdio.h>int main(int argc, char *argv[])
{int *ptr = (int *)0;*ptr = 100;return 0;
}

4.栈溢出

如:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{main(argc, argv);
}

5.数组越界

以下程序能正常执行,但执行完以后中断就显示 (core dumped)

#include <stdio.h>int main()
{int a[3];int i=0;for(i=0;i<10;i++){a[i] = i;}for(i=0;i<10;i++){printf("a[%d]=%d\n",i,a[i]);}return 0;
}

两种方法快速定位断错误

使用printf定位

这个是看似简单但是十分有效的调试方式,也许是程序员用的最多的调试方式。简单来说,就是在程序的重要代码附近加上printf输出信息,这样可以跟踪并打印出段错误在代码中最可能出现的位置。

使用案例:
以下面这段错误代码为例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{char *ptr = NULL;//This is the wrong implementation:strncpy(ptr, "abc", 3);//ptr没有申请空间就直接使用return 0;
}

可以在代码中添加printf调试信息:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{printf("line:%d\n", __LINE__); //单纯打印代码行数char *ptr = NULL;//This is the wrong implementation:printf("line:%d\n", __LINE__); //单纯打印代码行数strncpy(ptr, "abc", 3);//ptr没有申请空间就直接使用printf("line:%d\n", __LINE__); //单纯打印代码行数return 0;
}

编译运行后,会有如下输出:

line:7
line:10
Segmentation fault (core dumped)

通过日志信息,就可以看到strncpy(ptr, “abc”, 3);语句没有被执行,我们就可以根据定位到的位置来排除ptr是否是未分配空间或者分配的空间不足引发的问题。

使用GDB定位

如果程序通过了编译,但是在使用命令 ./ a.out 运行时程序崩溃了,即显示段错误(core dumped),其实系统会在程序崩溃的那一刹那将整个内核的信息记录在一个文件里边,ls 并不会看到这个文件。

使用命令 ulimit -a 打开ulimit这个文件,发现这个文件大小缺省为0,我们手动将这个文件的大小改大一点。使用 命令 ulimit -c 10240 (10240的位置可以是任意一个比较大的数字) ,然后再利用 ./ a.out执行一次。

当再次使用 ls 查看会发现这个 core.3288 文件就存在了 ----(3288表示产生这个文件的进程的 ID)。可以使用命令 file core.3288 ,将core.3288这个文件的具体信息给显示出来,命令最后会显示这个core文件是通过哪个文件产生的(假如是a.out)

PS:file命令是用来探测给定文件的类型。file命令对文件的检查分为文件系统、魔法幻数检查和语言检查3个过程。

接下来来调试分析问题到底出现在哪
使用命令 gdb 产生该core文件的文件名 core文件文件名 比如说 gdb a.out core.11277 然后回车
这个时候就会直接告诉我们问题出在哪个函数哪一行。

需要说明一下的是当前已经进入了调试模式,按 q 退出调试模式,然后就可以vim 我们的源文件根据错误信息修改我们的代码了。

注意:调段错误,编译的时候一定要加入-g选项,要不然在最后显示错误的时候只会显示错的地址,而不会显示错误的具体信息

下边就以一个极其简单的例子来具体显示一下解决段错误的整个过程

在这里插入图片描述
很明显这个程序是有问题的,在FunTest函数中,对NULL进行了解引用,这势必会导致程序崩溃,接下来就来通过这个简单的问题代码来帮助理解怎么解决断错误。

./a.out 执行,问题来了

在这里插入图片描述
使用命令 ulimit -a

在这里插入图片描述
重新运行一下然后再次使用 ls 命令查看,多了一个 core文件
在这里插入图片描述
利用命令 file core.3288查看一下这个文件的具体信息

在这里插入图片描述

然后使用命令 gdb a.out core.3288

在这里插入图片描述

最后 按 q 退出调试模式,然后修改我们的代码就OK了。

总结:
解决段错误主要就是如下几个步骤
step1:gcc -g
step2:ulimit -c 10240
step3 : ./a.out
step4 : gdb a.out core文件

GDB详细学习

概念

程序员写在编写程序的时候不可能是一帆风顺的,gcc编译器可以发现程序代码的语法错误,但不能发现程序的业务逻辑错误,调试程序是软件开发的内容之一。调试程序的方法有很多种,例如可以用printf语句跟踪程序的运行步骤和显示变量的值,本章节介绍一个功能强大的调试工具gdb。

  • 基于命令行的调试方法
  • 所有的调试都是可以进行脚本编写的
  • 能够调试所有架构的代码
  • 有三种调试方法供大家选择
  • GDB支持远程调试,支持与IDA进行联调

安装GDB增强工具 (gef)

一般来说,gdb启动起来就是这样的样子
在这里插入图片描述

  • gdb的一直都非常强大,但是每一步调试,可能有一些要查看的信息,如果每一步都要手动输入命令,未免有点麻烦,所以就出现了插件,把某一些经常要查看的信息每一步都自动帮你显示出来,方便调试

简单的安装方法(我的Linux是Ubuntu18.04)

1、切换到用户权限
2、切换到用户家目录 比如/home/qingmu
3、保持网络通畅,然后只要输入下面的命令 就行

git clone https://github.com/gatieme/GdbPlugins.git ~/GdbPlugins  从git上面下载一些插件
(没有git 可以自行安装)apt-get install git

在这里插入图片描述
克隆完之后,会在你当前目录下面有GdbPlugins 这个文件,并且里面会有这几个文件
在这里插入图片描述

  • 这样就算ok了,里面三个插件,当你想要用某一个插件的时候,只要输入对应命令就行
  • 这里我们主要使用gef 我们执行
echo "source ~/GdbPlugins/gef/gef.py" > ~/.gdbinit 

然后我们启动gdb就行了
在这里插入图片描述
至此gef插件安装完毕

尝试使用gdb进行小程序的调试

gcc -g的选项
gdb gdb-test
一些GDB的命令

GDB的命令

启动方法

本地普通启动 gdb
本地段错误文件启动 gdb core
attch方式启动 gdb
远程启动 gdbserver 0.0.0.0:1234 /path/to/file

启动选项

–symbols < file >
-s < file >
从指定文件中读取符号表
-se < file >
从指定文件中读取符号表信息,并把它们用在可执行文件中
–core < file >
调试时core dump的core文件
–directory < directory>
-d < directory>
加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径
详细的开关可以使用gdb --help

基本命令

set listsize 设置调试中可以查看的行数
set args 10 20 30 40 50 设置程序所需要的参数
path
show paths
save breakpoint name.bp
gdb elf -x name.bp
print § 查看运行数据
print *array@10 查看数组
print file::variable 查看file文件下的variable
x/n、f、u 查看内存
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,跟print 的格式参数相同
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。

GDB的基本命令

在这里插入图片描述

在这里插入图片描述

Linux程序发布流程
确定程序是否存在符号表
readelf -s test-1
生成符号表
objcopy --only-keep-debug test-1 test-1.symbol
生成发布程序
objcopy --strip-debug test-1 test-release
使用符号表进行程序debug
gdb -q --symbol=test-1.symbol --exec=test-release
GDB中暂停/恢复程序运行
断点
条件断点
break if
info breakpoints
delete
disable
enable

观察点
watch 地址
info watchpoints
rwatch

捕捉点
catch event
throw 抛出一个C++的异常 catch throw
catch 捕捉一个C++的异常 catch catch
exec 调用系统调用exev时停止 catch exec
fork 调用系统调用fork时停止 catch fork
load/load libname 载入动态链接库时 catch load / catch load libname

暂停命令
commands bnum
.
.
.
.
end

GDB的简单使用

0.编译hello.c

gcc -g hello.c -o hello

注意:编译的时候一定要加上 -g 选项,使之加入符号表,否贼调试的时候看不到程序的源代码。为了更方便的使用gdb 一定要安装gdb插件gef

以hello.c为例
在这里插入图片描述

1.启动GDB

gdb hello  //hello 是hello.c的可执行文件

在这里插入图片描述

2.下断点

也就是程序运行到哪,我们以man函数为例
也可以指定在哪一行下断点。

b
在这里插入图片描述
info breakpoints 可查看下的断点

3.让程序运行起来

r

在这里插入图片描述
此时程序就运行起来了

4.此时需要往下运行

" n " :执行一条语句,碰到函数会直接运行函数
" s ":执行下一条语句,碰到函数会进入到函数中

5.查看运行中程序的变量

" p "
在这里插入图片描述

6.运行时程序的打印会在最上面显示

在这里插入图片描述

7.退出gdb

q

8.补充一点(设置参数)

如果程序需要传入参数那么 “set args” 在启动gdb之后就可以设置参数
例如我需要传递两个参数:
在这里插入图片描述
中间用空格隔开就可以了

GDB多进程调试

下面以process.c为例

#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t pid = fork();//创建子进程if(pid == -1){perror("fork error");return -1;}else if(pid == 0)//child{printf("i am a child:my pid is %d,my father is %d\n",getpid(),getppid());}else//father{printf("i am a father:my pid is %d\n",getpid());wait(NULL);//等待子进程}return 0;}

1.编译

gcc -g process.c -o process  //一定要加-g 把符号表载入代码

2.启动gdb

gdb process

在这里插入图片描述

3.下断点(我们以man函数为例)

b main

在这里插入图片描述

4.运行

r

在这里插入图片描述

5.选择是走子进程还是父进程

fork()函数
返回值为0:子进程
返回值大于0:父进程
返回值小于0:创建失败
函数运行是他自己是走父进程还是子进程是不确定的,所以我们需要让他按照自己的想法运行,这是我们就按自己的需要是走父进程还是子进程

跟着父进程去运行

set follow-fork-mode parent  

跟着子进程运行

set follow-fork-mode child

设置之后我们就n往下运行就可以进入子进程
在这里插入图片描述

GDB多线程调试

下面以thread.c为例


#include<stdio.h>
#include<pthread.h>void* thread1(void* arg)
{printf("i am thread1,my tid is %u\n",pthread_self());int i=0;int result=0;for(;i<100;i++){result += i;}printf("thread1 result = %d\n",result);return NULL;
}void* thread2(void* arg)
{printf("i am thread2,my tid is %u\n",pthread_self());int i=0;int result=0;for(;i<100;i++){result ^= i;}printf("thread2 result = %d\n",result);return NULL;
}int main()
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,thread1,NULL);//创建线程1pthread_create(&tid2,NULL,thread2,NULL);//创建线程2pthread_join(tid1,NULL);//等待线程1pthread_join(tid2,NULL);//等待线程2 return 0;
}

1.编译

gcc -g thread.c  -o thread -lpthread  //线程库不是内核自带的 要自己加上

2.开始调试

gdb thread

在这里插入图片描述

3.选择线程

线程比较简单 想看哪个线程就把断点下到哪个线程的函数上

跟线程1运行

b thread1

跟线程2运行

b thread2

这里我们跟线程1运行
在这里插入图片描述
这里我们就跟进了线程1了
在这里插入图片描述
可以通过:"info threads"命令来查看正在运行程序中线程信息
其余的就是基本操作了
p 打印想看的数据
x查看对应的内存等等

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

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

相关文章

STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、单总线获取DHT11模块温湿度并通过IIC显示到屏幕(软件IIC)

参考&#xff1a;基于stm32软件IIC的oled显示温湿度 作者&#xff1a;ZPZ DayUp 发布时间&#xff1a; 2021-07-25 20:52:43 网址&#xff1a;https://blog.csdn.net/m0_56197680/article/details/119077076?spm1001.2014.3001.5501 目录软件模拟IIC时序(起始、停止、应答、发…

w ndows7端口在哪里,win7电脑遇到端口被占用的情况该如何查看并将其关闭

Windows7操作系统的酷炫和强大已经深受用户们的喜欢了&#xff0c;这里根大家分享的是教你查看win7电脑端口是否被占用的技巧&#xff0c;端口是我们在进行远程或者打印机等都会遇到的&#xff0c;但是有很多用户会遇到端口被占用的情况&#xff0c;遇到这样的问题首先就要找出…

STM32F103五分钟入门系列(十三)独立看门狗IWDG

参考&#xff1a;STM32F103五分钟入门系列&#xff08;十三&#xff09;独立看门狗IWDG 作者&#xff1a;自信且爱笑‘ 发布时间&#xff1a;2021-07-31 19:50:28 网址&#xff1a;https://blog.csdn.net/Curnane0_0/article/details/119269391?utm_sourceapp&app_version…

android自动软键盘,Android自定义软键盘

MyKeyboardAndroid自定义键盘的使用实现步骤第一步&#xff1a;1、新建一个xml文件夹放在res目录下面&#xff0c;然后新建xml文件:money_keyboard.xml2、然后在XML文件中添加按钮布局&#xff0c;这个布局就是键盘的样子了android:horizontalGap"1dp"android:keyWid…

Bootstrap使用-1

目录 结构&#xff1a;1. 视图函数2. 模板3. 登陆4. 怎样发生的添加模板基础模板提供的block定制基础模板结构&#xff1a; $ tree -I "__pycache*|*.pyc|*.xlsx" -FCL 3 . |-- templates/ | -- h1.html -- test-boostracp.py 1. 视图函数 test-boostracp.py from …

段错误、内存溢出、内存泄漏(区别)、堆溢出、栈溢出

参考&#xff1a;内存泄漏、内存溢出、段错误、堆溢出、栈溢出 作者&#xff1a;焦木白 发布时间&#xff1a;2019-10-22 网址&#xff1a;https://blog.csdn.net/jiaomubai/article/details/102680705?spm1001.2014.3001.5501 目录段错误内存溢出内存泄漏栈溢出堆溢出段错误 …

C语言中指针的地址和内容

参考&#xff1a;C语言中指针的地址和内容 作者&#xff1a;wallying 发布时间&#xff1a;2018-10-18 09:47:54 网址&#xff1a;https://blog.csdn.net/qq_36631580/article/details/89375140?spm1001.2014.3001.5502 #include <stdio.h>//一般用"地址"表示…

android运行过程简书,android 启动过程(一)

1、由init.rc调用 app_main.cpp的main方法&#xff0c;启动zygote进程3、调用AndroidRuntime.cpp 的start方法传递ZygoteInit类路径5、AndroidRuntime.cpp的start方法主要启动java虚拟机跟注册虚拟机&#xff0c;调用ZygoteInit的main方法6、ZygoteInit的main方法主要 把zygote…

回调函数 —— 借助中间通用函数(形参里有函数指针,实现函数注册)调用不同的回调函数 (多态/分层)

回调函数传参&#xff1a;函数指针做函数参数&#xff08;回调函数&#xff09; 目录背景回调函数是实现函数分层且单向依赖的好办法&#xff0c;使用函数指针运行struct结构体回调函数代码更清晰简单理解回调函数Demo其他回调函数博文背景 这是我在实际工作中遇到的问题&…

ESP8266等模块

模块讲解学习视频可参照&#xff1a;https://space.bilibili.com/323745961?spm_id_from333.788.b_765f7570696e666f.1 作者&#xff1a;叁议电子 目录ESP8266 WIFI模块介绍电脑操作ESP8266单片机上网ESP8266 WIFI模块介绍 电脑操作ESP8266 单片机上网

html5在线音乐列表播放器,HTML5列表音乐播放器SMusic

插件描述&#xff1a;一款基于HTML5、Css3的列表式音乐播放器&#xff0c;包含列表&#xff0c;音量&#xff0c;进度&#xff0c;时间&#xff0c;歌词展示以及模式等功能&#xff0c;不依赖任何库SMusic使用方法这是一款基于HTML5以及CSS3的列表式音乐播放器&#xff0c;增加…

unzipping/Users/xq/.gradle/wrapper /dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3-all.zi

unzipping/Users/xq/.gradle/wrapper /dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3-all.zip to /Users/xq/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9 可能是下载时网络原因造成文件缺失 解决方法&#xff1a; cd /Users/xq/.gradle/w…

WIFI小车APP

对应这篇文章&#xff1a;433M射频遥控灯、震动感应灯、WIFI避障小车 目录代码概要activity_contrl.xmlContrlActivity.javaNetUtils.java详细工程代码下载代码概要 activity_contrl.xml <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android&qu…

[Web App]必胜客宅急送产品设计思路介绍[转]

O2O模式是餐饮业在移动消费趋势下主动拥抱互联网的方向&#xff0c;迎合餐饮消费者从以往经验判断为主转变为依靠移动设备、LBS、社交网络进行立体决策的过程。App成为联系消费者和餐饮业的重要纽带&#xff0c;承载着在O2O双向流程的闭环中加深消费者对品牌的认同和感情认知的…

使用Raphael实现html中绘图

2019独角兽企业重金招聘Python工程师标准>>> 首先&#xff1a;下载Raphael的javascript库&#xff1a;http://raphaeljs.com/。或者在html页面<head></head>&#xff1a;如下 <script src"http://www.zfanw.com/blog/raphael.js"></…

vivo手机刷机鸿蒙,捡到vivo手机如何刷机

捡到手机&#xff0c;首先建议归还给失主&#xff01;然后如果需要刷机的话&#xff0c;建议可以清除账户密码&#xff1a;可以尝试在手机关机状态同时按住音量上键以及电源键3到4秒手机出现vivo手机标志后&#xff0c;放开按键进入recovery模式&#xff0c;先选择wipe data/fa…

详解ROM和RAM

参考&#xff1a;详解ROM和RAM 作者&#xff1a;嵌入式实验楼 网址&#xff1a;https://mp.weixin.qq.com/s/FhUxMLeAxkhWe5m-gV_hMw 目录1、只读存储器&#xff1a;ROM2、随机访问存储器&#xff1a;RAM内存是计算系统最重要的元素&#xff0c;因为没有它&#xff0c;计算机就…

深入理解STM32内存管理

更多干货推荐可以去牛客网看看&#xff0c;他们现在的IT题库内容很丰富&#xff0c;属于国内做的很好的了&#xff0c;而且是课程刷题面经求职讨论区分享&#xff0c;一站式求职学习网站&#xff0c;最最最重要的里面的资源全部免费&#xff01;&#xff01;&#xff01;点击进…

html 手写字效果,canvas画布实现手写签名效果的示例代码

最近项目中涉及到移动端手写签名的功能需求&#xff0c;将实现代码记录于此&#xff0c;供小伙伴们参考指摘哦~HTML代码&#xff1a;手写区清除确定CSS样式&#xff1a;.mSign_signMark_box{padding: 15px 15px 26px 15px;}.mSign_signMark_footer{max-width:640px;margin:0 au…

FTP云盘

参考&#xff1a;FTP云盘项目 作者&#xff1a;糯米啊啊 发布时间&#xff1a; 2021-08-19 10:34:05 网址&#xff1a;https://blog.csdn.net/weixin_43732386?spm1001.2014.3001.5509 参考&#xff1a;自制FTP云盘项目 作者&#xff1a;不说话的小脑斧 发布时间&#xff1a; …