当前位置: 首页 > news >正文

微网官方网站云南文山特产

微网官方网站,云南文山特产,中心网站建设,温岭市建设工程质量安全网站文章转自我朋友的公众号#xff0c;以下为内容正文大家好#xff0c;我是你们的工具人老吴。今天,和大家分享一下几个 Linux 内核的调试小技巧。当你遇到一个 bug#xff0c;你调试了 1 年半载都解决不了#xff0c;这其实一件好事。因为它会时刻提醒你平时写代码时要谨慎、… 文章转自我朋友的公众号以下为内容正文大家好我是你们的工具人老吴。今天,和大家分享一下几个 Linux 内核的调试小技巧。当你遇到一个 bug你调试了 1 年半载都解决不了这其实一件好事。因为它会时刻提醒你平时写代码时要谨慎、要多看书、多去认识一些更资深的人别问我为什么会有这样的感受因为是亲身经历~掌握一个调试工具是需要学习成本的这里只是列举我自己会用到的工具如果有某个你觉得特别牛逼的工具而我没提到的话请原谅我。好下面开始正文。最重要的是思路调试 bug 时不要急着做实验先梳理一下思路。一般可以总结成如下步骤1、理解问题;2、重现问题;3、定位问题找到相关的代码;4、尝试修复问题;5、如果失败回到第 1 步;bug 一般分为这几类1、Crash最常遇到的可能是因为我是做设备驱动开发的缘故;2、Lockup比较少这类问题预防比事后调试更重要;3、Logic/implementation error这个也比较容易遇到一般是运行不报错但是运行的结果不符合预期;4、Resource leak偶尔会遇到;5、Performance偶尔会遇到,对于做驱动开发的话一般是先考虑功能当性能达不到要求时再考虑优化性能。调试工具的类别1、很多人不知道调试最重要的工具是我们的大脑。换句话说也就是我们对内核个子系统、驱动开发的理解;2、Logs and dump analysis。内核很贴心许多异常发生时都会有一堆的 Kernel Panic 的信息经常能让我们直接定位到引起异常的代码;3、Tracing/profiling。这类工具一般能让我们理解程序的运行流程不仅适合用来调试问题也适合用来学习和理解内核的各种功能实现。4、Interactive debugging。主要就是 gdb我个人用得很少。5、Debugging frameworks。许多的调试工具经过不断地发展和完善后就慢慢地形成了一整套的调试框架例如 Ftrace、SystemTap。下面是几个我常用的调试技巧 / 工具。最常用的方法打印点击查看大图关于打印的工具主要是这 3 种1、printk()最原始的打印 api可以用但是主流观点已经不推荐使用了。与之相关的是启动参数 loglevel它决定了可以被打印出来的信息的最低优先级。2、pr_*()推荐用 pr_*() 来代替 printk()这是一个函数族pr_emerg(), pr_alert(), pr_crit(), pr_err(), pr_warning(), pr_notice(), pr_info(), pr_cont(), pr_debug()例如pr_info(Booting CPU %d\n, cpu); 内核会打印[ 202.350064] Booting CPU 1 3、dev_*()同样是一个函数族dev_emerg(), dev_alert(), dev_crit(), dev_err(), dev_warn(), dev_notice(), dev_info(), dev_dbg() 它们的最大特点是需要传入一个 struct device 的参数并且会打印出这个 device 的名字一边是在驱动相关的代码里使用。例如dev_info(pdev-dev, in probe\n); 内核会打印[ 25.878382] serial 48024000.serial: in probe 关于 pr_debug() and dev_dbg()要使用这两个 api需要在对应的代码里 #deinfe DEBUG。当内核使能了 CONFIG_DYNAMIC_DEBUG我们就可以通过 /sys/kernel/debug/dynamic_debug/control 动态地是否要打印 log以及打印哪些 log。使用方法大致如下$ mount -t debugfs none /sys/kernel/debug/ $ cd /sys/kernel/debug/dynamic_debug/ $ echo “file xxx.c p”  control $ echo “file svcsock.c line 1603 p”  control $ echo “file drivers/usb/core/* p”  control $ echo “file xxx.c -p”  control 具体地可以参考https://training.ti.com/sites/default/files/docs/Kernel-Debug-Series-Part4-dynamic-debug.pdf分析 Kernel Panic 的 信息举个例子下面是一次 Kernel Panic$ cat /sys/class/gpio/gpio504/value [23.688107] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [23.696431] pgd  (ptrval) [23.699167] [00000000] *pgd28bd4831, *pte00000000, *ppte00000000 [23.705596] Internal error: Oops: 17 [#1] SMP ARM [23.710316] Modules linked in: [23.713394] CPU: 1 PID: 177 Comm: cat Not tainted 4.19.17 #8 [23.719060] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [23.725606] PC is at mcp23sxx_spi_read0x34/0x84 [23.730241] LR is at _regmap_raw_read0xfc/0x384 [23.734866] pc : [c0539c44] lr : [c067d894] psr: 60040013 [23.741142] sp : d8c6da48 ip : 00000009 fp : d8c6da6c [23.746375] r10: 00000040 r9 : d8a94000 r8 : d8c6db30 [23.751608] r7 : c12ed9d4 r6 : 00000001 r5 : c0539c10 r4 : c1208988 [23.758145] r3 : d8789f41 r2 : 2afb07c1 r1 : d8789f40 r0 : 00000000 [...] // 省略 关键信息PC is at mcp23sxx_spi_read0x34pc : [c0539c44]PC 是当前执行的指令的地址。接下来我们可以借助 addr2line 定位到具体是哪一行代码引起了panic$ arm-linux-addr2line -f -e vmlinux 0xc0539c44 mcp23sxx_spi_read /home/sprado/elce/linux/drivers/pinctrl/pinctrl-mcp23s08.c:357 另外还可以用 gdb 来定位代码$ arm-linux-gdb vmlinux (gdb) list *(mcp23sxx_spi_read0x34) 0xc0539c44 is in mcp23sxx_spi_read (drivers/pinctrl/pinctrl-mcp23s08.c:357) earlyprintkearlyprintk 一般用来处理一些发生在启动初期时的异常。最常见的现象就是系统打印完 Starting Kernel... 后就 hang 住了。用法1、配置内核CONFIG_EARLY_PRINTK CONFIG_DEBUG_LL 、设置启动参数类似root/dev/mmcblk0p2 rootwait rw earlyprintk consolettyS0,115200WARN_ON()这个函数可以打印出当前的函数调用栈。我一般会在高度可疑的地方使用它。举个例子static int sun6i_spi_probe(struct platform_device *pdev) {struct spi_master *master;struct sun6i_spi *sspi;[...]// 用于调试 WARN_ON(1);master  spi_alloc_master(pdev-dev, sizeof(struct sun6i_spi));[...] 当运行到 WARN_ON(1) 时内核会打印[    1.847018] WARNING: CPU: 1 PID: 1 at drivers/spi/spi-sun6i.c:549 sun6i_spi_probe0x20/0x3ac [    1.855454] Modules linked in: [    1.858525] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.14.111 #196 [    1.864781] Hardware name: sun8i [    1.868032] [c02287fc] (unwind_backtrace) from [c0225398] (show_stack0x10/0x14) [    1.875776] [c0225398] (show_stack) from [c0a1ba3c] (dump_stack0x94/0xa8) [    1.882997] [c0a1ba3c] (dump_stack) from [c0240c24] (__warn0xe8/0x100) [    1.889953] [c0240c24] (__warn) from [c0240cec] (warn_slowpath_null0x20/0x28) [    1.897517] [c0240cec] (warn_slowpath_null) from [c06a03c0] (sun6i_spi_probe0x20/0x3ac) [    1.905953] [c06a03c0] (sun6i_spi_probe) from [c0617980] (platform_drv_probe0x4c/0xb0) [    1.914299] [c0617980] (platform_drv_probe) from [c06160dc] (driver_probe_device0x234/0x2f0) [    1.923162] [c06160dc] (driver_probe_device) from [c0616244] (__driver_attach0xac/0xb0) [    1.931592] [c0616244] (__driver_attach) from [c06144ec] (bus_for_each_dev0x68/0x9c) [    1.939762] [c06144ec] (bus_for_each_dev) from [c0615654] (bus_add_driver0x198/0x210) [    1.948020] [c0615654] (bus_add_driver) from [c0616aec] (driver_register0x78/0xf8) [    1.956017] [c0616aec] (driver_register) from [c0201a70] (do_one_initcall0x40/0x16c) [    1.964193] [c0201a70] (do_one_initcall) from [c1000e6c] (kernel_init_freeable0x1c8/0x264) [    1.972884] [c1000e6c] (kernel_init_freeable) from [c0a2ef4c] (kernel_init0x8/0x114) [    1.981054] [c0a2ef4c] (kernel_init) from [c0222058] (ret_from_fork0x14/0x3c) [    1.988686] ---[ end trace dc4e090f55ad2de8 ]--- 我们可以很清晰地看到 sun6i_spi_probe() 被调用的流程。这个方法跑起来很简单但是每次使用都得编译和更新内核非常不方便只适合轻度使用。Pstore如果发生 Kernel panic 时我们并没有连接串口终端那么这一次的崩溃信息就丢失了。Pstore (persistent storage) 就可以用来处理这种情况。当发生 Kernel painic 时Pstore 会自动保存 oops 和 panic 的 log并且在软重启后仍可以查看 log 信息。默认情况下log 是存储在 RAM 的某个保留区域中但也可以使用存储设备例如闪存。用法1、配置内核CONFIG_PSTORE CONFIG_PSTORE_RAM 2、配置 dts为 Pstore 预留一块内存类似reserved-memory {#address-cells 1;#size-cells 1;ranges;ramoops: ramoops0b000000 {compatible ramoops;reg 0x20000000 0x200000; /* 2MB */record-size 0x4000; /* 16kB */console-size 0x4000; /* 16kB */}; }; 3、假设刚发生了一次 Panic并且已经软重启$ mount -t pstore pstore /sys/fs/pstore/$ ls /sys/fs/pstore/ dmesg-ramoops-0 dmesg-ramoops-1 通过上面这两个文件就可以看到内核的崩溃信息了。内核文档Documentation/admin-guide/ramoops.rstdevmem2这是一个命令行工具它可以在用户空间去读写内存。大多数情况我是用它来读写寄存器简单粗暴。用法$ apt-get install devmem2 、查看寄存器 TMR_IRQ_EN_REG$ devmem2 0x0x01C20C00 /dev/mem opened. Memory mapped at address 0xb6f38000. Value at address 0x0 (0xb6f38000): 0xEA000016 2、修改 TMR_IRQ_EN_REG:# devmem2 0x0x01C20C00 w 0xEA000018 /dev/mem opened. Memory mapped at address 0xb6fe8000. Value at address 0x0 (0xb6fe8000): 0xEA000016 Written 0xEA000018; readback 0xEA000018 GDB如果你想完全控制内核的运行例如单步执行、查看变量等可以用 GDB。点击查看大图这里采用的是 C/S 架构在板子上运行 server (kgdb)在 PC 机上运行 client (gdb)通讯的方式可以是串口或者网络我一般是用串口。如何配置1、配置内核CONFIG_KGDB CONFIG_KGDB_SERIAL_CONSOLE CONFIG_KGDB_KDB 2、设置启动参数kgdocconsolettyS0,115200 kgdbocttyS0,115200 earlyprintk root/dev/mmcblk0p2 oopspanic panic0 ttyS0 是板子的调试串口。要使用 kgdb必须为其设置一个 I/O driver我一般使用 kgdb over serial console (简称 kgdboc)oopspanic panic0 很重要。另外也通过在启动后设置 kgdbocecho ttyS0  /sys/module/kgdboc/parameters/kgdboc 3、让内核进入 debug 模式$ echo g  /proc/sysrq-trigger [ 1958.025927] sysrq: SysRq : DEBUG [ 1958.029191] KGDB: Entering KGDB 4、让 PC 机连接板子$ arm-linux-gdb vmlinux(gdb) set serial baud 115200 (gdb) target remote /dev/ttyUSB0 Remote debugging using /dev/ttyUSB0 0xc02c3540 in kgdb_breakpoint () 举个例子配置好之后通过 gdb 调试内核跟通过 gdb 调试应用的操作是一样的。这里我举一个小例子。首先人为让内核 Crash$ echo WRITE_KERN  /sys/kernel/debug/provoke-crash/DIRECT Entering kdb (current0xffffffc0de55f040, pid 1470) on processor 4 Oops: (null) due to oops  0xffffff80108bfa48 CPU: 4 PID: 1470 Comm: bash Not tainted 5.3.0-rc2 #13 pc : __memcpy0x48/0x180 lr : lkdtm_WRITE_KERN0x4c/0x90 ... 下面开始调试。1、查看调用栈(gdb) bt Call trace: dump_backtrace0x0/0x138 show_stack0x20/0x2c kdb_show_stack0x60/0x84 ... do_mem_abort0x4c/0xb4 el1_da0x20/0x94 __memcpy0x48/0x180 lkdtm_do_action0x24/0x44 direct_entry0x130/0x178 2、查看栈帧的内容(gdb) frame 1 #1 0xffffff801056584c in lkdtm_WRITE_KERN () at .../drivers/misc/lkdtm/perms.c:116 116 memcpy(ptr, (unsigned char *)do_nothing, size); 基本可以确定是使用 memcpy() 时导致 Crash。3、查看相关代码(gdb) list 112  size  (unsigned long)do_overwritten - (unsigned long)do_nothing; [...] 116  memcpy(ptr, (unsigned char *)do_nothing, size); 需要核查一下 ptr、do_nothing、size这 3 个参数是否合法。4、打印变量值(gdb) print size $3  18446744073709551584(gdb) print do_overwritten - do_nothing $4  -32 最后发现 18446744073709551584 其实就是 (unsigned long) 的 -32。memcpy 的数据大小是 -32导致了内核崩溃。FtraceFtrace 的作用是帮助开发人员了解 Linux 内核的运行时行为以便进行故障调试或性能分析。最早 Ftrace 是一个 function tracer仅能够记录内核的函数调用流程。如今 ftrace 已经成为一个 framework采用 plugin 的方式支持开发人员添加更多种类的 trace 功能。用法$ mount -t tracefs none /sys/kernel/tracing $ cd /sys/kernel/tracing/ $ cat available_tracers hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop 跟踪器 tracer 表示的是要跟踪的目标。假设我们抓一次 spi 传输的过程echo 0  tracing_on echo function_graph  current_tracer echo *spi*  set_ftrace_filter echo *dma*  set_ftrace_filter echo *spin*  set_ftrace_notrace echo 1  tracing_on ./spidev_test echo 0  tracing_on cat trace 得到的信息1)  41.292 us   |  spidev_open();1)               |  spidev_ioctl() {1)               |    spi_setup() {1)   0.417 us    |      __spi_validate_bits_per_word.isra.0();1)               |      sunxi_spi_setup() {1)   0.834 us    |        sunxi_spi_check_cs();1)   0.875 us    |        spi_set_cs();1)   0.625 us    |        sunxi_spi_cs_control();1)  17.125 us   |      }1)   0.833 us    |      spi_set_cs();1)  30.458 us   |    }1) ! 699.875 us  |  }[...] 相关参考https://blog.csdn.net/Guet_Kite/article/details/101791125Kdump这个工具我没有用过但是它似乎很强大所以我觉得应该简单介绍一下。kdump 是一种基于 kexec 系统调用 的内核崩溃转储机制。当系统崩溃时kdump 使用 kexec 启动进入到第二个内核 (dump-capture kernel)从而获得 coredump 信息。用法1、设置启动参数crashkernel64M 2、运行 kexec$ kexec --type zImage -p /boot/zImage \--initrdinitrd-for-dump-capture-kernel \--dtbdtb-for-dump-capture-kernel \--command-lineXXX 运行完 kexec 后dump-capture kernel 就被加载进内存了。以后如果发生了 kernel panicdump-capture kernel 会被加载并运行。我们可以在 dump-capture kernel 下获得 coredump 文件$ cp /proc/vmcore dump-file 然后就可以在 PC 上使用 gdb/crash 来调试分析了$ arm-linux-gdb path/to/vmlinux -c path/to//vmcore $ crash path/to/vmlinux path/to/vmcore 内核文档Documentation/kdump/kdump.txt总结预防为主调试为辅。软件开发没有银弹同样的bug 调试也没有银弹。但是多熟悉一些调试工具是有好处的。当然还有很多调试工具、技巧是我不知道了欢迎大家分享给我。Anyway, what we know is a drop, what we dont know is an ocean.祝周末愉快。—— The End ——感谢完成阅读我是喜欢打篮球的写代码的篮球球痴这个是我的公众号感谢你关注并支持。我从大学开始接触电子和嵌入式软件知识至今已经毕业工作了9年我喜欢嵌入式也愿意从事这个行业。不管是从技术还是职场经验都积累了足够多的经验目前在一个非常优秀的团队中做开发工作。很高兴认识每一个对技术努力对人用心的朋友。关注公众号后台回复「1024」获取学习资料网盘链接。欢迎点赞关注转发在看您的每一次鼓励我都将铭记于心~嵌入式Linux微信扫描二维码关注我的公众号
http://www.zqtcl.cn/news/609946/

相关文章:

  • 网站上怎么做支付接口南乐网站建设
  • 咸阳网站建设公司电话做个公司网站大概多少钱
  • 网站如何做关键词排名点子网创意网
  • 浙江建设培训考试网站河源东莞网站建设
  • 网站移动端做pc端的301跳转哪些网站是增值网
  • wordpress新闻站浙江耀华建设集团网站
  • 网站开发代理企业网站推广技巧和方法
  • 俄语网站开发用模板做的网站多少钱
  • 丽水网站建设公司广州网络公司
  • 做基金的网站哪个好针对大学生推广引流
  • 国外对旅游网站的建设互联网推广和互联网营销
  • 海南省建设厅网站首页有什么做设计的兼职网站
  • 网站导航功能苏州市高新区建设局网站
  • jsp网站 值班多语种网站开发
  • 公司网站英文做电商
  • 合肥企业网站建设公司哪家好卖产品怎么做网站
  • 网站建设公司86215中国中小企业网站
  • 做网站 如何 挣钱游戏网站开发协议
  • 网站建设发展wordpress比较慢
  • 收费网站推广动漫制作就业方向
  • 湖北优化网站建设设计公司需要什么资质
  • 个人网站怎么制作wordpress创意小工具
  • 网站管理维护怎么做在线oa
  • vue做企业网站wordpress 不发送邮件
  • 深圳做网站哪家便宜邮政编码html编写
  • 黑龙江营商环境建设局网站门户网站整改报告
  • 是不是做推广都得有网站深圳网站建设新闻
  • 旅游做视频网站关于建设网站的书本
  • 营销网站是什么意思快站优惠券app
  • wordpress加dz公司网站搜索优化