随想录(kgdb的基本原理)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在linux kernel调试的时候,有一种很方便的调试方法,那就是kgdb。kgdb的本质,就是在kernel内部建立一个gdb server,通过串口,它就可以和gdb进行通信来调试了。kgdb的代码和kernel是独立开来的,也就是说,除了极少数的情况,kgdb不会调用kernel的函数。这样就保证kgdb可以任意在kernel中设置断点进行调试。
1、gdb调试
gdb调试时kgdb的基础。一般来说,我们怎么用gdb调试呢,如下所示
shell> gcc hello.c -g -o hello
shell> gdb hello
编译的时候,一般先添加-g选项。添加这个选项的好处是什么,那就是在编译的时候会在执行文件中添加调试信息。有了这些调试信息之后,查看变量、设置函数断点都非常方便。因为关于函数地址、变量地址的相关信息都保存在了调试信息当中。
那么如果没有-g选项,能不能调试呢,其实也是可以的,
shell> gcc hello.c -o hello
shell> gdb hello
没有-g本身并不会影响调试,只是gdb种的部分选项无法使用而已。这时候调试,一般需要把执行文件反汇编处理,利用地址和阅读汇编语言的方法进行调试和分析。
2、gdb server & gdb
对于嵌入式来说,硬件本身的性能决定了很多软件不能运行在嵌入式设备上。这个时候,人们就想出来了gdb server的方法。gdb server就相当于一个服务器,它接受pc 上gdb的命令,将gdb要做的动作做完,并反馈相应的结果就可以了。
shell> gdbserver ./hello :7070
shell> gdb ./hello
gdb> target remote 127.0.0.1:7070
上面的demo是为了说明gdb server的用法,在实际使用的使用,只需要将ip设置成对应的嵌入式设备ip就可以了。那么,如果gdb想要调试一个嵌入式板子上正在运行的程序怎么办?也不难,
shell> gdbserver :7070 --attach 12345
shell> gdb
gdb> target remote 127.0.0.1:7070
修改的方法就是在gdbserver运行的时候添加一个attach的编译选型就可以了。
3、kdgb
有一本书籍叫《软件调试》的,内容非常好,它主要就是讲软件调试基本原理的。一般来说,软件调试离不开cpu、操作系统、编译器和gdb等调试软件的帮助。cpu决定了什么指令本身会产生中断、操作系统帮助我们收集被调试程序的所有信息、编译器产生调试信息、gdb则完成调试者和系统的交互工作。前面,我们谈到了gdb server& gdb,这里的kgdb完成的就是gdb server的工作。kgdb的使用其实非常简单,
a、打开kernel下面的kgdb调试开关
b、修改kernel启动参数
c、打开gdb命令,设置target选项,完成和gdb的沟通工作,准备开始调试
关于上面kgdb的详细操作,可以参考这一篇文章,或者这一篇。 那kgdb本身是怎么完成的,大家可以参考kernel/debug下的代码,比如这,kernel本身最早是在2.6.35才正式引入kgdb的。之前kgdb都只能作为patch自己添加到kerne里面,十分不方便。
整个目录下面,最重要的文件就是debug_core.c和gdbstub.c两个文件。中间主要包括了两个流程,
a、设置断点的流程,在debug_core.c中有如下的代码
early_param("kgdbwait", opt_kgdb_wait);
,它说明了opt_kgdb_wait是在什么时候调用的。事实上,这个函数最重要的设计就是调用arch_kgdb_breakpoint,人为产生一个断点,让cpu进入异常,从而可以让kdgb可以与gdb进行交互处理。
b、处理异常
int
kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
{
struct kgdb_state kgdb_var;
struct kgdb_state *ks = &kgdb_var;
int ret;
ks->cpu = raw_smp_processor_id();
ks->ex_vector = evector;
ks->signo = signo;
ks->err_code = ecode;
ks->kgdb_usethreadid = 0;
ks->linux_regs = regs;
if (kgdb_reenter_check(ks))
return 0; /* Ouch, double exception ! */
kgdb_info[ks->cpu].exception_state |= DCPU_WANT_MASTER;
ret = kgdb_cpu_enter(ks, regs);
kgdb_info[ks->cpu].exception_state &= ~(DCPU_WANT_MASTER |
DCPU_IS_SLAVE);
return ret;
}
这段代码就是kgdb异常的入口点。从kgdb_handle_exception-> kgdb_cpu_enter-> gdb_serial_stub函数,一条龙完成kgdb交互之前的准备工作,gdb_serial_stub函数如下所示,
/*
* This function performs all gdbserial command procesing
*/
int gdb_serial_stub(struct kgdb_state *ks)
{
int error = 0;
int tmp;
/* Clear the out buffer. */
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
if (kgdb_connected) {
unsigned char thref[8];
char *ptr;
/* Reply to host that an exception has occurred */
ptr = remcom_out_buffer;
*ptr++ = 'T';
ptr = pack_hex_byte(ptr, ks->signo);
ptr += strlen(strcpy(ptr, "thread:"));
int_to_threadref(thref, shadow_pid(current->pid));
ptr = pack_threadid(ptr, thref);
*ptr++ = ';';
put_packet(remcom_out_buffer);
}
kgdb_usethread = kgdb_info[ks->cpu].task;
ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
ks->pass_exception = 0;
while (1) {
error = 0;
/* Clear the out buffer. */
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
get_packet(remcom_in_buffer);
switch (remcom_in_buffer[0]) {
case '?': /* gdbserial status */
gdb_cmd_status(ks);
break;
case 'g': /* return the value of the CPU registers */
gdb_cmd_getregs(ks);
break;
case 'G': /* set the value of the CPU registers - return OK */
gdb_cmd_setregs(ks);
break;
case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
gdb_cmd_memread(ks);
break;
case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */
gdb_cmd_memwrite(ks);
break;
case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */
gdb_cmd_binwrite(ks);
break;
/* kill or detach. KGDB should treat this like a
* continue.
*/
case 'D': /* Debugger detach */
case 'k': /* Debugger detach via kill */
gdb_cmd_detachkill(ks);
goto default_handle;
case 'R': /* Reboot */
if (gdb_cmd_reboot(ks))
goto default_handle;
break;
case 'q': /* query command */
gdb_cmd_query(ks);
break;
case 'H': /* task related */
gdb_cmd_task(ks);
break;
case 'T': /* Query thread status */
gdb_cmd_thread(ks);
break;
case 'z': /* Break point remove */
case 'Z': /* Break point set */
gdb_cmd_break(ks);
break;
#ifdef CONFIG_KGDB_KDB
case '3': /* Escape into back into kdb */
if (remcom_in_buffer[1] == '\0') {
gdb_cmd_detachkill(ks);
return DBG_PASS_EVENT;
}
#endif
case 'C': /* Exception passing */
tmp = gdb_cmd_exception_pass(ks);
if (tmp > 0)
goto default_handle;
if (tmp == 0)
break;
/* Fall through on tmp < 0 */
case 'c': /* Continue packet */
case 's': /* Single step packet */
if (kgdb_contthread && kgdb_contthread != current) {
/* Can't switch threads in kgdb */
error_packet(remcom_out_buffer, -EINVAL);
break;
}
dbg_activate_sw_breakpoints();
/* Fall through to default processing */
default:
default_handle:
error = kgdb_arch_handle_exception(ks->ex_vector,
ks->signo,
ks->err_code,
remcom_in_buffer,
remcom_out_buffer,
ks->linux_regs);
/*
* Leave cmd processing on error, detach,
* kill, continue, or single step.
*/
if (error >= 0 || remcom_in_buffer[0] == 'D' ||
remcom_in_buffer[0] == 'k') {
error = 0;
goto kgdb_exit;
}
}
/* reply to the request */
put_packet(remcom_out_buffer);
}
kgdb_exit:
if (ks->pass_exception)
error = 1;
return error;
}
当然,除了第一次交互是人为设置断点之外,后面的断点主要是依靠kgdb的命令设置来完成的。在交互模式下,你可以完成很多操作,比如查看内存、寄存器、设置断点,甚至可以扩展kgdb的功能都可以。目前kgdb支持最好的是x86 cpu,其他cpu只是完成gdb server的一些基本工作而已。
随想录(kgdb的基本原理)的更多相关文章
- Ognl表达式基本原理和使用方法
Ognl表达式基本原理和使用方法 1.Ognl表达式语言 1.1.概述 OGNL表达式 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个 ...
- Android自定义控件之基本原理
前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理. 自 ...
- HMM基本原理及其实现(隐马尔科夫模型)
HMM(隐马尔科夫模型)基本原理及其实现 HMM基本原理 Markov链:如果一个过程的“将来”仅依赖“现在”而不依赖“过去”,则此过程具有马尔可夫性,或称此过程为马尔可夫过程.马尔可夫链是时间和状态 ...
- 动态令牌-(OTP,HOTP,TOTP)-基本原理
名词解释和基本介绍 OTP 是 One-Time Password的简写,表示一次性密码. HOTP 是HMAC-based One-Time Password的简写,表示基于HMAC算法加密的一次性 ...
- ZooKeeper基本原理
ZooKeeper简介 ZooKeeper是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等. ZooKeeper设计目的 1. ...
- GBDT的基本原理
这里以二元分类为例子,给出最基本原理的解释 GBDT 是多棵树的输出预测值的累加 GBDT的树都是 回归树 而不是分类树 分类树 分裂的时候选取使得误差下降最多的分裂 计算的技巧 最终分裂收益按照下面 ...
- 全文检索引擎 Solr 部署与基本原理
全文检索引擎 Solr 部署与基本原理 搜索引擎Solr环境搭建实例 关于 solr , schema.xml 的配置说明 全文检索引擎Solr系列-–全文检索基本原理 一.搜索引擎Solr环境搭建实 ...
- 相机位姿估计0:基本原理之如何解PNP问题
关键词:相机位姿估计 PNP问题求解 用途:各种位姿估计 文章类型:原理 @Author:VShawn(singlex@foxmail.com) @Date:2016-11-18 @Lab: CvLa ...
- geohash基本原理
geohash基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码,这种方式简单粗暴,可以满足对小规模的数据进行经纬度的检索 目录: 经纬度常识 认 ...
随机推荐
- 20145327 《Java程序设计》第十周学习总结
20145327 <Java程序设计>第十周学习总结 教材学习内容总结 网络编程就是运行在不同计算机中两个程序之间的数据交换. 网络中的每个设备都会有一个唯一的数字标识,这个就是IP地址. ...
- 20145329 《Java程序设计》第五周学习总结
20145329 <Java程序设计>第五周学习总结 教材学习内容总结 第八章 Java异常处理是要处理Exception类及其子类(Checked Exception),RuntimeE ...
- Spring Security核心概念介绍
Spring Security是一个强大的java应用安全管理库,特别适合用作后台管理系统.这个库涉及的模块和概念有一定的复杂度,而大家平时学习Spring的时候也不会涉及:这里基于官方的参考文档,把 ...
- JAVA基础补漏--文件读取
public class Test2 { public static void main(String[] args) throws IOException { FileInputStream fis ...
- HTML请求与相应
HTTP的请求与响应 HTTP通信由两部分组成: 客户端请求消息 与 服务器响应消息 浏览器发送HTTP请求的过程: 当用户在浏览器的地址栏中输入一个URL并按回车键之后,浏览器会向HTTP服务器发送 ...
- 网络软中断与NAPI函数分析
网卡只有rx硬中断,外设通过中断控制器向CPU发出有数据包来临的通知, 而没有tx硬中断,因为发送数据包是cpu向外设发出的命令. ixgbe驱动的rx软中断和tx软中断在同一个CPU上处理. htt ...
- No input file specified. nginx服务器报错解决
配置虚拟域名的时候报No input file specified. 仔细观察一圈也没有发现任何错误,最终,我把注意力放到 \ 上.才发现问题. 以前我都是直接copy文件路径过去 文件里面 都是使用 ...
- Learning Perl 第六章习题第一题
按照first name找last name 知识点: 1. hash的使用和初始化 2. 使用exists函数检测hash中的键是否存在
- 日期插件My97DatePicker
因为项目中需要选中日期,所以就找到了My97DatePicker这个插件,用法非常的简单,但是因为各个公司的要求不同,我们公司使用js拼代码,然后渲染到页面上的,所以遇到了一点问题… 1.My97Da ...
- 【转】学习Python的19个资源
原文链接:[译]学习Python编程的19个资源 用Python编写代码一点都不难,事实上它一直被赞誉为最容易学的编程语言.如果你准备学习web开发, Python是一个不错的开始,甚至想做游戏的话, ...