第一部分:U-Boot 阶段调试 (MT7621 MIPS32)

1.1 MT7621 U-Boot 启动流程与调试点

MT7621 U-Boot MIPS32 启动调用树
_start (arch/mips/cpu/mips32/start.S)
|
|-> reset (arch/mips/cpu/mips32/start.S)
    |
    |-> init_c0 (arch/mips/cpu/mips32/start.S)
    |   |
    |   |-> 初始化 CP0 状态寄存器
    |   |-> 设置异常向量基地址
    |
    |-> lowlevel_init (arch/mips/cpu/mips32/mt7621/lowlevel_init.S)
    |   |
    |   |-> 初始化内存控制器
    |   |-> 配置系统时钟
    |   |-> 设置串口调试
    |
    |-> relocate_code (arch/mips/lib/relocate.S)
    |   |
    |   |-> 代码重定位到 RAM
    |
    |-> board_init_f (common/board_f.c)
        |
        |-> init_sequence_f[] (common/board_f.c)
            |
            |-> mtk_timer_init (arch/mips/mach-mt7621/timer.c)
            |-> dram_init (arch/mips/mach-mt7621/sdram_mt7621.c)
            |-> mtk_serial_init (arch/mips/mach-mt7621/serial.c)
MT7621 串口调试初始化 (MIPS32)
// 文件: arch/mips/mach-mt7621/serial.c
​
void mtk_serial_init(void)
{
    // MT7621 UART Lite 基地址: 0xBE000000 (物理地址)
    struct mtk_uart *const uart = (struct mtk_uart *)0xBE000000;
    
    // 1. 使能 UART 时钟 - 系统控制器配置
    struct mtk_sysctl *sysctl = (struct mtk_sysctl *)MT7621_SYSCTL_BASE; // 0x10000000
    // 设置 SYSCTL_CLKCFG1[16] 为 UART 时钟使能
    writel(readl(&sysctl->clk_cfg1) | (1 << 16), &sysctl->clk_cfg1);
    
    // 2. 配置 GPIO 复用为 UART 功能
    // UART Lite: GPIO14 (TX), GPIO15 (RX)
    struct mtk_gpio *gpio = (struct mtk_gpio *)MT7621_GPIO_BASE; // 0x10000600
    // GPIO14 配置为 UART TX, GPIO15 配置为 UART RX
    writel((readl(&gpio->gpio_mode) & ~0x3) | 0x1, &gpio->gpio_mode); // 设置复用模式
    
    // 3. 配置 UART 参数: 57600 波特率, 8N1
    unsigned long clock_rate = 40000000;   // MT7621 UART 时钟频率 40MHz
    unsigned long baud_rate = 57600;       // 调试波特率
    unsigned long divisor = clock_rate / (baud_rate * 16);
    
    // 4. 设置 UART 寄存器
    uart->ier = 0;                         // 禁用中断
    uart->lcr = UART_LCR_DLAB;             // 使能分频锁存器访问
    uart->rbr = divisor & 0xff;            // 分频低字节
    uart->ier = (divisor >> 8) & 0xff;     // 分频高字节
    uart->lcr = UART_LCR_8BIT;             // 8位数据,无校验,1停止位
    uart->fcr = UART_FCR_FIFO_EN |         // 使能 FIFO
                UART_FCR_CLEAR_RCVR |      // 清除接收 FIFO
                UART_FCR_CLEAR_XMIT;       // 清除发送 FIFO
    
    // 5. 使能中断
    uart->ier = UART_IER_RDI;              // 使能接收数据可用中断
    
    printf("MT7621 UART Lite initialized at 0x%08lx, baud %lu\n", 
           (ulong)uart, baud_rate);
}

1.2 U-Boot 死在启动时的调试方法

使用 MT7621 串口调试输出
// 文件: common/console.c
​
// 串口输出字符函数 (MIPS32 实现)
void serial_putc(const char c)
{
    struct mtk_uart *uart = (struct mtk_uart *)CONFIG_DEBUG_UART_BASE; // 0xBE000000
    
    // 等待发送缓冲区空 - 检查线路状态寄存器 THRE 位
    while (!(readl(&uart->lsr) & UART_LSR_THRE)) {
        // MIPS32 空循环等待
        asm volatile ("nop");
    }
    
    // 发送字符到发送保持寄存器
    writel(c, &uart->rbr);
    
    // 如果是换行符,发送回车
    if (c == '\n')
        serial_putc('\r');
}
​
// 在关键启动位置插入调试标记
void board_init_f(ulong boot_flags)
{
    // 调试标记 A: 进入 board_init_f
    serial_putc('A');
    
    // 1. 初始化全局数据
    serial_putc('B');
    gd = (gd_t *)(CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_INIT_SP_OFFSET);
    memset(gd, 0, sizeof(gd_t));
    
    // 2. DRAM 初始化 - MT7621 关键步骤
    serial_putc('C'); 
    dram_init();
    
    // 3. 定时器初始化
    serial_putc('D');
    timer_init();
    
    // 4. 串口控制台初始化
    serial_putc('E');
    console_init_f();
    
    // 调试标记 F: board_init_f 完成
    serial_putc('F');
}
MT7621 DRAM 初始化失败调试
// 文件: arch/mips/mach-mt7621/sdram_mt7621.c
​
int dram_init(void)
{
    struct mt7621_ddr_params *params;
    int ret;
    
    printf("DRAM: Starting MT7621 DDR2 initialization\n");
    
    // 1. 获取 DRAM 配置参数
    params = get_ddr_config();
    if (!params) {
        printf("DRAM: Failed to get configuration\n");
        return -EINVAL;
    }
    
    // 2. 配置 DDR PHY
    printf("DRAM: Configuring PHY\n");
    ret = ddr_phy_config(params);
    if (ret) {
        printf("DRAM: PHY configuration failed: %d\n", ret);
        return ret;
    }
    
    // 3. 配置 DDR 控制器
    printf("DRAM: Configuring controller\n");
    ret = ddr_pctl_config(params);
    if (ret) {
        printf("DRAM: Controller configuration failed: %d\n", ret);
        return ret;
    }
    
    // 4. 执行内存校准
    printf("DRAM: Starting memory calibration\n");
    ret = ddr_calibration(params);
    if (ret) {
        printf("DRAM: Memory calibration failed: %d\n", ret);
        return ret;
    }
    
    // 5. 设置内存大小
    gd->ram_size = params->dram_size;
    printf("DRAM: Initialized %lu MB\n", gd->ram_size / 1024 / 1024);
    
    return 0;
}
​
// DDR 校准状态检查
static int check_calibration_status(void)
{
    struct mt7621_ddr_pctl *pctl = (struct mt7621_ddr_pctl *)MT7621_DDR_PCTL_BASE;
    u32 status;
    int timeout = 100000;  // 100ms 超时
    
    while (timeout--) {
        status = readl(&pctl->stat);
        if (status & DDR_CALIBRATION_DONE) {
            return 0;  // 校准完成
        }
        udelay(1);
    }
    
    printf("DRAM: Calibration timeout, status: 0x%08x\n", status);
    return -ETIMEDOUT;
}

第二部分:Linux 内核启动调试 (MT7621 MIPS32)

2.1 MT7621 MIPS32 内核启动流程

MIPS32 内核启动调用树
kernel_entry (arch/mips/kernel/head.S)
|
|-> kernel_entry_setup (arch/mips/kernel/head.S)
|   |
|   |-> 设置 CP0 状态寄存器
|   |-> 配置异常处理
|
|-> start_kernel (init/main.c)
    |
    |-> setup_arch (arch/mips/kernel/setup.c)
    |   |
    |   |-> prom_init (arch/mips/ralink/prom.c)
    |   |-> early_serial_setup (arch/mips/ralink/serial.c)
    |
    |-> trap_init (arch/mips/kernel/traps.c)
    |   |
    |   |-> 设置异常处理向量
    |
    |-> time_init (arch/mips/ralink/time.c)
    |   |
    |   |-> mtk_timer_init (arch/mips/ralink/mt7621/time.c)
    |
    |-> console_init (drivers/tty/serial/8250/8250_core.c)
MT7621 早期串口控制台初始化
// 文件: arch/mips/ralink/mt7621/serial.c
​
// 早期控制台设置
void __init early_serial_setup(void)
{
    // 映射 UART Lite 物理地址到虚拟地址
    // MT7621 UART Lite 物理地址: 0xBE000000
    void __iomem *uart_base = ioremap_nocache(0xBE000000, SZ_4K);
    
    if (!uart_base) {
        pr_err("Failed to map debug UART\n");
        return;
    }
    
    // 配置早期控制台
    early_console_init(uart_base);
}
​
// 文件: arch/mips/include/asm/serial.h
​
// MIPS32 串口调试宏
#define UART_MT7621_BASE    0xBE000000
​
// 早期打印字符函数
static inline void prom_putchar(char c)
{
    volatile u8 *uart = (volatile u8 *)UART_MT7621_BASE;
    
    // 等待发送缓冲区空
    while (!(uart[5] & 0x20))
        ;
    
    // 发送字符
    uart[0] = c;
    
    // 如果是换行符,发送回车
    if (c == '\n')
        prom_putchar('\r');
}

2.2 内核死在启动时的调试方法

使用 earlyprintk 调试 MIPS32 内核
// 在内核命令行添加: earlyprintk=serial,0xBE000000,57600
​
// 文件: arch/mips/kernel/early_printk.c
​
static struct earlycon_device early_console_dev;
​
void __init early_printk(const char *fmt, ...)
{
    char buf[512];
    va_list args;
    int i;
    
    // 1. 格式化字符串
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
    
    // 2. 通过串口输出每个字符
    for (i = 0; buf[i] && i < sizeof(buf); i++) {
        prom_putchar(buf[i]);
    }
}
​
// 在关键启动函数中添加调试输出
asmlinkage void __init start_kernel(void)
{
    early_printk("MIPS32: Entering start_kernel\n");
    
    // 设置架构相关
    early_printk("MIPS32: Setting up architecture\n");
    setup_arch(&command_line);
    
    early_printk("MIPS32: Initializing traps\n");
    trap_init();
    
    early_printk("MIPS32: Initializing memory\n");
    mm_init();
    
    early_printk("MIPS32: Starting rest_init\n");
    rest_init();
}
MT7621 时钟初始化调试
// 文件: arch/mips/ralink/mt7621/time.c
​
void __init plat_time_init(void)
{
    struct clk *clk;
    unsigned long rate;
    int ret;
    
    pr_info("MT7621: Initializing clocks\n");
    
    // 1. 获取 CPU 时钟
    clk = clk_get(NULL, "cpu");
    if (IS_ERR(clk)) {
        pr_err("MT7621: Failed to get CPU clock\n");
        return;
    }
    
    rate = clk_get_rate(clk);
    pr_info("MT7621: CPU clock: %lu Hz\n", rate);
    
    // 2. 获取总线时钟
    clk = clk_get(NULL, "bus");
    if (IS_ERR(clk)) {
        pr_err("MT7621: Failed to get bus clock\n");
        return;
    }
    
    rate = clk_get_rate(clk);
    pr_info("MT7621: Bus clock: %lu Hz\n", rate);
    
    // 3. 初始化系统定时器
    ret = mtk_timer_init();
    if (ret) {
        pr_err("MT7621: Timer initialization failed: %d\n", ret);
        return;
    }
    
    pr_info("MT7621: Clock controller initialized\n");
}
​
// MT7621 定时器初始化
static int __init mtk_timer_init(void)
{
    struct device_node *np;
    void __iomem *base;
    int irq, ret;
    
    // 查找定时器节点
    np = of_find_compatible_node(NULL, NULL, "mediatek,timer");
    if (!np) {
        pr_err("MT7621: No timer node found\n");
        return -ENODEV;
    }
    
    // 映射定时器寄存器
    base = of_iomap(np, 0);
    if (!base) {
        pr_err("MT7621: Failed to map timer registers\n");
        return -ENOMEM;
    }
    
    // 获取中断
    irq = irq_of_parse_and_map(np, 0);
    if (irq <= 0) {
        pr_err("MT7621: Failed to get timer IRQ\n");
        return -EINVAL;
    }
    
    // 注册时钟事件设备
    ret = mtk_clockevent_init(base, irq);
    if (ret) {
        pr_err("MT7621: Clockevent init failed: %d\n", ret);
        return ret;
    }
    
    // 注册时钟源
    ret = mtk_clocksource_init(base);
    if (ret) {
        pr_err("MT7621: Clocksource init failed: %d\n", ret);
        return ret;
    }
    
    pr_info("MT7621: Timer initialized\n");
    return 0;
}

第三部分:根文件系统挂载调试 (MT7621)

3.1 MT7621 根文件系统挂载流程

MIPS32 挂载调用树
start_kernel (init/main.c)
|
|-> vfs_caches_init (fs/dcache.c)
|   |
|   |-> dcache_init (fs/dcache.c)
|   |-> inode_init (fs/inode.c)
|   |-> files_init (fs/file_table.c)
|
|-> mnt_init (fs/namespace.c)
|   |
|   |-> sysfs_init (fs/sysfs/mount.c)
|   |-> rootfs_init (fs/ramfs/inode.c)
|
|-> prepare_namespace (init/do_mounts.c)
    |
    |-> mount_root (init/do_mounts.c)
        |
        |-> mount_block_root (init/do_mounts.c)
            |
            |-> do_mount_root (init/do_mounts.c)
                |
                |-> vfs_kern_mount (fs/namespace.c)
MT7621 SPI Flash 驱动初始化 (MIPS32)
// 文件: drivers/mtd/spi-nor/spi-mt7621.c
​
static int mtk_spi_probe(struct platform_device *pdev)
{
    struct spi_controller *ctlr;
    struct mtk_spi *ms;
    struct resource *res;
    int irq, ret;
    
    pr_info("MT7621: Probing SPI controller\n");
    
    // 1. 分配 SPI 控制器
    ctlr = spi_alloc_master(&pdev->dev, sizeof(*ms));
    if (!ctlr)
        return -ENOMEM;
    
    ms = spi_controller_get_devdata(ctlr);
    platform_set_drvdata(pdev, ctlr);
    
    // 2. 获取寄存器资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ms->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(ms->base)) {
        ret = PTR_ERR(ms->base);
        goto err_put_ctlr;
    }
    
    // 3. 获取中断
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        ret = irq;
        goto err_put_ctlr;
    }
    
    // 4. 获取时钟
    ms->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(ms->clk)) {
        ret = PTR_ERR(ms->clk);
        goto err_put_ctlr;
    }
    
    // 5. MT7621 特定配置
    ctlr->dev.of_node = pdev->dev.of_node;
    ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
    ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
    ctlr->num_chipselect = 1;
    ctlr->setup = mtk_spi_setup;
    ctlr->transfer_one = mtk_spi_transfer_one;
    
    // 6. 注册 SPI 控制器
    ret = devm_spi_register_controller(&pdev->dev, ctlr);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register SPI controller: %d\n", ret);
        goto err_put_ctlr;
    }
    
    dev_info(&pdev->dev, "MT7621 SPI controller initialized\n");
    return 0;
    
err_put_ctlr:
    spi_controller_put(ctlr);
    return ret;
}

3.2 根文件系统挂载失败调试

使用 initramfs 进行调试
// 在内核命令行添加: root=/dev/mtdblock5 rw rootwait init=/bin/sh
​
// 文件: init/do_mounts.c
​
void __init prepare_namespace(void)
{
    int err;
    char *root_device_name = NULL;
    
    pr_emerg("MIPS32: Preparing namespace\n");
    
    // 1. 等待根设备就绪
    if (root_wait) {
        pr_emerg("Waiting for root device %s...\n", 
                 __bdevname(ROOT_DEV, root_device_name));
        
        // MIPS32 特定的设备等待
        while (driver_probe_done() != 0) {
            mdelay(100);
            touch_nmi_watchdog();
        }
    }
    
    // 2. 挂载根文件系统
    pr_emerg("Mounting root filesystem\n");
    err = mount_root();
    if (err) {
        pr_emerg("Failed to mount root fs on %s, err %d\n", 
                 root_device_name, err);
        
        // 打印 MTD 设备信息用于调试
        printk("Available MTD devices:\n");
        for (int i = 0; i < MAX_MTD_DEVICES; i++) {
            struct mtd_info *mtd = get_mtd_device(NULL, i);
            if (!IS_ERR(mtd)) {
                printk("mtd%d: %s, size 0x%08llx\n", 
                       i, mtd->name, mtd->size);
                put_mtd_device(mtd);
            }
        }
        
        panic("VFS: Unable to mount root fs");
    }
    
    // 3. 切换到根文件系统
    pr_emerg("Switching to root filesystem\n");
    err = sys_chdir("/root");
    if (err) {
        pr_emerg("Failed to change to root directory: %d\n", err);
        panic("VFS: Unable to change to root directory");
    }
    
    // 4. 执行 pivot_root
    err = sys_pivot_root(".", ".");
    if (err) {
        pr_emerg("Failed to pivot root: %d\n", err);
        panic("VFS: Unable to pivot root");
    }
    
    pr_emerg("MIPS32: Root filesystem mounted successfully\n");
}
MT7621 设备树存储节点配置
// 文件: arch/mips/boot/dts/mediatek/mt7621.dtsi
​
// SPI 控制器
spi0: spi@1e000000 {
    compatible = "mediatek,mt7621-spi";
    reg = <0x1e000000 0x1000>;
    interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&sysclock>;
    clock-names = "spi";
    resets = <&sysreset 18>;
    status = "disabled";
};
​
// 文件: arch/mips/boot/dts/mediatek/mt7621-rfb.dts
​
&spi0 {
    status = "okay";
    
    flash@0 {
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <50000000>;
        
        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;
            
            partition@0 {
                label = "u-boot";
                reg = <0x0 0x30000>;
            };
            
            partition@30000 {
                label = "u-boot-env";
                reg = <0x30000 0x10000>;
            };
            
            partition@40000 {
                label = "factory";
                reg = <0x40000 0x10000>;
            };
            
            partition@50000 {
                label = "firmware";
                reg = <0x50000 0xfb0000>;
            };
        };
    };
};

第四部分:应用程序启动调试 (MIPS32)

4.1 MIPS32 应用程序调试准备

交叉编译 GDB 和 GDBServer
# 配置 MIPS32 交叉编译工具链
export CROSS_COMPILE=mipsel-openwrt-linux-
export ARCH=mips
​
# 编译 GDBServer
cd gdb-9.2/gdb/gdbserver
./configure --host=mipsel-openwrt-linux --target=mipsel-openwrt-linux
make
​
# 复制到 MT7621 设备
scp gdbserver root@192.168.1.1:/usr/bin/
​
# 编译 GDB (在开发机上)
cd gdb-9.2
./configure --target=mipsel-openwrt-linux
make
MIPS32 应用程序调试符号
// 文件: app_main.c (MIPS32 应用程序)
​
#include <signal.h>
#include <execinfo.h>
​
#define BT_BUF_SIZE 100
​
// MIPS32 回溯处理函数
void mips32_backtrace(int sig)
{
    void *buffer[BT_BUF_SIZE];
    char **strings;
    int nptrs;
    FILE *fp;
    
    // 打开日志文件
    fp = fopen("/tmp/backtrace.log", "a");
    if (!fp) return;
    
    // 获取回溯
    nptrs = backtrace(buffer, BT_BUF_SIZE);
    fprintf(fp, "Backtrace (MIPS32) for PID %d, signal %d:\n", 
            getpid(), sig);
    
    // 解析符号
    strings = backtrace_symbols(buffer, nptrs);
    if (strings) {
        for (int i = 0; i < nptrs; i++) {
            fprintf(fp, "#%d %s\n", i, strings[i]);
        }
        free(strings);
    }
    
    fclose(fp);
    
    // 重新抛出信号
    signal(sig, SIG_DFL);
    raise(sig);
}
​
int main(int argc, char *argv[])
{
    // 设置信号处理
    signal(SIGSEGV, mips32_backtrace);
    signal(SIGABRT, mips32_backtrace);
    signal(SIGILL, mips32_backtrace);
    
    // MIPS32 特定初始化
    printf("MIPS32 Application starting on MT7621\n");
    
    // 主应用程序逻辑
    return application_main(argc, argv);
}

4.2 使用 GDB 调试 MIPS32 应用程序

GDBServer 启动配置
# 在 MT7621 设备上启动 GDBServer
gdbserver :2345 /usr/bin/app_main
​
# 在开发机上连接 GDB
mipsel-openwrt-linux-gdb
(gdb) target remote 192.168.1.1:2345
(gdb) file app_main
(gdb) break main
(gdb) continue
MIPS32 寄存器调试
// 文件: mips32_debug.c
​
#include <sys/ptrace.h>
#include <sys/user.h>
​
// MIPS32 用户寄存器结构 (来自 Linux 内核)
struct mips32_user_regs {
    unsigned long regs[32];    // 通用寄存器 $0-$31
    unsigned long lo;          // LO 寄存器
    unsigned long hi;          // HI 寄存器  
    unsigned long cp0_epc;     // CP0 EPC 寄存器
    unsigned long cp0_badvaddr;// CP0 BadVAddr 寄存器
    unsigned long cp0_status;  // CP0 Status 寄存器
    unsigned long cp0_cause;   // CP0 Cause 寄存器
};
​
void debug_mips32_registers(pid_t pid)
{
    struct mips32_user_regs regs;
    
    // 获取 MIPS32 寄存器
    if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
        perror("ptrace GETREGS failed");
        return;
    }
    
    printf("MIPS32 Registers for PID %d:\n", pid);
    printf("$0(zero): 0x%08lx $1(at): 0x%08lx $2(v0): 0x%08lx $3(v1): 0x%08lx\n",
           regs.regs[0], regs.regs[1], regs.regs[2], regs.regs[3]);
    printf("$4(a0): 0x%08lx $5(a1): 0x%08lx $6(a2): 0x%08lx $7(a3): 0x%08lx\n",
           regs.regs[4], regs.regs[5], regs.regs[6], regs.regs[7]);
    printf("$8(t0): 0x%08lx $9(t1): 0x%08lx $10(t2): 0x%08lx $11(t3): 0x%08lx\n",
           regs.regs[8], regs.regs[9], regs.regs[10], regs.regs[11]);
    printf("$12(t4): 0x%08lx $13(t5): 0x%08lx $14(t6): 0x%08lx $15(t7): 0x%08lx\n",
           regs.regs[12], regs.regs[13], regs.regs[14], regs.regs[15]);
    printf("$16(s0): 0x%08lx $17(s1): 0x%08lx $18(s2): 0x%08lx $19(s3): 0x%08lx\n",
           regs.regs[16], regs.regs[17], regs.regs[18], regs.regs[19]);
    printf("$20(s4): 0x%08lx $21(s5): 0x%08lx $22(s6): 0x%08lx $23(s7): 0x%08lx\n",
           regs.regs[20], regs.regs[21], regs.regs[22], regs.regs[23]);
    printf("$24(t8): 0x%08lx $25(t9): 0x%08lx $26(k0): 0x%08lx $27(k1): 0x%08lx\n",
           regs.regs[24], regs.regs[25], regs.regs[26], regs.regs[27]);
    printf("$28(gp): 0x%08lx $29(sp): 0x%08lx $30(s8): 0x%08lx $31(ra): 0x%08lx\n",
           regs.regs[28], regs.regs[29], regs.regs[30], regs.regs[31]);
    printf("LO: 0x%08lx HI: 0x%08lx EPC: 0x%08lx\n",
           regs.lo, regs.hi, regs.cp0_epc);
    printf("Status: 0x%08lx Cause: 0x%08lx BadVAddr: 0x%08lx\n",
           regs.cp0_status, regs.cp0_cause, regs.cp0_badvaddr);
}

第五部分:内存泄漏调试 (MIPS32)

5.1 MIPS32 内核内存泄漏检测

使用 kmemleak 检测 MIPS32 内核内存泄漏
// 在内核命令行添加: kmemleak=on
​
// 文件: mm/kmemleak.c
​
// MIPS32 特定的内存分配跟踪
void *__kmalloc(size_t size, gfp_t gfp)
{
    void *ptr;
    
    // MIPS32 使用 kmalloc_caches
    ptr = __do_kmalloc(size, gfp, _RET_IP_);
    
    // kmemleak 记录
    if (kmemleak_enabled && ptr && !(gfp & __GFP_NOLEAKTRACE)) {
        kmemleak_alloc(ptr, size, 1, gfp);
    }
    
    return ptr;
}
​
// MIPS32 vmalloc 跟踪
void *vmalloc(unsigned long size)
{
    void *addr;
    
    // MIPS32 vmalloc 区域在 0xc0000000 以上
    addr = __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,
                               GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
                               __builtin_return_address(0));
    
    // kmemleak 记录 vmalloc 分配
    if (kmemleak_enabled && addr) {
        kmemleak_alloc(addr, size, 2, GFP_KERNEL);
    }
    
    return addr;
}
MT7621 特定驱动的内存泄漏检测
// 文件: drivers/net/ethernet/mediatek/mtk_eth_soc.c
​
struct mtk_mac {
    struct device *dev;
    struct mtk_eth *eth;
    int id;
    struct phy_device *phy;
    u32 msg_enable;
};
​
static int mtk_probe(struct platform_device *pdev)
{
    struct mtk_eth *eth;
    struct resource *res;
    int i, ret;
    
    // 分配以太网控制器结构
    eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
    if (!eth)
        return -ENOMEM;
    
    // kmemleak 跟踪
    kmemleak_alloc(eth, sizeof(*eth), 1, GFP_KERNEL);
    
    platform_set_drvdata(pdev, eth);
    eth->dev = &pdev->dev;
    
    // 分配 MAC 结构
    eth->mac = devm_kcalloc(&pdev->dev, MTK_MAC_COUNT,
                           sizeof(*eth->mac), GFP_KERNEL);
    if (!eth->mac) {
        ret = -ENOMEM;
        goto free_eth;
    }
    
    kmemleak_alloc(eth->mac, sizeof(*eth->mac) * MTK_MAC_COUNT, 1, GFP_KERNEL);
    
    // 初始化每个 MAC
    for (i = 0; i < MTK_MAC_COUNT; i++) {
        eth->mac[i] = devm_kzalloc(&pdev->dev, sizeof(struct mtk_mac),
                                  GFP_KERNEL);
        if (!eth->mac[i]) {
            ret = -ENOMEM;
            goto free_macs;
        }
        
        kmemleak_alloc(eth->mac[i], sizeof(struct mtk_mac), 1, GFP_KERNEL);
        eth->mac[i]->id = i;
        eth->mac[i]->eth = eth;
    }
    
    return 0;
    
free_macs:
    for (i = 0; i < MTK_MAC_COUNT; i++) {
        if (eth->mac[i]) {
            kmemleak_free(eth->mac[i]);
            devm_kfree(&pdev->dev, eth->mac[i]);
        }
    }
    kmemleak_free(eth->mac);
    
free_eth:
    kmemleak_free(eth);
    return ret;
}

5.2 MIPS32 用户空间内存泄漏检测

使用 mtrace 检测应用程序内存泄漏
// 文件: app_main.c
​
#include <mcheck.h>
​
int main(int argc, char *argv[])
{
    // 启用内存跟踪
    setenv("MALLOC_TRACE", "/tmp/app_mtrace.log", 1);
    mtrace();
    
    // 应用程序逻辑
    application_main(argc, argv);
    
    // 关闭内存跟踪
    muntrace();
    
    return 0;
}
​
// 在开发机上分析内存泄漏
// bash
mtrace app_main /tmp/app_mtrace.log
MIPS32 内存调试包装函数
// 文件: mips32_mem_debug.c
​
#ifdef MIPS32_MEMORY_DEBUG
​
#include <execinfo.h>
​
// MIPS32 内存分配跟踪
void *mips32_debug_malloc(size_t size, const char *file, int line)
{
    void *ptr = malloc(size);
    
    if (ptr) {
        void *bt[10];
        int frames = backtrace(bt, 10);
        char **symbols = backtrace_symbols(bt, frames);
        
        fprintf(stderr, "MIPS32 MALLOC: %p %zu bytes at %s:%d\n", 
                ptr, size, file, line);
        fprintf(stderr, "Backtrace:\n");
        for (int i = 0; i < frames; i++) {
            fprintf(stderr, "  #%d %s\n", i, symbols[i]);
        }
        
        free(symbols);
    }
    
    return ptr;
}
​
void mips32_debug_free(void *ptr, const char *file, int line)
{
    if (ptr) {
        fprintf(stderr, "MIPS32 FREE: %p at %s:%d\n", ptr, file, line);
        free(ptr);
    }
}
​
// 内存使用报告
void mips32_memory_report(void)
{
    struct mallinfo mi = mallinfo();
    
    fprintf(stderr, "MIPS32 Memory Report:\n");
    fprintf(stderr, "  Total non-mmapped bytes: %d\n", mi.arena);
    fprintf(stderr, "  Number of free chunks: %d\n", mi.ordblks);
    fprintf(stderr, "  Bytes in free chunks: %d\n", mi.fordblks);
    fprintf(stderr, "  Maximum total allocated space: %d\n", mi.usmblks);
    fprintf(stderr, "  Bytes in mmapped regions: %d\n", mi.hblks);
    fprintf(stderr, "  Total allocated space: %d\n", mi.uordblks);
    fprintf(stderr, "  Total free space: %d\n", mi.fordblks);
    fprintf(stderr, "  Releasable free space: %d\n", mi.keepcost);
}
​
#endif

第六部分:系统崩溃和稳定性调试

6.1 MIPS32 内核崩溃分析

MIPS32 Oops 分析
// 文件: arch/mips/kernel/traps.c
​
// MIPS32 异常处理
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
                             unsigned long address)
{
    struct task_struct *tsk = current;
    struct mm_struct *mm = tsk->mm;
    struct vm_area_struct *vma;
    int fault;
    
    // 检查地址是否在用户空间
    if (address < TASK_SIZE) {
        // 用户空间页错误
        if (!mm) {
            // 没有内存映射
            pr_alert("MIPS32: Page fault in NULL mm, address 0x%08lx\n",
                     address);
            goto bad_area_nosemaphore;
        }
    } else {
        // 内核空间页错误
        pr_alert("MIPS32: Kernel page fault, address 0x%08lx\n", address);
        goto no_context;
    }
    
    // 尝试处理页错误
    fault = handle_mm_fault(vma, address, write ? FAULT_FLAG_WRITE : 0, regs);
    
    if (fault & VM_FAULT_ERROR) {
        // 页错误处理失败
        if (fault & VM_FAULT_OOM) {
            pr_alert("MIPS32: Out of memory\n");
            goto out_of_memory;
        } else if (fault & VM_FAULT_SIGBUS) {
            pr_alert("MIPS32: SIGBUS error\n");
            goto do_sigbus;
        }
    }
    
    return;
    
bad_area_nosemaphore:
    // 用户空间错误区域
    pr_alert("MIPS32: Bad area access\n");
    force_sig_fault(SIGSEGV, SEGV_MAPERR, (void __user *)address);
    return;
    
no_context:
    // 内核空间无上下文
    pr_alert("MIPS32: No context for kernel page fault\n");
    die("Oops", regs);
    return;
    
out_of_memory:
    // 内存不足
    pr_alert("MIPS32: Out of memory\n");
    if (!user_mode(regs))
        goto no_context;
    pagefault_out_of_memory();
    return;
    
do_sigbus:
    // 总线错误
    pr_alert("MIPS32: Bus error\n");
    force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address);
}
​
// MIPS32 寄存器显示
void show_regs(struct pt_regs *regs)
{
    pr_emerg("MIPS32 Registers:\n");
    pr_emerg("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
             regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3],
             regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]);
    pr_emerg("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
             regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11],
             regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]);
    pr_emerg("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
             regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19],
             regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]);
    pr_emerg("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
             regs->regs[24], regs->regs[25], regs->regs[26], regs->regs[27],
             regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]);
    pr_emerg("Hi : %08lx\nLo : %08lx\n", regs->hi, regs->lo);
    pr_emerg("epc   : %08lx %pS\n", regs->cp0_epc, (void *)regs->cp0_epc);
    pr_emerg("ra    : %08lx %pS\n", regs->regs[31], (void *)regs->regs[31]);
}

6.2 MT7621 系统稳定性监控

系统监控脚本
#!/bin/bash
# mt7621_monitor.sh
​
# 监控系统状态
while true; do
    echo "=== MT7621 System Status $(date) ==="
    
    # CPU 使用率
    echo "CPU Usage:"
    mpstat 1 1 | tail -1
    
    # 内存使用
    echo "Memory Usage:"
    free -m
    
    # 温度监控
    if [ -f /sys/class/thermal/thermal_zone0/temp ]; then
        temp=$(cat /sys/class/thermal/thermal_zone0/temp)
        echo "CPU Temperature: $((temp/1000))°C"
    fi
    
    # 网络统计
    echo "Network Statistics:"
    netstat -i
    
    # 进程内存泄漏检查
    echo "Top memory processes:"
    ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -10
    
    sleep 30
done

更多推荐