======================= 我的环境 ==========================
PC 端: win7 + vmware-15 ubuntu16.04
开发板:Freescale i.MX6  单CPU  Linux-4.1.15
 交叉编译器为 arm-none-linux-gnueabi-gcc(gcc version 4.6.3)
===========================================================

1. 前言

  上篇博文介绍了应用程序调试工具 gdb + gdbserver, 那有没有调试内核的呢?  没错, 就是本文介绍的kgdb, 当然早期有kdb, 后面kdb合并到kgdb了, 作为kgdb的前端, 后面我们会介绍, 而kgdb工具跟开发板通信支持kgdboc(串口)和kgdboe(网络),但新版内核只整合kgdboc, 网络被废弃了, 所以下文我们只介绍串口通信。

  串口通信有个问题就是, 如果开发板有多余的串口接出来是最好的, 但一般只有控制台console接出来, 所以当我们占用console作为kgdboc的通信接口, 那内核printk等打印我们是没办法通过shell  CRT软件看到的, 只有退出kgdb的时候才可以使用,

另外需要非常注意的是, 虚拟机必须用vmware, 不能用virtbox, 我用vbox-6.4版本的经常通信一会儿就没反应了。

2. kernel配置选项

Kernel hacking --->
  [*] KGDB: kernel debugger --->

  如果想用kdb 则选上“KGDB_KDB: include kdb frontend for kgdb”, 但触发kgdb是会先进入kdb模式, 也配置文件多出以下几个选项(注意红色):

--- target/linux/imx6ul/config-4.1    (revision )
+++ target/linux/imx6ul/config-4.1 (working copy)
@@ -, +, @@
# CONFIG_COMPILE_TEST is not set
CONFIG_CONFIGFS_FS=y
# CONFIG_CONNECTOR is not set
+CONFIG_CONSOLE_POLL=y
CONFIG_CONSOLE_TRANSLATIONS=y
# CONFIG_CORDIC is not set
CONFIG_COREDUMP=y
# CONFIG_CORESIGHT is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_CPUFREQ_DT is not set
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
@@ -, +, @@
# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_GPIO is not set
CONFIG_DEBUG_IMX_UART_PORT=
-# CONFIG_DEBUG_INFO is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_INFO_DWARF4 is not set
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_INFO_SPLIT is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_KOBJECT is not set
@@ -, +, @@
CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
# CONFIG_GAMEPORT is not set
+# CONFIG_GDB_SCRIPTS is not set
# CONFIG_GENERIC_ADC_BATTERY is not set
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_BUG=y
@@ -, +, @@
# CONFIG_JUMP_LABEL is not set
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
+CONFIG_KDB_CONTINUE_CATASTROPHIC=
+CONFIG_KDB_DEFAULT_ENABLE=0x1
+# CONFIG_KDB_KEYBOARD is not set
CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_LZ4 is not set
# CONFIG_KERNEL_LZMA is not set
@@ -, +, @@
# CONFIG_KEXEC is not set
CONFIG_KEYBOARD_SNVS_PWRKEY=y
CONFIG_KEYS=y
-# CONFIG_KGDB is not set
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KGDB_SERIAL_CONSOLE=y
+# CONFIG_KGDB_TESTS is not set
# CONFIG_KMX61 is not set
# CONFIG_KPROBES is not set
# CONFIG_KSM is not set
@@ -, +, @@
# CONFIG_SERIAL_IFX6X60 is not set
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
+# CONFIG_SERIAL_KGDB_NMI is not set
# CONFIG_SERIAL_MAX3100 is not set
# CONFIG_SERIAL_MAX310X is not set
# CONFIG_SERIAL_NONSTANDARD is not set

3.  启动参数

  上面选项只是编译相关调试代码, 如何告知kgdboc使用哪个串口呢?  一般有两种方式:

a. 在uboot传给kernel的cmdline 加上关键字 “console=ttyAMA4, kgdboc=ttyAMA4,”
b. 系统起来后 echo "kgdboc=ttyAMA4,115200" > /sys/module/kgdboc/parameters/kgdboc 启动过程中出现log:
[    2.055290] KGDB: Registered I/O driver kgdboc

4. 触发进入kgdb调试模式

  在控制台输入: echo g > /proc/sysrq-trigger

   

  如果没有选中配置选项“KGDB_KDB: include kdb frontend for kgdb”, 会直接进入kgdb模式, 否则需要在kdb下键入命令“kgdb”

5. 连接开发板

  首先我的PC机是win7系统, 虚拟机vmware-15装ubuntu-16.04, 然后串口配置为虚拟机独占win7的COM1, 对应就是ubuntu的/dev/ttyS0

  开发板跑的是Linux的Image镜像, 但调试得是带有调试信息的vmlinux, 同时也要有源码, 跟调试应用程序一样道理, 跑到kernel根目录下执行:

linux-4.1.$ sudo /opt/toolchain/arm-2012.03/bin/arm-none-linux-gnueabi-gdb vmlinux
GNU gdb (Sourcery CodeBench Lite 2012.03-) 7.2.50.20100908-cvs
Copyright (C) Free Software Foundation, Inc.
License GPLv3+: GNU GPL version or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi".
For bug reporting instructions, please see:
<https://support.codesourcery.com/GNUToolchain/>...
Reading symbols from /home/vedic/project/firmware_3/build_dir/linux-imx6ul_imx6_pax/linux-4.1./vmlinux...done.
(gdb) set remotebaud 115200
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
kgdb_breakpoint () at kernel/debug/debug_core.c:
arch_kgdb_breakpoint();
(gdb) set detach-on-fork on /* 后面有解释 */
(gdb) l
*/
noinline void kgdb_breakpoint(void)
{
atomic_inc(&kgdb_setting_breakpoint);
wmb(); /* Sync point before breakpoint */
arch_kgdb_breakpoint();
wmb(); /* Sync point after breakpoint */
atomic_dec(&kgdb_setting_breakpoint);
}
EXPORT_SYMBOL_GPL(kgdb_breakpoint);
(gdb) step
0xc00619bc in arch_kgdb_breakpoint () at kernel/debug/debug_core.c:
wmb(); /* Sync point before breakpoint */
(gdb) step
kgdb_breakpoint () at kernel/debug/debug_core.c:
wmb(); /* Sync point after breakpoint */
(gdb) l
noinline void kgdb_breakpoint(void)
{
atomic_inc(&kgdb_setting_breakpoint);
wmb(); /* Sync point before breakpoint */
arch_kgdb_breakpoint();
wmb(); /* Sync point after breakpoint */
atomic_dec(&kgdb_setting_breakpoint);
}
EXPORT_SYMBOL_GPL(kgdb_breakpoint); (gdb) step
atomic_dec(&kgdb_setting_breakpoint);
(gdb) b wake_up_process /* 设置断点, 这个函数会被系统频繁调度的 */
Breakpoint at 0xc003b858: file kernel/sched/core.c, line .
(gdb) c
Continuing. Breakpoint , wake_up_process (p=0xc8279c00) at kernel/sched/core.c:
WARN_ON(task_is_stopped_or_traced(p));
(gdb) l
* It may be assumed that this function implies a write memory barrier before
* changing the task state if and only if any tasks are woken up.
*/
int wake_up_process(struct task_struct *p)
{
WARN_ON(task_is_stopped_or_traced(p));
return try_to_wake_up(p, TASK_NORMAL, );
}
EXPORT_SYMBOL(wake_up_process); (gdb) bt
# wake_up_process (p=0xc8279c00) at kernel/sched/core.c:
# 0xc0033484 in __queue_work (cpu=, wq=0xc819aa80, work=0xc06d4f94) at kernel/workqueue.c:
# 0xc004dc48 in call_timer_fn (timer=<value optimized out>, fn=0xc0033494 <delayed_work_timer_fn>,
data=<value optimized out>) at kernel/time/timer.c:
# 0xc004de1c in __run_timers (h=<value optimized out>) at kernel/time/timer.c:
# run_timer_softirq (h=<value optimized out>) at kernel/time/timer.c:
# 0xc00267e8 in __do_softirq () at kernel/softirq.c:
# 0xc0026b64 in do_softirq_own_stack () at include/linux/interrupt.h:
# invoke_softirq () at kernel/softirq.c:
# irq_exit () at kernel/softirq.c:
# 0xc00467f4 in __handle_domain_irq (domain=0xc8006000, hwirq=<value optimized out>, lookup=true,
regs=<value optimized out>) at kernel/irq/irqdesc.c:
# 0xc0009340 in handle_domain_irq (regs=0xc8715e78) at include/linux/irqdesc.h:
# gic_handle_irq (regs=0xc8715e78) at drivers/irqchip/irq-gic.c:
# 0xc0011b40 in __irq_svc () at arch/arm/kernel/entry-armv.S:
Backtrace stopped: frame did not save the PC (gdb) next
{
(gdb) next
WARN_ON(task_is_stopped_or_traced(p));
(gdb) next
return try_to_wake_up(p, TASK_NORMAL, );
(gdb) step
}
(gdb) step
Cannot access memory at address 0x2
(gdb)
try_to_wake_up (p=0xc8279c00, state=, wake_flags=) at kernel/sched/core.c:
{
(gdb) l
* Return: %true if @p was woken up, %false if it was already running.
* or @state didn't match @p's state.
*/
static int
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
unsigned long flags;
int cpu, success = ; /*
(gdb) s
1668 raw_spin_lock_irqsave(&p->pi_lock, flags);
(gdb) n
1669 if (!(p->state & state))
(gdb) n
1707 raw_spin_unlock_irqrestore(&p->pi_lock, flags);
(gdb) n
1710 }
(gdb) n
__queue_work (cpu=0, wq=0xc819aa80, work=0xc06d4f94) at kernel/workqueue.c:1389
1389 }
(gdb) n
call_timer_fn (timer=<value optimized out>, fn=0xc0033494 <delayed_work_timer_fn>,
data=<value optimized out>) at kernel/time/timer.c:1158
1158 if (count != preempt_count()) {
(gdb) n
1169 }
(gdb) n
__run_timers (h=<value optimized out>) at kernel/time/timer.c:1204
1204 while (!list_empty(head)) {
(gdb) c
Continuing. Breakpoint 1, wake_up_process (p=0xc8278380) at kernel/sched/core.c:1762
1762 WARN_ON(task_is_stopped_or_traced(p));
(gdb) c
Continuing. Breakpoint 1, wake_up_process (p=0xc8106e00) at kernel/sched/core.c:1762
1762 WARN_ON(task_is_stopped_or_traced(p));
(gdb) c
Continuing. Breakpoint 1, wake_up_process (p=0xc804e700) at kernel/sched/core.c:1762
1762 WARN_ON(task_is_stopped_or_traced(p));
(gdb)
在用gdb来调试内核的时候,由于内核在初始化的时候,会创建很多子线程。而默认gdb会接管所有的线程,如果你从一个线程切换到另外一个线程,gdb会马上把原先的线程暂停。但是这样很容易导致kernel死掉,所以需要设置一下gdb。
一般用gdb进行多线程调试,需要注意两个参数:follow-fork-mode和detach-on-fork。 detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:
set detach-on-fork [on|off]
on: 断开调试follow-fork-mode指定的进程。
off: gdb将控制父进程和子进程。 follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。follow-fork-mode的用法为:
set follow-fork-mode [parent|child]
parent: fork之后继续调试父进程,子进程不受影响。
child: fork之后调试子进程,父进程不受影响。

  当没有断点, 输入continue让系统跑的时候,  串口将不会被kgdboc占用, 所以控制台又可以用了

6.  调试module.ko

  略......

7. 注意事项

a. 虚拟机用vmware, vbox串口有问题
b. 确保kgdboc加载成功, 如果出现“kgdb: Unregistered I/O driver, debugger disabled” 或者没有节点“/sys/module/kgdboc/parameters/kgdboc”
很有可能串口驱动没有支持如下三个函数: struct uart_ops {
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
}; 其实也简单, 在所在的串口驱动必然有提供读写TX/RX寄存器和判断FIFO的函数, 直接while查询即可
static int serial_get_poll_char(struct uart_port *port)
{
if((serial_in(port, ARM_UART_STS1) & 0xff) == )
return NO_POLL_CHAR; return serial_in(port, ARM_UART_RXD);
}
static inline void wait_for_xmitr(struct uart_port *port); static void serial_put_poll_char(struct uart_port *port, unsigned char c)
{ wait_for_xmitr(port);
serial_out(port, ARM_UART_TXD, c);
} c. 如果希望系统启动过程中就进入kgdb, 而不是后面“echo g > /proc/sysrq-trigger”
cmdline改成 -> “console=ttyAMA4, kgdboc=ttyAMA4, kgdbwait” d. PC端ubuntu用的gdb还是跟上篇博文的一样

  建议也看一下上篇博文:https://www.cnblogs.com/vedic/p/11104204.html

Linux嵌入式kgdb调试环境搭建的更多相关文章

  1. Linux嵌入式GDB调试环境搭建

    ======================= 我的环境 ==========================PC 端: CPU:x86_64, 系统:Ubuntu,IP:172.16.2.212开发 ...

  2. HDP2.0.6+hadoop2.2.0+eclipse(windows和linux下)调试环境搭建

    花了好几天,搭建好windows和linux下连接HDP集群的调试环境,在此记录一下 hadoop2.2.0的版本比hadoop0.x和hadoop1.x结构变化很大,没有eclipse-hadoop ...

  3. 【课程分享】深入浅出嵌入式linux系统移植开发 (环境搭建、uboot的移植、嵌入式内核的配置与编译)

    深入浅出嵌入式linux系统移植开发 (环境搭建.uboot的移植.嵌入式内核的配置与编译) 亲爱的网友,我这里有套课程想和大家分享,假设对这个课程有兴趣的,能够加我的QQ2059055336和我联系 ...

  4. Windows下Lua+Redis 断点调试环境搭建==Linux下类似

    Lua+Redis 断点调试环境搭建 windows环境,使用Redis,写lua脚本头疼的问题之一不能对脚本断点调试,google加上自己的摸索,终于搞定. 1.下载ZeroBraneStudio, ...

  5. arm64 调试环境搭建及 ROP 实战

    前言 比赛的一个 arm 64 位的 pwn 题,通过这个题实践了 arm 64 下的 rop 以及调试环境搭建的方式. 题目文件 https://gitee.com/hac425/blog_data ...

  6. 一步一步 Pwn RouterOS之调试环境搭建&&漏洞分析&&poc

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 本文分析 Vault 7 中泄露的 RouterOs 漏洞.漏洞影 ...

  7. eos源码分析和应用(一)调试环境搭建

    转载自 http://www.limerence2017.com/2018/09/02/eos1/#more eos基于区块链技术实现的开源引擎,开发人员可以基于该引擎开发DAPP(分布式应用).下面 ...

  8. Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...

  9. Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建

    Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以 ...

随机推荐

  1. WPF图片放大后模糊的解决方法

    原文:WPF图片放大后模糊的解决方法 WPF中显示图片的方式很多,可以用Image控件来显示图像,或者直接设置一个控件的Background.图片的放大也很简单,直接设置显示图片的控件的Width和H ...

  2. win10限制访问解决

    你的IT管理员已经限制对此应用一些区域的访问,你尝试访问的项目不可用.有关详细,请与你的IT支持人员联系. 按下win+s打开Cortana,在框中输入命令提示符,右键管理员身份运行在命令提示符中输入 ...

  3. CentOS 7.3 源码安装apache 2.4.16配置基于域名的虚拟主机

    主配置文件末尾添加一条配置: [root@vm2 ~]# vim /usr/local/apache/conf/httpd.conf Include conf/vhosts.conf 在conf目录下 ...

  4. Poco vs Boost(Poco也有不少优点,特别是网络功能更强)

    POCO的优点: 1) 比boost更好的线程库,特别是一个活动的方法的实现,并且还可设置线程的优先级. 2) 比 boost:asio更全面的网络库.但是boost:asio也是一个非常好的网络库. ...

  5. centos7 防火墙问题

    centos从7开始默认用的是firewalld,这个是基于iptables的,虽然有iptables的核心,但是iptables的服务是没安装的.所以你只要停止firewalld服务即可:sudo ...

  6. 一款好用的视频转换gif的小软件——抠抠视频秀

           在平常生活中,我们拍下来精彩的视频想要转换为gif动画,或是想要录制网页上的视频.电脑上的鼠标操作等等,大家可以使用以下这款很好用的视频转换gif的小软件——抠抠视频秀,这个软件操作简单 ...

  7. HTML特殊编码转换

    var encoded = ""'&<>¡¢£¤" + "¥¦§¨©ª«¬­®" + "¯°±²³´µ¶·" ...

  8. Android零基础入门第77节:Activity任务栈和启动模式

    通过前面的学习,Activity的基本使用都已掌握,接下来一起来学习更高级的一些内容. Android采用任务栈(Task)的方式来管理Activity的实例.当启动一个应用时,Android就会为之 ...

  9. 解决xp越来越慢的办法(其中有些自动备份的功能)

    1.减少磁盘空间占用2.终止不常用的系统服务3.安全问题4.另外一些技巧 首先问一下,你是不是很想激活XP,不...准确的说你是不是想在ms的站上能够升级.如果答案是肯定的话,那我们就先来探讨一下安装 ...

  10. 《解读window核心编程》 之 进程

    1.         进程是执行文件的运行时形态.包括两部分:内核数据(对应内核对象).地址空间(包括执行文件代码和栈堆等动态内存). 2.         把VC的“系统-子系统”值删除掉,即不指定 ...