background
- Software debugging, not much code is written, without debugging, you can know where the problem is, especially the RT_ASSERT assertion.
- There are more functions. After the RT_ASSERT assertion appears, it is difficult to immediately understand what went wrong, such as the rt_free assertion. Where did the call to rt_free cause it?
- How to use Keil MDK5 Debug to quickly assert the true position of the assertion and debug the software?
case study
- I wrote a BUG program before to test the use of RT-Thread mailbox, and must now assert: rt_free, because the application and release of dynamic memory are used.
- First of all, we need to locate the problem. We can't say that there is a problem with the rt-thread rt_free function! ! It should be caused by incorrect use.
- Keil MDK5 is in Debug mode, you can find the calling relationship of functions, such as the caller, in the debugging window [Call Stack], call stack window.
- Through [follow the vine], find the real calling function: the location of the problem! !
Troubleshooting
- The problem with this routine is caused by the rt_free null pointer, so it must be caused by the improper pair operation of applying and releasing memory.
- Because I didn’t find the problem at once, I set a breakpoint at the problem code location and found that there was a problem with the rt_free pointer during the second execution, or a wild pointer
- Quote: extern void list_mem(void); Add to print and find that the memory is getting more and more! ! (A lot of rt_free releases!!)
- The part of the code that went wrong is as follows:
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);
}
}
}
- It turned out that a place where the memory was applied for was misplaced, causing this function to only apply for memory once during initialization. When used, it is a wild pointer.
- Solution: Put the piece of code that applies for memory in the while(1) loop, and apply for memory for each operation! !
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);
}
}
}
to sum up
- RT_ASSERT is still relatively easy to use, pay attention to the correct breakpoints, debugging, very efficient.
- Summarize more debugging methods to solve actual problems.