Linux基础 30分钟GDB调试快速突破
引言 Linus心灵鸡汤
在*nix开发中有道卡叫gdb调试,不管你怎么搞. 它依然在那丝毫不会松动.今天致敬一个 活着的传奇 Linus Torvalds
Unix 始于上个世纪60年代,在70年代得到了迅猛的发展,
这时候的李纳斯还躺在祖父公寓的摇篮里睡大觉,如果不是后来 Unix 王国自乱阵脚,
出现阵营分裂和法律纠纷,可能 Linux 系统根本都不会出现。真实的情况是,
Unix 浪费了大把的时间和机会,似乎就是为了等待这个大鼻子、头发纷乱的芬兰小子长大,然后一决高下。
李纳斯赢得了自己的时间,他一刻不停的磨练自己的技艺,在清晨的微光中练习算法,
在赫尔辛基的雪山上编译代码,随时随地补充的粮草和武器。
二十一年之后,李纳斯抚着雪亮的刀锋上路了,他要去追寻属于程序员的最高荣耀。[
I simply know better than you, that's why I'm your god.
- - Linus Torvalds
]
前言 gdb 开始调试开始上手
1. 开启core, 采集程序崩溃的状态
首先你跟着我做开启core崩溃状态采集. 可以通过 ulimit -c 查看 如果是0表示没有开启. 开启按照下面操作
su root vi /etc/profile
Shift + G
i
# No core files by default , unlimited is oo
ulimit -S -c unlimited > /dev/null >&
wq! source /etc/profile
上面shell 操作是 在 /etc/profile 最后一行添加 上面设置全局开启 core文件调试,大小不限. 最后 立即生效.
再跟着我做, 因为生成的core文件同名会覆盖. 这里为其加上一个 core命名规则, 让其变成 [core.pid] 格式.
su root vi /etc/sysctl.conf
Shift + G
i # open, add core.pid
kernel.core_pattern = ./core_%t_%p_%e
kernel.core_uses_pid = 1 wq! sysctl -p /etc/sysctl.conf
在 /etc/sysctl.conf 文件中添加系统配置. 后面立即启用. 最后是下面状态表示core启用都搞好了.
(上面是ubuntu 15.10 环境中, 后面测试用的是centos 6.4)
2. 简单接触 GDB , 开始调试 r n p
第一个演示代码 heoo.c
#include <stdio.h> int g_var = ; static int _add(int a, int b) {
printf("_add callad, a:%d, b:%d\n", a, b);
return a+b;
} int main(void) {
int n = ; printf("one n=%d, g_var=%d\n", n, g_var);
++n;
--n; g_var += ;
g_var -= ;
n = _add(, g_var);
printf("two n=%d, g_var=%d\n", n, g_var); return ;
}
我们下面从图说起.(如果用视频说更好,文字和图意义在于查询方便.更简约)
第一个命令 gdb heoo.out 表示 gdb加载 heoo.out 开始调试. 如果需要使用gdb调试的话编译的时候 gcc 需要加上 -g命令.
其中l命令表示 查看加载源码内容. 下面将演示如何加断点.
r 表示调试的程序开始运行.
p 命令表示 打印值. n表示过程调试, 到下一步. 不管子过程如何都不进入. 直接一次跳过.
上面用的s 表示单步调试, 遇到子函数,会进入函数内部调试.
总结一下 . l 查看源码 , b 加断点, r 开始运行调试, n 下一步, s下一步但是会进入子函数. p 输出数据.
到这里gdb 基本会用了. 是不是也很容易. 直白. 小代码可以随便调试了.
看到这里基础知识普及完毕了. 后面可以不看了. 有机会再看. 好那我们接着扯.
正文 第一部分 gdb其它开发中用的命令
开始扯一点, linux总是敲命令操作, 也很不安全. 有时候晕了. 写这样编译命令.
gcc -g -Wall -o heoo.c heoo.out
非常恐怖, heoo.c 代码删除了. heoo.out => heoo.c 先创建后生成失败退出. 原先的内容被抹掉了. 哈哈. 服务器开发, 经验不足, 熟练度不够.自己都怕自己.
1. gdb 其它常用命令用法 c q b info
首先看 用到的调试文件 houge.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /*
* arr 只能是数组
* 返回当前数组长度
*/
#define LEN(arr) (sizeof(arr)/sizeof(*arr)) // 简单数组打印函数
static void _parrs(int a[], int len) {
int i = -;
puts("当前数组内容值如下:"); while(++i < len)
printf("%d ", a[i]);
putchar('\n');
} // 简单包装宏, arr必须是数组
#define PARRS(arr) \
_parrs(arr, LEN(arr)) #define _INT_OLD (23) /*
* 主函数,简单测试
* 测试 core文件,
* 测试 宏调试
* 测试 堆栈内存信息
*/
int main(void) {
int i;
int a[_INT_OLD];
int* ptr = NULL; // 来个随机数填充值吧
srand((unsigned)time(NULL));
for(i=; i<LEN(a); ++i)
a[i] = rand()%; PARRS(a); //全员加double, 包含一个错误方便测试
for(i=; i<=LEN(a); ++i)
a[i] <<= ;
PARRS(a); // 为了错,强制错
*ptr = ; return ;
}
同样需要仔细看下面图中使用的命令. 首先对前言部分加深一些. 看下面
这个图是前言的补充, c跳过直到下一个断点处, q表示程序退出.
在 houge.c 中我们开始调试. 一运行段错误, 出现了我们的 core.pid 文件
通过 gdb houge.out core.27047 开始调试. 马上定位出来了错误原因.
2. 调试 内存堆栈信息
刚开始 print a , 在main中当做数组处理.打印的信息多. 后面在_add函数中, a就是个形参数组地址.
主要看 info args 查看当前函数参数值
info locals 看当前函数栈上值信息, info registers 表示查看寄存器值.
后面查看内存信息 需要记得东西多一些. 先看图
x /23dw a 意思是 查看 从a地址开始 23个 4字节 有符号十进制数 输出.
关于x 更加详细见下面
用gdb查看内存格式:
x /nfu ptr 说明
x 是 examine 的缩写
n表示要显示的内存单元的个数 f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。 u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节 Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, bytes) ptr 表示从那个地址开始
这个命令常用于监测内存变化.调试中特别常用.
3. gdb 设置条件断点
很简单 b 17 if i == 8. 在17行设置一个断点,并且只有i==8的时候才会触发.
4. gdb 删除断点
gdb 删除有d 后面跟断点索引1,2,3..
clear 行数或名称. 删除哪一行断点. 看下面演示
到这里 介绍的gdb调试技巧基本都够用了. 感觉用图形ide,例如vs调试也就用到这些了.
估计gdb调试突破20min过去了.够用了. 后面可以不用看了.
正文 第二部分 gdb 多线程多进程调试
到这里实战中用的机会少了, 也就老鸟会用上些. 这部分可以调试,不好调试. 一般一调估计小半天就走了. 好,那我们处理最后10min.
1. 首先对上面正文第一部分加深 gdb调试宏
首先看上面命令
macro expand 宏(参数) => 得到宏导出内容.
info macro 宏名 => 宏定义内容
如果你需要用到上面gdb功能, 查看和导出宏的话.还需要gcc 支持,生成的时候加上 -ggdb3如下
gcc -Wall -ggdb3 -o houge.out houge.c
就可以使用了. 扩展一下 对于 gcc 编译的有个过程叫做 预编译 gcc -E -o *.i *.c.
这时候处理多数宏,直接展开, 也可以查看最后结果. 也算也是一个黑科技.
2. 开始多线程调试
首先看测试用例 dasheng.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> // 声明一个都用的量
static int _old; // 线程跑的函数
static void* _run(void* arg) {
int piyo = ;
int n = *(int*)arg;
int i; //设置线程分离
pthread_detach(pthread_self()); for(i=; i<n; ++i) {
printf("n=%d, i=%d\n", n, i);
++_old;
printf("n=%d, piyo = %d, _old=%d\n", n, piyo, _old);
} return NULL;
} #define _INT_PTX (3) int main(void) {
int i, rt, j;
pthread_t tx[_INT_PTX]; puts("main beign"); for(i=; i<_INT_PTX; ++i) {
// &i 是有问题的, 但是这里为了测试, 可以乱搞
rt = pthread_create(tx+i, NULL, _run, &i);
if(rt < ) {
printf("pthread_create create error! rt = %d, i=%d\n", rt, i);
break;
}
} //CPU忙等待
for(j=; j<; ++j)
;
puts("end"); return ;
}
编译命令
gcc -Wall -g -o dasheng.out dasheng.c -lpthread
那先看下面测试图
上面 info threads 查看所有运行的线程信息. *表示当前调试的线程.
后面 l _run 表示查看 _run附近代码. 当然还有 l 16 查看16行附近文件内容.
gdb多线程切换 测试如下
thread 3表示切换到第三个线程, info threads 第一列id 就是 thread 切换的id.
上面测试线程 就算你切换到 thread 3. 其它线程还是在跑的. 我们用下面命令 只让待调试的线程跑. 其它线程阻塞.
set scheduler-locking on 开始多线程单独调试. 不用了 设置 set scheduler-locking off 关闭. 又会回到你调试这个, 其它线程不阻塞.
总结 多线程调试常用就这三个实用命令
info threads
thread id
set scheduler-locking on/off
分别是查看,切换,设置同步调试.到这里多线程调试基本完毕了.
3. 开始gdb 多进程调试
首先看 liaobude.c 测试代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> // 声明一个都用的量
static int _old; // 线程跑的函数
static void _run(int n) {
int piyo = ;
int i; ++n;
for(i=; i<n; ++i) {
printf("n=%d, i=%d\n", n, i);
++_old;
printf("n=%d, piyo = %d, _old=%d\n", n, piyo, _old);
}
} #define _INT_PTX (3) int main(void) {
int i;
pid_t rt; puts("main beign"); for(i=; i<_INT_PTX; ++i) {
// &i 是有问题的, 但是这里为了测试, 可以乱搞
rt = fork();
if(rt < ) {
printf("fork clone error! rt = %d, i=%d\n", rt, i);
break;
}
if(rt == ) {
_run(i);
exit(EXIT_FAILURE);
}
} //等待子进程结束
for(;;) {
rt = waitpid(-1, NULL, WNOHANG);
if(rt>=0 || errno==EINTR)
continue;
break;
} puts("end"); // 这里继续等待
for(i=; i<; ++i){
printf("等待 有缘人[%d]!\n", i);
sleep();
} return ;
}
编译命令
gcc -Wall -g -o liaobude.out liaobude.c
其实对多进程调试, 先介绍一个 常用的, 调试正在运行的程序. 首先让 ./liaobude.out 跑起来.
再通过 ps -ef 找到需要调试的进程. 复制进程文件描述符pid.
这时候启动gdb.
attach pid
gdb就把pid那个进程加载进来了. 加载的进程会阻塞到当前正在运行的地方. 直到使用命令控制. 这个功能还是非常猛的.
最后介绍 进程调试的有关命令(需要最新的gdb才会支持). 多进程的调试思路和多线程调试流程很相似.
GDB可以同时调试多个程序。
只需要设置follow-fork-mode(默认值:parent)和detach-on-fork(默认值:on)即可。 设置方法:set follow-fork-mode [parent|child] set detach-on-fork [on|off] 查询正在调试的进程:info inferiors
切换调试的进程: inferior <infer number>
具体的意思有
set follow-fork-mode [parent|child] set detach-on-fork [on|off]
parent on 只调试主进程(gdb默认)
child on 只调试子进程
parent off 同时调试两个进程,gdb跟主进程,子进程block在fork位置
child off 同时调试两个进程,gdb跟子进程,主进程block在fork位置
更加详细的 gdb 多进程调试demo 可以参照 http://blog.csdn.net/pbymw8iwm/article/details/7876797
使用方式和线程调试思路是一样的. 就是gdb 的命令换了字符. 工作中多进程调试遇到少.
遇到了很少用gdb调试. 会用下面2种调试好办法
2) 写单元测试
3) 打日志检测日志,分析
到这里 gdb30分钟内容讲解完毕. 多试试写写练一练, gdb基本突破没有问题.
后记
错误是难免的, 有问题可以随时交流. 拜~~, 周六下午愉快. 希望明天仍然是个好天气~~
Linux基础 30分钟GDB调试快速突破的更多相关文章
- 【干货】零基础30分钟让你拥有一个完整属于自己的短视频APP系统
目录 一.附言: 1 二.购买域名和购买服务器: 2 三.搭建服务器环境: 5 四.配置APP前端部分: 8 1.工具以及文件准备: 9 2.配置后端接口地址 11 3.配置APP启动图和启动图标 ...
- linux下如何使用gdb调试
gdb是linux下非常好用的一个调试工具,虽然它是命令行模式的调试工具,但是它的功能强大到你无法想象,这里简单介绍下gdb下常用的命令. 首先编译生成可执行文件(这里的test.c是一个简单的求前n ...
- linux下如何用GDB调试c++程序
转:http://blog.csdn.net/wfdtxz/article/details/7368357 GDB 是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形 ...
- 在qemu环境中用gdb调试Linux内核
简介 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试.其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qem ...
- GDB调试:Linux开发人员必备技能
开篇词:Linux C/C++ 开发人员要熟练掌握 GDB 调试 大家好,我是范蠡,目前在某知名互联网旅游公司基础框架业务部技术专家组任开发经理一职. 本系列课程的主题是 Linux 后台开发的 C/ ...
- DAX基础入门 - 30分钟从SQL到DAX -- PowerBI 利器
看到漂漂亮亮的PowerBI报表,手痒痒怎么办?! 有没有面对着稀奇古怪的DAX而感到有点丈八金刚摸不着头脑或者干瞪眼?! 有没有想得到某个值想不出来DAX怎么写而直跳脚!? 看完这篇文章,你会恍然大 ...
- Linux下GDB调试简单示例
这里介绍对文件first.c的基本GDB调试操作,只有部分命令,只是一个示例,运行环境为装有gcc编译器和gdb调试器的Linux环境,基本GDB调试命令如下表: 命令 ...
- GDB 调试指南
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 00 介绍 ...
- Eclipse+CDT+GDB调试android NDK程序(转)
Eclipse+CDT+gdb调试android ndk程序 先介绍一下开发环境,在这个环境下,up主保证是没有问题的. ubuntu 11.10 eclipse 3.7(indego) for ja ...
随机推荐
- [ CodeVS冲杯之路 ] P1171
不充钱,你怎么AC? 题目:http://codevs.cn/problem/1171/ 代码调了很久才调好啊,一开始题目都看错了(要是真的NOIP肯定没戏了QuQ) 后面发现CodeVS上的数据输入 ...
- Web自动化框架LazyUI使用手册(2)--先跑起来再说(第一个测试用例-百度搜索)
作者:cryanimal QQ:164166060 上篇文章中,简要介绍了LazyUI框架,本文便来演示,如何从无到有快速搭建基于lazyUI的工程,并成功运行第一个测试用例. 本文以百度搜索为例,选 ...
- [实战经验]Macbook pro 苹果系统换window系统
1. Macbook的window软件驱动备份 通过Boot Camp助手进行window支持苹果驱动下载 2. 拆机把SSD固态硬盘放在主盘位置,把苹果的机械盘放在光驱位置 ...
- Flex4 自定义分页组件
自己写的Flex4分页组件,去伪存真,只实现基本的分页功能,数据过滤神马的都不应该是分页组件干的活,有呆毛才有真相: [源代码下载] Flex自从转手给Apache后人气急跌,本人也很捉鸡,尽管Apa ...
- Solum入门知识(编辑中)
概要 参考:https://wiki.openstack.org/wiki/Solum An OpenStack project designed to make cloud services eas ...
- js控制台调试
在web编程的过程中js代码出现错误,可以通过console.log()将相关信息输入到控制台进行调试. 清空控制台右击选 Clear console 菜单,或者输入 clear() 都行 控制台相关 ...
- linux 关闭系统提示声音
关闭Linux 提示声音: rmmod pcspkr //永久关闭 在/etc/modprobe.d/blacklist文件最后加上 blacklist pcspkr
- 2016-03-15:关于VS中模块定义文件
1 def模块定义文件 在使用开源库libx265时,因x265项目的头文件x265中有如下的宏定义 #ifdef X265_API_IMPORTS #define X265_API __declsp ...
- C# 请求外部服务的办法
) { returnMenuBeizhu = ; ).ToString() borrowT ...
- 第四次java实验报告
20145306 实验四 java 开发基础 设计过程: 1.创建项目 2.选择activity_main.xml 3.显示自己的学号 4.双击改变字体大小 5.预览