Post

使用SystemView分析FreeRTOS应用

免费的RTOS Tracealyzer,SEGGER大法好

使用SystemView分析FreeRTOS应用

1.前言

       在使用FreeRTOS进行应用开发时经常会遇到普通的方式难以调试的问题,如栈内存不足等等,同时也希望对运行的多个Task进行实时的性能及资源占用的分析,通常的调试手段在这里就变的心有余而力不足了。以FreeRTOS为例,如何在长时间的运行过程中收集调试数据进行分析,以及如何调试不同的组件(如Queue,Notification,Semaphore等等)?这个时候就需要Trace工具帮忙了。针对RTOS的Trace需求,有多种商业工具可供选择,但其中可供免费使用的产品则寥寥无几。本文将针对SEGGER开发的SystemView Trace工具进行介绍。

2. SystemView

       SystemView是SEGGER开发的针对嵌入式系统的trace工具,支持多种RTOS,也支持自定义OS的移植(需实现trace API,参见User Manual)。其核心基于SEGGER RTT,一个Host-Target间的通信框架,可通过多种方式连接,除J-LINK之外还可以使用串口及TCP-IP协议,对非商业用途免费且无功能限制。

3. 环境准备

       我们使用的测试环境是STM32H750VB开发板,CPU为Cortex-M7,最高主频480MHz,程序位于QSPI Flash中,通过XIP方式运行。FreeRTOS为STM32CubeMX配置的未修改版本。运行一个自定义Task,Toggle开发板上的一个LED灯。

1
2
3
4
5
6
7
8
9
10
11
//测试Task
TaskHandle_t xTaskHelloHandle = NULL;
void vTaskHello(void *pvParameters) {
    for(;;) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
        SEGGER_SYSVIEW_PrintfTarget("Hello world @%lu\n", HAL_GetTick());
        //这里使用了SystemView系统库提供的输出函数,向主机输出Message
        vTaskDelay(1000);
    }
    vTaskDelete(NULL);
}

在启动时自动创建该Task:

1
xTaskCreate(vTaskHello, "hello_task", 1024, NULL, 7, NULL);

栈深度为1024字,优先级为7

4. SystemView trace库移植

首先下载目标板源码,将其中的 SEGGER 目录下代码以及 Sample 中的FreeRTOS代码添加到项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SystemView
├── Config
│   ├── Global.h
│   ├── SEGGER_RTT_Conf.h
│   ├── SEGGER_SYSVIEW_Conf.h
│   └── SEGGER_SYSVIEW_Config_FreeRTOS.c
└── SEGGER
    ├── SEGGER.h
    ├── SEGGER_RTT.c
    ├── SEGGER_RTT.h
    ├── SEGGER_RTT_ASM_ARMv7M.s
    ├── SEGGER_RTT_printf.c
    ├── SEGGER_SYSVIEW.c
    ├── SEGGER_SYSVIEW.h
    ├── SEGGER_SYSVIEW_ConfDefaults.h
    ├── SEGGER_SYSVIEW_FreeRTOS.c
    ├── SEGGER_SYSVIEW_FreeRTOS.h
    ├── SEGGER_SYSVIEW_Int.h
    └── Syscalls
        └── SEGGER_RTT_Syscalls_GCC.c
    
    3 directories, 16 files

接下来我们需要实现通过串口的RTT数据收发,这里使用一个额外的Task实现这个操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#define SYSVIEW_COMM_APP_HELLO_SIZE 32
#define SYSVIEW_COMM_TARGET_HELLO_SIZE 32

#define SYSVIEW_SINGLE_TX 256

U8 hello_message[SYSVIEW_COMM_TARGET_HELLO_SIZE] = {
    'S', 'E', 'G', 'G', 'E', 'R', ' ',
    'S', 'y', 's', 't', 'e', 'm', 'V', 'i', 'e', 'w',
    ' ', 'V', '0' + SEGGER_SYSVIEW_MAJOR,
    '.', '0' + (SEGGER_SYSVIEW_MINOR / 10),
    '0' + (SEGGER_SYSVIEW_MINOR % 10),
    '.', '0' + (SEGGER_SYSVIEW_REV / 10),
    '0' + (SEGGER_SYSVIEW_REV % 10),
    '\0', 0, 0, 0, 0, 0 
};

TaskHandle_t xTaskTraceCommHandle = NULL;
void vTaskTraceComm(void *pvParameters) {
    //获取Channel ID
    int channel_id = SEGGER_SYSVIEW_GetChannelID();

    //发送HELLO包
    HAL_UART_Transmit(&huart1, hello_message, SYSVIEW_COMM_TARGET_HELLO_SIZE, 1000);

    uint8_t rx_buf;
    uint8_t tx_buf[SYSVIEW_SINGLE_TX];

    //启动记录
    SEGGER_SYSVIEW_Start();

    //等待通过串口中断接收数据
    HAL_UART_Receive_IT(&huart1, &rx_buf, 0x01);

    for(;;) {
        if(xTaskNotifyWait(0x00, 0x01, NULL, pdMS_TO_TICKS(400)) == pdTRUE) {
            //接收到数据,写入到RTT缓存
            SEGGER_RTT_WriteDownBufferNoLock(channel_id, &rx_buf, 0x01);
            HAL_UART_Receive_IT(&huart1, &rx_buf, 0x01);
        }
        //获取上行缓存数据长度
        unsigned int tx_length = SEGGER_RTT_GetBytesInBuffer(channel_id);
        if(tx_length >= SYSVIEW_SINGLE_TX) { 
            //STM32 HAL代码有最大长度限制,这里默认设置为256字节,可调整
            //从RTT缓存中读出数据
            uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, SYSVIEW_SINGLE_TX);
            //通过串口发送,此处可使用中断方式发送,尚未实现。
            HAL_UART_Transmit(&huart1, tx_buf, num, 100);
        } else if (tx_length != 0){
            // 同上
            uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, tx_length);
            HAL_UART_Transmit(&huart1, tx_buf, num, 100);
        }
    }
    vTaskDelete(NULL);
}

//串口ISR
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART1) {
        //向Task发送通知,收到数据
        BaseType_t higher_woken = pdFALSE;
        xTaskNotifyFromISR(xTaskTraceCommHandle, 0x01, eSetBits, &higher_woken);
        portYIELD_FROM_ISR(higher_woken);
    }
}

创建Task:

1
xTaskCreate(vTaskTraceComm, "trace_task", 1024, NULL, 6, &xTaskTraceCommHandle);

在RTOS调度器启动之前执行 SEGGER_SYSVIEW_Conf()

默认情况下SystemView会从DWT的Cycle counter获取系统当前周期数用于生成时间戳:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*********************************************************************
*
* SystemView timestamp configuration
*/
#if !defined(SEGGER_SYSVIEW_GET_TIMESTAMP) && !defined(SEGGER_SYSVIEW_TIMESTAMP_BITS)
  #if SEGGER_SYSVIEW_CORE == SEGGER_SYSVIEW_CORE_CM3
    #define SEGGER_SYSVIEW_GET_TIMESTAMP() (*(U32 *)(0xE0001004)) // Retrieve a system timestamp. Cortex-M cycle counter.
    #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by clock source
  #else
    #define SEGGER_SYSVIEW_GET_TIMESTAMP() SEGGER_SYSVIEW_X_GetTimestamp() // Retrieve a system timestamp via user-defined function
    #define SEGGER_SYSVIEW_TIMESTAMP_BITS 32 // Define number of valid bits low-order delivered by SEGGER_SYSVIEW_X_GetTimestamp()
  #endif
#endif

在STM32CubeMX的初始化代码中该计数器默认未启用,在这里手工启用。

1
2
3
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

修改 FreeRTOSConfig.h 引入SystemView的trace宏:

1
2
3
4
5
6
7
8
/* USER CODE BEGIN Defines */   	      
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
    
#define INCLUDE_xTaskGetIdleTaskHandle 1
    
#include "SEGGER_SYSVIEW_FreeRTOS.h"
    
/* USER CODE END Defines */ 

至此,我们的移植过程基本结束。

注:在SEGGER代码中, _write 部分的实现存在签名不兼容的问题,此函数是用于将stdout通过RTT输出到SystemView,可直接删除 SEGGER/Syscalls/SEGGER_RTT_Syscalls_GCC.c ,也可修改函数签名使其与系统库一致。

注2:在初始化时使用了 strcpy() 函数,在测试环境中出现Hardfault问题,可在 SEGGER/SEGGER_RTT.c 中将

1
2
3
#ifndef STRCPY
  #define STRCPY(pDest, pSrc, NumBytes) strcpy((pDest), (pSrc))
#endif

替换为

1
2
3
#ifndef STRCPY
  #define STRCPY(pDest, pSrc, NumBytes) strncpy((pDest), (pSrc), (NumBytes))
#endif

初步推测为Null-Terminated的问题,有头绪欢迎联系指正。

2020-05-07更新:使用DMA传输以减少CPU开销

更新部分代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for(;;) {
    if(xTaskNotifyWait(0x00, 0x03, &notify_flag, pdMS_TO_TICKS(400)) == pdTRUE) {
        if(notify_flag & 0x01) {
            SEGGER_RTT_WriteDownBufferNoLock(channel_id, &rx_buf, 0x01);
            HAL_UART_Receive_IT(&huart1, &rx_buf, 0x01);
        }
        if(notify_flag & 0x02) {
            if(dma_in_progress) {
                dma_in_progress = 0;
                prev_send = xTaskGetTickCount();
            }
        }
    }
    if(dma_in_progress == 0 && xTaskGetTickCount() - prev_send >= pdMS_TO_TICKS(400)) {
        unsigned int tx_length = SEGGER_RTT_GetBytesInBuffer(channel_id);
        if(tx_length >= SYSVIEW_SINGLE_TX) {
            uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, SYSVIEW_SINGLE_TX);
            HAL_UART_Transmit_DMA(&huart1, tx_buf, num);
        } else if (tx_length != 0){
            uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, tx_length);
            HAL_UART_Transmit_DMA(&huart1, tx_buf, num);
        }
        dma_in_progress = 1;
    }
}

同时添加DMA传输完成中断

1
2
3
4
5
6
7
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART1) {
        BaseType_t higher_woken = pdFALSE;
        xTaskNotifyFromISR(xTaskTraceCommHandle, 0x02, eSetBits, &higher_woken);
        portYIELD_FROM_ISR(higher_woken);
    }
}

即可实现DMA传输

5. 软件使用

启动SystemView,在Target->Recorder Configuration中指定串口及波特率,连接后RESET目标板即可接收事件。如下图。

SystemView主界面

6. Patch

默认的FreeRTOS trace宏不足以收集足够的参数,因此需要Patch FreeRTOS。提供参考Patch如下,来自SEGGER的Sample目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
diff -ur Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h ../FreeRTOS/Source/include/FreeRTOS.h
--- Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h	2020-05-07 22:43:33.533067758 +0800
+++ ../FreeRTOS/Source/include/FreeRTOS.h	2020-05-07 22:41:30.520012215 +0800
@@ -156,6 +156,10 @@
 	#define INCLUDE_uxTaskGetStackHighWaterMark 0
 #endif
 
+#ifndef INCLUDE_pxTaskGetStackStart
+	#define INCLUDE_pxTaskGetStackStart 0
+#endif
+
 #ifndef INCLUDE_uxTaskGetStackHighWaterMark2
 	#define INCLUDE_uxTaskGetStackHighWaterMark2 0
 #endif
@@ -400,6 +404,25 @@
 	#define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
 #endif
 
+#ifndef traceREADDED_TASK_TO_READY_STATE
+ #define traceREADDED_TASK_TO_READY_STATE( pxTCB ) traceMOVED_TASK_TO_READY_STATE( pxTCB )
+#endif
+
+#ifndef traceMOVED_TASK_TO_DELAYED_LIST
+ #define traceMOVED_TASK_TO_DELAYED_LIST()
+#endif
+
+#ifndef traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST
+ #define traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST()
+#endif
+
+#ifndef traceMOVED_TASK_TO_SUSPENDED_LIST
+ #define traceMOVED_TASK_TO_SUSPENDED_LIST( pxTCB )
+#endif
+
+
+
+
 #ifndef traceQUEUE_CREATE
 	#define traceQUEUE_CREATE( pxNewQueue )
 #endif
@@ -644,6 +667,18 @@
 	#define traceTASK_NOTIFY_GIVE_FROM_ISR()
 #endif
 
+#ifndef traceISR_EXIT_TO_SCHEDULER
+	#define traceISR_EXIT_TO_SCHEDULER()
+#endif
+
+#ifndef traceISR_EXIT
+	#define traceISR_EXIT()
+#endif
+
+#ifndef traceISR_ENTER
+	#define traceISR_ENTER()
+#endif
+
 #ifndef traceSTREAM_BUFFER_CREATE_FAILED
 	#define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer )
 #endif
diff -ur Middlewares/Third_Party/FreeRTOS/Source/include/task.h ../FreeRTOS/Source/include/task.h
--- Middlewares/Third_Party/FreeRTOS/Source/include/task.h	2020-05-07 22:43:33.529734437 +0800
+++ ../FreeRTOS/Source/include/task.h	2020-05-07 22:41:30.516678886 +0800
@@ -1438,6 +1438,25 @@
 
 /**
  * task.h
+ * <PRE>uint8_t* pxTaskGetStackStart( TaskHandle_t xTask);</PRE>
+ *
+ * INCLUDE_pxTaskGetStackStart must be set to 1 in FreeRTOSConfig.h for
+ * this function to be available.
+ *
+ * Returns the start of the stack associated with xTask. That is,
+ * the highest stack memory address on architectures where the stack grows down
+ * from high memory, and the lowest memory address on architectures where the
+ * stack grows up from low memory.
+ *
+ * @param xTask Handle of the task associated with the stack returned.
+ * Set xTask to NULL to return the stack of the calling task.
+ *
+ * @return A pointer to the start of the stack.
+ */
+uint8_t* pxTaskGetStackStart( TaskHandle_t xTask) PRIVILEGED_FUNCTION;
+
+/**
+ * task.h
  * <PRE>configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );</PRE>
  *
  * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for
diff -ur Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c ../FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c
--- Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c	2020-05-07 22:43:33.533067758 +0800
+++ ../FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c	2020-05-07 22:41:30.536678867 +0800
@@ -492,14 +492,20 @@
 	save and then restore the interrupt mask value as its value is already
 	known. */
 	portDISABLE_INTERRUPTS();
+	traceISR_ENTER();
 	{
 		/* Increment the RTOS tick. */
 		if( xTaskIncrementTick() != pdFALSE )
 		{
+ traceISR_EXIT_TO_SCHEDULER();
 			/* A context switch is required. Context switching is performed in
 			the PendSV interrupt. Pend the PendSV interrupt. */
 			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
 		}
+ else
+ {
+ traceISR_EXIT();
+ }
 	}
 	portENABLE_INTERRUPTS();
 }
diff -ur Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h ../FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h
--- Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h	2020-05-07 22:43:33.533067758 +0800
+++ ../FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h	2020-05-07 22:41:30.536678867 +0800
@@ -89,7 +89,7 @@
 
 #define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
 #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
-#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
+#define portEND_SWITCHING_ISR( xSwitchRequired ) { if( xSwitchRequired ) { traceISR_EXIT_TO_SCHEDULER(); portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } else { traceISR_EXIT(); } }
 #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
 /*-----------------------------------------------------------*/
 
diff -ur Middlewares/Third_Party/FreeRTOS/Source/tasks.c ../FreeRTOS/Source/tasks.c
--- Middlewares/Third_Party/FreeRTOS/Source/tasks.c	2020-05-07 22:43:33.549734367 +0800
+++ ../FreeRTOS/Source/tasks.c	2020-05-07 22:41:30.546678856 +0800
@@ -220,6 +220,17 @@
 	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
 	vListInsertEnd( &( pxReadyTasksLists[( pxTCB )->uxPriority] ), &( ( pxTCB )->xStateListItem ) ); \
 	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
+
+/*
+ * Place the task represented by pxTCB which has been in a ready list before
+ * into the appropriate ready list for the task.
+ * It is inserted at the end of the list.
+ */
+#define prvReaddTaskToReadyList( pxTCB ) \
+ traceREADDED_TASK_TO_READY_STATE( pxTCB ); \
+ taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
+ vListInsertEnd( &( pxReadyTasksLists[( pxTCB )->uxPriority] ), &( ( pxTCB )->xStateListItem ) ); \
+ tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
 /*-----------------------------------------------------------*/
 
 /*
@@ -1126,7 +1137,7 @@
 		#endif /* configUSE_TRACE_FACILITY */
 		traceTASK_CREATE( pxNewTCB );
 
- prvAddTaskToReadyList( pxNewTCB );
+ prvReaddTaskToReadyList( pxNewTCB );
 
 		portSETUP_TCB( pxNewTCB );
 	}
@@ -1726,6 +1737,8 @@
 				mtCOVERAGE_TEST_MARKER();
 			}
 
+ traceMOVED_TASK_TO_SUSPENDED_LIST(pxTCB);
+
 			vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
 
 			#if( configUSE_TASK_NOTIFICATIONS == 1 )
@@ -3821,6 +3834,20 @@
 #endif /* INCLUDE_uxTaskGetStackHighWaterMark */
 /*-----------------------------------------------------------*/
 
+#if (INCLUDE_pxTaskGetStackStart == 1)
+ uint8_t* pxTaskGetStackStart( TaskHandle_t xTask)
+ {
+ TCB_t *pxTCB;
+ UBaseType_t uxReturn;
+ (void)uxReturn;
+
+ pxTCB = prvGetTCBFromHandle( xTask );
+ return ( uint8_t * ) pxTCB->pxStack;
+ }
+
+#endif /* INCLUDE_pxTaskGetStackStart */
+/*-----------------------------------------------------------*/
+
 #if ( INCLUDE_vTaskDelete == 1 )
 
 	static void prvDeleteTCB( TCB_t *pxTCB )
@@ -3990,7 +4017,7 @@
 
 					/* Inherit the priority before being moved into the new list. */
 					pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
- prvAddTaskToReadyList( pxMutexHolderTCB );
+ prvReaddTaskToReadyList( pxMutexHolderTCB );
 				}
 				else
 				{
@@ -4080,7 +4107,7 @@
 					any other purpose if this task is running, and it must be
 					running to give back the mutex. */
 					listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
- prvAddTaskToReadyList( pxTCB );
+ prvReaddTaskToReadyList( pxTCB );
 
 					/* Return true to indicate that a context switch is required.
 					This is only actually required in the corner case whereby
@@ -5112,6 +5139,7 @@
 			/* Add the task to the suspended task list instead of a delayed task
 			list to ensure it is not woken by a timing event. It will block
 			indefinitely. */
+ traceMOVED_TASK_TO_SUSPENDED_LIST(pxCurrentTCB);
 			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
 		}
 		else
@@ -5128,12 +5156,14 @@
 			{
 				/* Wake time has overflowed. Place this item in the overflow
 				list. */
+ traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
 				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
 			}
 			else
 			{
 				/* The wake time has not overflowed, so the current block list
 				is used. */
+ traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
 				vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
 
 				/* If the task entering the blocked state was placed at the
@@ -5163,11 +5193,13 @@
 		if( xTimeToWake < xConstTickCount )
 		{
 			/* Wake time has overflowed. Place this item in the overflow list. */
+ traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
 			vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
 		}
 		else
 		{
 			/* The wake time has not overflowed, so the current block list is used. */
+ traceMOVED_TASK_TO_DELAYED_LIST();
 			vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
 
 			/* If the task entering the blocked state was placed at the head of the

7. 参考

User Manual

This post is licensed under CC BY 4.0 by the author.