# 调试

使用以下方法了解
high-level 内核调试方式，并报告
Qualcomm Linux 内核中固有的调试功能识别出的问题。

有关调试的详细信息，参见 [Qualcomm Linux 调试指南](bundle/publicresource/topics/80-70015-12)。

## 串行控制台

串行控制台用于访问标准启动和内核日志。
串行控制台支持在实时设备上进行实时调试。Qualcomm Linux 内核启用 `CONFIG_SERIAL_QCOM_GENI` 驱动程序，以支持 UART 和控制台。

在内核命令行参数中添加以下行：

console=ttyMSM0,115200n8
    
    # as in following line in meta-qcom-hwe/conf/machine/include/qcom-<SoC>.conf
    KERNEL_CMDLINE_EXTRA ?= "root=/dev/disk/by-partlabel/system rw rootwait console=ttyMSM0,115200n8 pcie_pme=nomsi earlycon"
    Copy to clipboard

重新编译和加载内核时：

- 按如下所示连接一根一端是 Micro USB 接口一端是 USB-A 型接口的电缆：

    - 设备 &gt; Micro USB 接口====USB-A 型接口 &gt; 主机
- 若要连接到主机上的设备串行端口，可使用主机上的串行控制台客户端。
- 在串行客户端上可以查看串行控制台日志和其它内核日志。

有关串行控制台的详细信息，参见 [Linux 串行控制台](https://docs.kernel.org/admin-guide/serial-console.html)。

## 配置控制台日志级别

配置内核控制台日志级别，以记录详细日志或最小化日志从而满足调试或软件发布版本的要求。

使用 `/proc/sys/kernel/printk` 配置日志级别，控制出现在控制台上的日志消息。日志级别可设置为 1 到 7。

将日志级别值设置为 1 时，只会记录最低级别的日志。当日志级别值设置为 1 时，
只会打印 `pr_emerg/KERN_EMERG` printk 日志。

将日志级别值设置为 7 时，会启用最高日志级别
`pr_info/KERN_INFO`，并将所有 printk 日志打印到控制台。

$ echo "1" > /proc/sys/kernel/printk
    Copy to clipboard

## 显示内核日志

运行以下任一命令，显示内核日志：

$ dmesg
    # or
    $ cat /proc/kmsg
    Copy to clipboard

## 显示启动后的内核日志

请运行以下任一命令，显示启动后的内核日志：

$ cat /var/log/messages
    or
    $ cat /var/log/kern.log
    Copy to clipboard

## 使用 printk 进行调试

使用 `printk` 技术在 Linux 内核中调试以打印消息和跟踪。

printk 的封装在 `include/linux/printk.h` 中定义，
它支持在日志语句中添加日志级别。

例如，

pr_emerg("At line %d: Func: %s\n", __LINE__, __func__);
    pr_info("At line");
    Copy to clipboard

有关详细信息，参见 [使用 printk 记录消息日志](https://www.kernel.org/doc/html/next/core-api/printk-basics.html)。

## 使用日志级别

可以使用 `/linux/printk.h` 中定义的任何其他日志级别打印消息
。控制台日志通过 printk 中使用的日志级别和设备上选择的日志级别进行控制。

应根据您的用例选择日志级别。
如果在频繁调用的函数中选择较低的日志级别，可能会生成大量日志。

以下示例所示为内核打印级别的日志：

pr_info("At func %s\n", __func__);
    pr_notice("At func %s\n", __func__);
    pr_warn("At func %s\n", __func__);
    pr_err("At func %s\n", __func__);
    pr_crit("At func %s\n", __func__);
    pr_alert("At func %s\n", __func__);
    pr_emerg("At func %s\n", __func__);
    Copy to clipboard

## 启用 SSH

在 Permissive 模式下启用安全 shell (SSH) 以安全访问主机设备。相关说明，可参见 [SSH 使用指南](https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-254/how_to.html#use-ssh)。

## 从设备中检索信息

可以从设备中检索信息，用于调试内核问题。

下表为内核命令列表：

表：内核命令列表

| 命令 | 说明 |
| --- | --- |
| $ zcat /proc/config.gz<br>    Copy to clipboard | 内核配置 |
| $ cat /proc/cmdline<br>    Copy to clipboard | 内核命令行参数 |
| $ cat /proc/version<br>    Copy to clipboard | 内核版本 |
| $ ls /proc/device-tree<br>    Copy to clipboard | 设备上的设备树配置 |
| $ lsmod<br>    Copy to clipboard | 已加载模块 |
| $ cat /proc/interrupts<br>    Copy to clipboard | 中断状态 |
| $ cat /sys/devices/system/cpu/cpufreq/policyX/scaling_available_frequecies<br>    Copy to clipboard | 可用的 CPU 频率，其中 X = 群集 |
| $ cat /sys/kernel/debug/qcom_socinfo/chip_id<br>    Copy to clipboard | 使用 debugfs 的 chip\_id。 |

有关 `procfs` 的详细信息，参见 [The /proc
Filesystem](https://docs.kernel.org/filesystems/proc.html)。

## 启用 debugfs

Debugfs 是一个文件系统，可帮助内核开发人员向用户空间提供内核信息。

Debugfs 用于访问内核数据结构和变量、跟踪事件、调试消息和统计信息。

- 确保内核配置中已启用 `CONFIG_DEBUG_FS` （默认启用）。
- 如果尚未启用，可以使用 `menuconfig` 在内核配置中启用。
- 如果 debugfs 尚未安装，请进行安装。

$ mount -t debugfs none /sys/kernel/debug
    Copy to clipboard

## 通过 KGDB 调试内核和模块

内核 GNU 调试器 (KGDB) 支持在设备上对内核进行实时调试。

可将 KGDB 配置为调试内核和模块问题。KGDB 是与 GDB 配合使用的源代码级调试工具，用于调试 Qualcomm Linux 内核。

有关 KGDB 内核调试的信息，请参见 [通过 gdb 调试内核和模块](https://docs.kernel.org/dev-tools/gdb-kernel-debugging.html)。

### 使用 KGDB 通过串行 COM 端口调试

在内核配置文件中启用以下内核驱动程序，以支持 KGDB 通过串行 COM 端口调试：

CONFIG_FRAME_POINTER
    CONFIG_KGDB
    CONFIG_KGDB_SERIAL_CONSOLE
    CONFIG_HAVE_ARCH_KGDB
    CONFIG_CONSOLE_POLL
    CONFIG_MAGIC_SYSRQ
    Copy to clipboard

更新用于调试的命令行：

# Append  KERNEL_CMDLINE_EXTRA in meta-qcom-hwe/conf/machine/include/qcom-<SoC>.conf with following string
     "kgdboc=ttyMSM0,115200n8 kgdbwait nokaslr"
    
    # If waiting during the boot of kernel for gdb connection is not desired,
    # the parameter 'kgdbwait' can be skipped.
    Copy to clipboard

**禁用看门狗**

- 在使用 KGDB 调试时禁用看门狗。当看门狗处于启用状态时，
SoC 会复位。
- 添加以下内核命令行参数，禁用看门狗：

echo 1 > /sys/bus/platform/devices/hypervisor:qcom,gh-watchdog/disable
        Copy to clipboard

**进入调试模式的方法**

**方法 1** - 使用 `kgdbwait`：

如果将 `kgdbwait` 作为命令行参数传递，内核在启动过程中暂停执行，并进入调试模式，等待与远程 GDB 的连接，同时显示以下消息：

[ 0.239669] printk: console [ttyMSM0] enabled
    [ 1.535669] random: fast init done
    [ 1.541411] KGDB: Registered I/O driver kgdboc
    [ 2.224804] KGDB: Waiting for connection from remote gdb...
    Copy to clipboard

通过配置禁用看门狗。

**方法 2** - 使用 `Magic SysRq`：

若要进入调试模式，在 shell 中运行 `SysRq-G`，然后运行以下命令：

`echo g > /proc/sysrq-trigger`

**方法 3** - 要触发源代码中的 KGDB 断点时使用。将以下代码片段添加至目标驱动程序文件中。

#include <linux/kgdb.h>   (you may need to include this file )
     kgdb_breakpoint(); //call this for adding a break point at compile time
    Copy to clipboard

**方法 4** - 触发异常：内核在发生异常时自动进入调试模式。若要触发崩溃，可使用 `Magic SysRq`
功能。

`echo c > /proc/sysrq-trigger`

### 使用 GDB 经由 COM 端口连接到 Qualcomm SoC

安装 GDB `gdb-multiarch: mingw64\bin\gdb-multiarch.exe`。

- 适用于 Windows 系统的 GDB：通过 [MSYS2](https://www.msys2.org/) 安装 Mingw-w64
。
- 适用于 Linux 系统的 GDB：依据 [GDB 安装指南](https://www.gdbtutorial.com/tutorial/how-install-gdb) 安装 GDB。
- 使用GDB 经由 COM 端口连接到 SoC 时，与串行端口的所有其他连接均须关闭。
。
- 在 Windows 主机上 - COM 端口号必须设置为大于 16。
可以从设备管理器中获取端口号。
- 在 Windows 主机上 - COM 端口应以 `\\.\com<#>` 的形式传递。
- 在以下示例中，通过 Windows 主机上的串行端口进行连接。串行端口号为 COM17。先将 GDB 与 COM 端口断开连接，
然后再重新连接 PuTTY。若要连接和断开 GDB，
可运行以下命令：

gdb-multiarch.exe -b 115200 <path_to_vmlinux>
        (gdb) target remote \\.\com17
            Remote debugging using \\.\com17
            warning: multi-threaded target stopped without sending a thread-id, using first non-exited thread
            [Switching to Thread -2]
            arch_kgdb_breakpoint () at arch/arm64/include/asm/kgdb.h:21
            21      arch/arm64/include/asm/kgdb.h: No such file or directory.
        Copy to clipboard

使用以下命令设置断点、查看断点或查看 CPU 寄存器：

# backtrace:
    > bt
    
    # set a break point:
      break <function_name>
    or
      break <filename>: line no
    
    # View all current break points:
      info break
    
    # View CPU registers:
      info reg
    
    # step over:
      s
    
    # continue execution:
      c
    Copy to clipboard

### 使用 KGDB 调试内核模块

KGDB 是一种内核补丁，支持使用 GDB 接口对运行中的 Qualcomm Linux 内核进行源代码级调试。

用户必须拥有 `.ko` 文件和 `.text` 内核模块文件的位置。

若要获取已加载模块 `.text` 部分的地址，可运行以下命令：

# from live device
    # cat /sys/module/<module>/sections/.text
    Copy to clipboard

若要添加模块符号文件，可运行以下命令：

(gdb) add-symbol-file <path_to_.ko> <.text_addr>
    Copy to clipboard

例如， `add-symbol-file /sys/modules/linux/linux.ko 0xc0ae22d0`。

## 函数追踪

ftrace 提供了跟踪工具，可以在运行时对整个系统进行分析和追踪。

有关 ftrace 的详细信息，参见 [函数追踪](https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-12/debugging_linux_kernel.html#sub$Function_trace_ftrace)。

### 启用 memory-mapped 的输入/输出追踪

Qualcomm Linux 内核中的内存映射 I/O (MMIO) 跟踪功能可提供相关
信息，便于用户了解驱动程序如何与 MMIO 设备及其
相关硬件状态进行交互。

通用 MMIO 跟踪使用 `__raw_{read,write}{b,l,w,q}` 访问器函数
对内存映射寄存器执行读/写操作。

在下列情况下，设备会挂起，或者一些未定义的行为
会导致设备崩溃：

> 
> 
> 表：设备崩溃的原因
> 
> 
> | 原因 | 说明 |
> | --- | --- |
> | 无时钟时的访问 | 如果对寄存器空间的访问没有时钟信号控制，则设备会崩溃。 |
> | 受保护的寄存器空间 | 如果寄存器空间受到保护，并且未设置为允许非安全世界访问，则会发生异常。例如，仅允许异常级别 (EL3) 的访问，禁止任何 EL2/EL1 访问。 |
> | xPU 控制 | xPU 内存保护单元控制特定客户端对特定内存或寄存器区域的访问。 |

前述场景会导致即时重启、同步错误(SErrors)/片上网络 (NoC) 问题或互连挂起。
`CONFIG_TRACE_MMIO_ACCESS` 提供 ftrace 跟踪事件以记录此类 MMIO 寄存器访问，提供早期启用追踪事件、筛选功能以及将 ftrace 日志转储在控制台上的功能。

以下所示为启用 MMIO 追踪后追踪缓冲区的输出示例：

# List all rwmmio trace events
    $ cat /sys/kernel/tracing/available_events | grep rwmmio
    
    # Enable all rwmmio trace events
    $ cat /sys/kernel/tracing/available_events | grep rwmmio >> /sys/kernel/tracing/set_event
    
    # List all traces
    $ cat /sys/kernel/tracing/trace
    rwmmio_read: gic_peek_irq+0xd0/0xd8 readl addr=0xffff800010040104
    rwmmio_write: gic_poke_irq+0xe4/0xf0 writel addr=0xffff800010040184
    rwmmio_read: gic_do_wait_for_rwp+0x54/0x90 readl addr=0xffff800010040000
    rwmmio_write: gic_set_affinity+0x1bc/0x1e8 writeq addr=0xffff800010046130
    Copy to clipboard

### Kprobes

Kprobes用于在不影响系统运行的情况下探入任何内核例程，收集调试和性能信息。

内核代码地址使用触发断点时调用的处理程序例程捕获。在调度器调试过程中，当在不同的调度事件中生成跟踪信息以了解系统行为时，Kprobes 很有用。

有关详细信息，参见 [内核探针](https://www.kernel.org/doc/html/next/trace/kprobes.html)。

## 解决内核问题

使用以下方法解决内核问题：

### 选错/没有可用的 DTB 导致启动失败

解决因 DTB 加载问题而导致的启动失败时，需要验证鉴权及 DTB 可用性，然后按以下步骤解决问题。

如下场景会导致启动失败：

- **鉴权失败**

    以下示例所示为因鉴权错误而导致的启动失败的日志：

Platform Subtype : 0
        DtPlatformLoadDtb qcs6490-rb3gen2.dtb is loaded
        Platform Subtype : -2090817768
        DtPlatformLoadSign qcs6490-rb3gen2.sgn is loaded
        failed to authenticate image !
        Copy to clipboard
- **因 DTB 不可用导致失败**

    以下示例所示为因缺少 DTB 而导致的启动失败的日志：

DtPlatformLoadDtb qcs6490-rb3gen2.dtb is loading failed with Status = E
        DtPlatformDxeEntryPoint: no DTB blob could be loaded, defaulting to ACPI (Status == Not Found)
        Copy to clipboard

    按照下列步骤解决 DTB 不可用的问题：

    - 验证相应的 DTB 文件是否包含在打包的
`efi.bin` 文件中。
    - 在主机开发计算机本地安装 `efi.bin` 文件，以验证以下内容：

$ mount -t vfat efi.bin /mnt/
            
            $ ls -lR /mnt/
            Copy to clipboard

### 串行控制台不工作

若要解决串行控制台日志失败问题，需在中启用特定的驱动程序并将相关参数添加到内核启动参数中。

- 在内核配置文件中启用以下驱动程序：

    - `CONFIG_SERIAL_QCOM_GENI=y`
    - `CONFIG_SERIAL_QCOM_GENI_CONSOLE=y`
- 将以下参数添加到内核启动参数中：

console=ttyMSM0,115200n8
        Copy to clipboard
- 若要从内核获取早期启动消息，需将以下参数添加到内核启动参数中：

earlycon
        Copy to clipboard

### 调试 Remoteproc 失败

若要解决 remoteproc 失败问题，需捕获内核日志，使用匹配的固件签名文件并检查设备中的固件位置。

- 确保所有匹配的子系统镜像均已正确刷写，
未出现任何错误。
- 验证内核日志中是否存在任何错误。

    以下所示为内核日志示例：

0x000000000A27652C |   5198.790423:   qcom_q6v5_pas 3000000.remoteproc: fatal error received: err_inject_crash.c:413:Crash injected via Diag
        0x000000000A276689 |   5198.801061:   remoteproc remoteproc2: crash detected in 3000000.remoteproc: type fatal error
        0x000000000A2767A1 |   5198.809602:   remoteproc remoteproc2: handling crash #1 in 3000000.remoteproc
        0x000000000A27688E |   5198.816837:   remoteproc remoteproc2: recovering 3000000.remoteproc
        0x000000000A276971 |   5198.823784:   qcom_q6v5_pas 8a00000.remoteproc: subsystem event rejected
        Copy to clipboard
- 禁用以下子系统重启功能，确保
remoteproc 崩溃签名显示在内核日志中：

echo disabled > /sys/kernel/debug/remoteproc/remoteprocN/recovery
        Copy to clipboard
- 确认所有必要的固件文件是否存在于
`/lib/firmware/qcom/<SoC> in rootfs` 文件系统。

### 调试配置或符号未反映在镜像中

若要调试内核配置更改问题，即配置或符号未反映在镜像中，可按照下列步骤操作：

- 将调试配置驱动程序添加到
`arch/arm64/configs/qcom_debug.config` 文件中。
- 导出宏 `DEBUG_BUILD=1`，然后运行 `bitbake`
命令。

Note

旧的调试配置文件与定制 BSP 版本有关。
将更改添加到基本 BSP 版本的 `qcom.cfg` 文件中。

### 系统内存不足

使用 [内存不足](https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-12/debugging_linux_kernel.html#sub$Out_of_memory) 中提到的步骤解决系统内存不足的问题。

### 在启动过程中识别 DTB

当设备启动时，boot loader 会验证以下 ID 并加载相应的 DTB 文件：

qcom,msm-id = <x z>;
    qcom,board-id = <y y'>;
    Copy to clipboard

- x = ID for SoC
- z = ID for SoC revision (reserved field)
- y = ID for CDP, MTP (hardware variants), and Platform
- y’ = ID for Subtype (assumed 0 if absent)

Last Published: Dec 15, 2024

[Previous Topic
定制](https://docs.qualcomm.com/bundle/publicresource/80-70015-3SC/topics/customize.md) [Next Topic
参考资料](https://docs.qualcomm.com/bundle/publicresource/80-70015-3SC/topics/references.md)