RT-Thread 入门学习笔记 - Keil MDK软件调试排查assert

背景

  • 软件调试,写的代码不多,不用调试,就知道问题出现在哪里,尤其是必现的RT_ASSERT断言。
  • 功能多了,发现出现RT_ASSERT断言后,很难一下子明白哪里出了问题,如rt_free 断言,是哪个地方调用rt_free引起的呢?
  • 如何使用Keil MDK5 Debug,快速地位assert断言的真实位置,并调试软件?

案例分析

  • 自己之前写了一个BUG程序,测试RT-Thread邮箱的使用,必现assert断言:rt_free,因为使用了动态内存的申请与释放。
  • 首先,需要定位问题的所在,不能说rt-thread rt_free函数出了问题!!应该是使用不正确引起的。

2021-02-19_180711.png

2021-02-20_104007.png

2021-02-20_104124.png

  • Keil MDK5在Debug模式,可以在调试的窗口【Call Stack】,调用栈窗口,查找函数的调用关系,如调用者。

2021-02-20_104330.png

2021-02-20_105218.png

2021-02-20_105346.png

2021-02-20_105450.png

2021-02-20_105546.png

2021-02-20_105744.png

  • 通过【顺藤摸瓜】,找到真正的调用函数:出问题的位置!!

2021-02-20_105847.png

 

问题排查

  • 这个例程的问题,是rt_free 空指针引起的,所以,一定是申请与释放内存的成对操作不当引起的问题。
  • 因为一下子没发现问题所在,所以,我在问题代码位置,打上断点,发现,第二次执行时,rt_free的指针有问题,或者说是个野指针
  • 引用:extern void list_mem(void); 加入打印,发现内存越变越多了!!(rt_free释放的多!!)
  • 出问题的部分代码如下:
struct mb_msg
{
    rt_uint8_t *data_ptr;
    rt_uint32_t data_size;
};


static void thread1_entry(void *param)
{
    struct mb_msg *msg_recv_ptr1;
    struct mb_msg *msg_send_ptr1;
    char sbuf[6] = {'T', 'a', 's', 'k', '1', '.'};
    msg_send_ptr1 = (struct mb_msg *)rt_malloc(sizeof(struct mb_msg)); /* !!!! 申请内存的位置放错了!!!*/

    while(1)
    {
        if (rt_mb_recv(&t1_mb, (rt_ubase_t *)&msg_recv_ptr1, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("thread 1:[recv=%s], print 1.\n", msg_recv_ptr1->data_ptr);
            rt_thread_mdelay(10);
            rt_free(msg_recv_ptr1->data_ptr);
            rt_free(msg_recv_ptr1);
            rt_thread_mdelay(500);
            msg_send_ptr1->data_size = sizeof(sbuf);
            msg_send_ptr1->data_ptr = (rt_uint8_t *)rt_malloc(msg_send_ptr1->data_size);
            rt_memcpy(msg_send_ptr1->data_ptr, sbuf, sizeof(sbuf));
            rt_kprintf("thread 1:[send=%s]\n", msg_send_ptr1->data_ptr);
            rt_mb_send(&t2_mb, (rt_uint32_t)msg_send_ptr1);
        }
    }
}
  • 原来一处申请内存的位置,放错了,造成这个函数,只有初始化时申请一次内存,使用时,是个野指针。
  • 解决方法:把申请内存的那段代码,放在while(1) 循环里面,每次操作再申请内存!!
static void thread1_entry(void *param)
{
    struct mb_msg *msg_recv_ptr1;
    struct mb_msg *msg_send_ptr1;
    char sbuf[6] = {'T', 'a', 's', 'k', '1', '.'};

    while(1)
    {
        if (rt_mb_recv(&t1_mb, (rt_ubase_t *)&msg_recv_ptr1, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("thread 1:[recv=%s], print 1.\n", msg_recv_ptr1->data_ptr);
            rt_thread_mdelay(10);
            rt_free(msg_recv_ptr1->data_ptr);
            list_mem();
            rt_free(msg_recv_ptr1);
            list_mem();
            rt_thread_mdelay(500);
            msg_send_ptr1 = (struct mb_msg *)rt_malloc(sizeof(struct mb_msg));
            msg_send_ptr1->data_size = sizeof(sbuf);
            msg_send_ptr1->data_ptr = (rt_uint8_t *)rt_malloc(msg_send_ptr1->data_size);
            rt_memcpy(msg_send_ptr1->data_ptr, sbuf, sizeof(sbuf));
            rt_kprintf("thread 1:[send=%s]\n", msg_send_ptr1->data_ptr);
            rt_mb_send(&t2_mb, (rt_uint32_t)msg_send_ptr1);
        }
    }
}

总结

  • RT_ASSERT还是比较好用的,注意正确下断点,调试起来,非常的高效。
  • 多总结调试的方法,用于解决实际的问题。

猜你喜欢

转载自blog.csdn.net/tcjy1000/article/details/113915080