//作用:包含統計任務的程式碼,用來計算全域性CPU使用率以及每個任務的CPU使用率;
/*
*********************************************************************************************************
* uC/OS-III
* The Real-Time Kernel
*
* Copyright 2009-2022 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* STATISTICS MODULE
*
* File : os_stat.c
* Version : V3.08.02
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#include "os.h"
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const CPU_CHAR *os_stat__c = "$Id: $";
#endif
#if (OS_CFG_STAT_TASK_EN > 0u)
/*
************************************************************************************************************************
* RESET STATISTICS
*
* Description: This function is called by your application to reset the statistics.
*
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_NONE The call succeeded
*
* Returns : none
*
* Note(s) : none
************************************************************************************************************************
*/
// 重置作業系統統計資訊
void OSStatReset(OS_ERR *p_err)
{
// 除錯配置部分
#if (OS_CFG_DBG_EN > 0u)
OS_TCB *p_tcb; // 指向任務控制塊的指標
#if (OS_MSG_EN > 0u)
OS_MSG_Q *p_msg_q; // 指向訊息佇列的指標
#endif
#if (OS_CFG_Q_EN > 0u)
OS_Q *p_q; // 指向佇列的指標
#endif
#endif
CPU_SR_ALLOC(); // 分配狀態暫存器空間
// 安全關鍵配置
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
// 進入臨界區
CPU_CRITICAL_ENTER();
#if (OS_CFG_STAT_TASK_EN > 0u)
OSStatTaskCPUUsageMax = 0u; // 重置任務統計的最大CPU使用率
#if (OS_CFG_TS_EN > 0u)
OSStatTaskTimeMax = 0u; // 重置任務統計的最大時間
#endif
#endif
#if (OS_CFG_TS_EN > 0u) && (OS_CFG_TICK_EN > 0u)
OSTickTime = 0u; // 重置系統滴答時間
OSTickTimeMax = 0u; // 重置最大系統滴答時間
#endif
#if (OS_CFG_TMR_EN > 0u)
#if (OS_CFG_TS_EN > 0u)
OSTmrTaskTime = 0u; // 重置定時器任務時間
OSTmrTaskTimeMax = 0u; // 重置最大定時器任務時間
#endif
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
#if (OS_CFG_TS_EN > 0u)
OSIntDisTimeMax = 0u; // 重置最大中斷禁用時間
CPU_StatReset(); // 重置CPU特定的效能監控器
#endif
#endif
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
OSSchedLockTimeMax = 0u; // 重置最大排程鎖定時間
#endif
#if ((OS_MSG_EN > 0u) && (OS_CFG_DBG_EN > 0u))
OSMsgPool.NbrUsedMax = 0u; // 重置訊息池的最大使用數量
#endif
// 退出臨界區
CPU_CRITICAL_EXIT();
#if (OS_CFG_DBG_EN > 0u)
// 進入臨界區
CPU_CRITICAL_ENTER();
p_tcb = OSTaskDbgListPtr; // 獲取任務除錯列表指標
CPU_CRITICAL_EXIT();
// 重置每個任務的統計資訊
while (p_tcb != (OS_TCB *)0) {
// 進入臨界區
CPU_CRITICAL_ENTER();
#ifdef CPU_CFG_INT_DIS_MEAS_EN
p_tcb->IntDisTimeMax = 0u; // 重置任務的最大中斷禁用時間
#endif
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
p_tcb->SchedLockTimeMax = 0u; // 重置任務的最大排程鎖定時間
#endif
#if (OS_CFG_TASK_PROFILE_EN > 0u)
#if (OS_CFG_TASK_Q_EN > 0u)
p_tcb->MsgQPendTimeMax = 0u; // 重置任務的訊息佇列等待最大時間
#endif
p_tcb->SemPendTimeMax = 0u; // 重置任務的訊號量等待最大時間
p_tcb->CtxSwCtr = 0u; // 重置任務的上下文切換計數
p_tcb->CPUUsage = 0u; // 重置任務的CPU使用率
p_tcb->CPUUsageMax = 0u; // 重置任務的最大CPU使用率
p_tcb->CyclesTotal = 0u; // 重置任務的總週期數
p_tcb->CyclesTotalPrev = 0u; // 重置任務的前一個總週期數
#if (OS_CFG_TS_EN > 0u)
p_tcb->CyclesStart = OS_TS_GET(); // 重置任務的起始週期
#endif
#endif
#if (OS_CFG_TASK_Q_EN > 0u)
p_msg_q = &p_tcb->MsgQ; // 獲取任務的訊息佇列
p_msg_q->NbrEntriesMax = 0u; // 重置訊息佇列的最大條目數
#endif
p_tcb = p_tcb->DbgNextPtr; // 獲取下一個任務控制塊
// 退出臨界區
CPU_CRITICAL_EXIT();
}
#endif
#if (OS_CFG_Q_EN > 0u) && (OS_CFG_DBG_EN > 0u)
// 進入臨界區
CPU_CRITICAL_ENTER();
p_q = OSQDbgListPtr; // 獲取佇列除錯列表指標
CPU_CRITICAL_EXIT();
// 重置訊息佇列的統計資訊
while (p_q != (OS_Q *)0) {
// 進入臨界區
CPU_CRITICAL_ENTER();
p_msg_q = &p_q->MsgQ; // 獲取佇列的訊息佇列
p_msg_q->NbrEntriesMax = 0u; // 重置訊息佇列的最大條目數
p_q = p_q->DbgNextPtr; // 獲取下一個佇列
// 退出臨界區
CPU_CRITICAL_EXIT();
}
#endif
*p_err = OS_ERR_NONE; // 設定錯誤程式碼為無錯誤
}
/*
************************************************************************************************************************
* DETERMINE THE CPU CAPACITY
*
* Description: This function is called by your application to establish CPU usage by first determining how high a 32-bit
* counter would count to in 1/10 second if no other tasks were to execute during that time. CPU usage is
* then determined by a low priority task which keeps track of this 32-bit counter every second but this
* time, with other tasks running. CPU usage is determined by:
*
* OS_Stat_IdleCtr
* CPU Usage (%) = 100 * (1 - ------------------)
* OS_Stat_IdleCtrMax
*
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_NONE The call was successful
* OS_ERR_OS_NOT_RUNNING If uC/OS-III is not running yet
*
* Returns : none
*
* Note(s) : none
************************************************************************************************************************
*/
/**
* @brief 初始化CPU使用率統計任務
*
* 該函式用於初始化統計CPU使用率的任務, 包括校驗引數有效性、同步時鐘滴答、
* 清空空閒計數器、計算最大空閒計數器值、恢復定時器任務等步驟。
*
* @param p_err 指向錯誤程式碼的指標,用於返回函式執行結果
*/
void OSStatTaskCPUUsageInit (OS_ERR *p_err)
{
OS_ERR err;
OS_TICK dly;
CPU_SR_ALLOC();
// 顯式初始化err,以便於靜態分析
err = OS_ERR_NONE;
// 安全關鍵程式碼檢查:確保p_err引數有效
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
// 檢查作業系統是否正在執行
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
// 暫停定時器任務,如果配置支援
#if ((OS_CFG_TMR_EN > 0u) && (OS_CFG_TASK_SUSPEND_EN > 0u))
OSTaskSuspend(&OSTmrTaskTCB, &err);
if (err != OS_ERR_NONE) {
*p_err = err;
return;
}
#endif
// 同步時鐘滴答,確保時間準確性
OSTimeDly(2u,
(OS_OPT )OS_OPT_TIME_DLY,
(OS_ERR *)&err);
if (err != OS_ERR_NONE) {
*p_err = err;
return;
}
// 進入臨界區,清空空閒計數器
CPU_CRITICAL_ENTER();
OSStatTaskCtr = 0u;
CPU_CRITICAL_EXIT();
// 計算最大空閒計數器值
dly = 0u;
if (OSCfg_TickRate_Hz > OSCfg_StatTaskRate_Hz) {
dly = (OS_TICK)(OSCfg_TickRate_Hz / OSCfg_StatTaskRate_Hz);
}
if (dly == 0u) {
dly = (OSCfg_TickRate_Hz / 10u);
}
// 延時以確定最大空閒計數器值
OSTimeDly(dly,
OS_OPT_TIME_DLY,
&err);
// 恢復定時器任務,如果配置支援
#if ((OS_CFG_TMR_EN > 0u) && (OS_CFG_TASK_SUSPEND_EN > 0u))
OSTaskResume(&OSTmrTaskTCB, &err);
if (err != OS_ERR_NONE) {
*p_err = err;
return;
}
#endif
// 進入臨界區,初始化統計任務的相關變數
CPU_CRITICAL_ENTER();
#if (OS_CFG_TS_EN > 0u)
OSStatTaskTimeMax = 0u;
#endif
// 儲存最大空閒計數器值,設定統計任務就緒狀態
OSStatTaskCtrMax = OSStatTaskCtr;
OSStatTaskRdy = OS_STATE_RDY;
CPU_CRITICAL_EXIT();
// 設定錯誤程式碼為無錯誤
*p_err = OS_ERR_NONE;
}
/*
************************************************************************************************************************
* STATISTICS TASK
*
* Description: This task is internal to uC/OS-III and is used to compute some statistics about the multitasking
* environment. Specifically, OS_StatTask() computes the CPU usage. CPU usage is determined by:
*
* OSStatTaskCtr
* OSStatTaskCPUUsage = 100 * (1 - ------------------) (units are in %)
* OSStatTaskCtrMax
*
* Arguments : p_arg this pointer is not used at this time.
*
* Returns : none
*
* Note(s) : 1) This task runs at a priority level higher than the idle task.
*
* 2) You can disable this task by setting the configuration #define OS_CFG_STAT_TASK_EN to 0.
*
* 3) You MUST have at least a delay of 2/10 seconds to allow for the system to establish the maximum value
* for the idle counter.
*
* 4) This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/
/**
* @brief 作業系統統計任務函式
*
* 該函式是作業系統的一個任務,負責收集和記錄系統的執行統計資訊。透過傳入的引數來執行具體的操作。
*
* @param p_arg 指向任務引數的指標。根據具體實現的不同,這個引數可以用來傳遞各種型別的資料。
* 例如,它可以是一個結構體的指標,包含任務需要的所有引數。
*/
void OS_StatTask (void *p_arg)
{
#if (OS_CFG_DBG_EN > 0u)
#if (OS_CFG_TASK_PROFILE_EN > 0u)
OS_CPU_USAGE usage;
OS_CYCLES cycles_total;
OS_CYCLES cycles_div;
OS_CYCLES cycles_mult;
OS_CYCLES cycles_max;
#endif
OS_TCB *p_tcb;
#endif
OS_TICK ctr_max;
OS_TICK ctr_mult;
OS_TICK ctr_div;
OS_ERR err;
OS_TICK dly;
#if (OS_CFG_TS_EN > 0u)
CPU_TS ts_start;
#endif
#if (OS_CFG_STAT_TASK_STK_CHK_EN > 0u) && (OS_CFG_ISR_STK_SIZE > 0u)
CPU_STK *p_stk;
CPU_INT32U free_stk;
CPU_INT32U size_stk;
#endif
CPU_SR_ALLOC();
(void)p_arg; /* Prevent compiler warning for not using 'p_arg' */
while (OSStatTaskRdy != OS_TRUE) {
OSTimeDly(2u * OSCfg_StatTaskRate_Hz, /* Wait until statistic task is ready */
OS_OPT_TIME_DLY,
&err);
}
OSStatReset(&err); /* Reset statistics */
dly = (OS_TICK)0; /* Compute statistic task sleep delay */
if (OSCfg_TickRate_Hz > OSCfg_StatTaskRate_Hz) {
dly = (OSCfg_TickRate_Hz / OSCfg_StatTaskRate_Hz);
}
if (dly == 0u) {
dly = (OSCfg_TickRate_Hz / 10u);
}
for (;;) {
#if (OS_CFG_TS_EN > 0u)
ts_start = OS_TS_GET();
#ifdef CPU_CFG_INT_DIS_MEAS_EN
OSIntDisTimeMax = CPU_IntDisMeasMaxGet();
#endif
#endif
CPU_CRITICAL_ENTER(); /* ---------------- OVERALL CPU USAGE ----------------- */
OSStatTaskCtrRun = OSStatTaskCtr; /* Obtain the of the stat counter for the past .1 second*/
OSStatTaskCtr = 0u; /* Reset the stat counter for the next .1 second */
CPU_CRITICAL_EXIT();
if (OSStatTaskCtrMax > OSStatTaskCtrRun) { /* Compute CPU Usage with best resolution */
if (OSStatTaskCtrMax < 400000u) { /* 1 to 400,000 */
ctr_mult = 10000u;
ctr_div = 1u;
} else if (OSStatTaskCtrMax < 4000000u) { /* 400,000 to 4,000,000 */
ctr_mult = 1000u;
ctr_div = 10u;
} else if (OSStatTaskCtrMax < 40000000u) { /* 4,000,000 to 40,000,000 */
ctr_mult = 100u;
ctr_div = 100u;
} else if (OSStatTaskCtrMax < 400000000u) { /* 40,000,000 to 400,000,000 */
ctr_mult = 10u;
ctr_div = 1000u;
} else { /* 400,000,000 and up */
ctr_mult = 1u;
ctr_div = 10000u;
}
ctr_max = OSStatTaskCtrMax / ctr_div;
OSStatTaskCPUUsage = (OS_CPU_USAGE)((OS_TICK)10000u - ((ctr_mult * OSStatTaskCtrRun) / ctr_max));
if (OSStatTaskCPUUsageMax < OSStatTaskCPUUsage) {
OSStatTaskCPUUsageMax = OSStatTaskCPUUsage;
}
} else {
OSStatTaskCPUUsage = 0u;
}
OSStatTaskHook(); /* Invoke user definable hook */
#if (OS_CFG_DBG_EN > 0u)
#if (OS_CFG_TASK_PROFILE_EN > 0u)
cycles_total = 0u;
CPU_CRITICAL_ENTER();
p_tcb = OSTaskDbgListPtr;
CPU_CRITICAL_EXIT();
while (p_tcb != (OS_TCB *)0) { /* ---------------- TOTAL CYCLES COUNT ---------------- */
CPU_CRITICAL_ENTER();
p_tcb->CyclesTotalPrev = p_tcb->CyclesTotal; /* Save accumulated # cycles into a temp variable */
p_tcb->CyclesTotal = 0u; /* Reset total cycles for task for next run */
CPU_CRITICAL_EXIT();
cycles_total += p_tcb->CyclesTotalPrev; /* Perform sum of all task # cycles */
CPU_CRITICAL_ENTER();
p_tcb = p_tcb->DbgNextPtr;
CPU_CRITICAL_EXIT();
}
#endif
#if (OS_CFG_TASK_PROFILE_EN > 0u)
/* ------------ INDIVIDUAL TASK CPU USAGE ------------- */
if (cycles_total > 0u) { /* 'cycles_total' scaling ... */
if (cycles_total < 400000u) { /* 1 to 400,000 */
cycles_mult = 10000u;
cycles_div = 1u;
} else if (cycles_total < 4000000u) { /* 400,000 to 4,000,000 */
cycles_mult = 1000u;
cycles_div = 10u;
} else if (cycles_total < 40000000u) { /* 4,000,000 to 40,000,000 */
cycles_mult = 100u;
cycles_div = 100u;
} else if (cycles_total < 400000000u) { /* 40,000,000 to 400,000,000 */
cycles_mult = 10u;
cycles_div = 1000u;
} else { /* 400,000,000 and up */
cycles_mult = 1u;
cycles_div = 10000u;
}
cycles_max = cycles_total / cycles_div;
} else {
cycles_mult = 0u;
cycles_max = 1u;
}
#endif
CPU_CRITICAL_ENTER();
p_tcb = OSTaskDbgListPtr;
CPU_CRITICAL_EXIT();
while (p_tcb != (OS_TCB *)0) {
#if (OS_CFG_TASK_PROFILE_EN > 0u) /* Compute execution time of each task */
usage = (OS_CPU_USAGE)(cycles_mult * p_tcb->CyclesTotalPrev / cycles_max);
if (usage > 10000u) {
usage = 10000u;
}
p_tcb->CPUUsage = usage;
if (p_tcb->CPUUsageMax < usage) { /* Detect peak CPU usage */
p_tcb->CPUUsageMax = usage;
}
#endif
#if (OS_CFG_STAT_TASK_STK_CHK_EN > 0u)
OSTaskStkChk( p_tcb, /* Compute stack usage of active tasks only */
&p_tcb->StkFree,
&p_tcb->StkUsed,
&err);
#endif
CPU_CRITICAL_ENTER();
p_tcb = p_tcb->DbgNextPtr;
CPU_CRITICAL_EXIT();
}
#endif
/*------------------ Check ISR Stack -------------------*/
#if (OS_CFG_STAT_TASK_STK_CHK_EN > 0u) && (OS_CFG_ISR_STK_SIZE > 0u)
free_stk = 0u;
#if (CPU_CFG_STK_GROWTH == CPU_STK_GROWTH_HI_TO_LO)
p_stk = OSCfg_ISRStkBasePtr; /* Start at the lowest memory and go up */
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
p_stk += OS_CFG_TASK_STK_REDZONE_DEPTH;
size_stk = OSCfg_ISRStkSize - OS_CFG_TASK_STK_REDZONE_DEPTH;
#else
size_stk = OSCfg_ISRStkSize;
#endif
while ((*p_stk == 0u) && (free_stk < size_stk)) { /* Compute the number of zero entries on the stk */
p_stk++;
free_stk++;
}
#else
p_stk = OSCfg_ISRStkBasePtr + OSCfg_ISRStkSize - 1u;/* Start at the highest memory and go down */
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
p_stk -= OS_CFG_TASK_STK_REDZONE_DEPTH;
size_stk = OSCfg_ISRStkSize - OS_CFG_TASK_STK_REDZONE_DEPTH;
#else
size_stk = OSCfg_ISRStkSize;
#endif
while ((*p_stk == 0u) && (free_stk < size_stk)) { /* Compute the number of zero entries on the stk */
free_stk++;
p_stk--;
}
#endif
OSISRStkFree = free_stk;
OSISRStkUsed = OSCfg_ISRStkSize - free_stk;
#endif
if (OSStatResetFlag == OS_TRUE) { /* Check if need to reset statistics */
OSStatResetFlag = OS_FALSE;
OSStatReset(&err);
}
#if (OS_CFG_TS_EN > 0u)
OSStatTaskTime = OS_TS_GET() - ts_start; /*----- Measure execution time of statistic task -------*/
if (OSStatTaskTimeMax < OSStatTaskTime) {
OSStatTaskTimeMax = OSStatTaskTime;
}
#endif
OSTimeDly(dly,
OS_OPT_TIME_DLY,
&err);
}
}
/*
************************************************************************************************************************
* INITIALIZE THE STATISTICS
*
* Description: This function is called by OSInit() to initialize the statistic task.
*
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_STAT_STK_INVALID If you specified a NULL stack pointer during configuration
* OS_ERR_STAT_STK_SIZE_INVALID If you didn't specify a large enough stack.
* OS_ERR_STAT_PRIO_INVALID If you specified a priority for the statistic task equal to or
* lower (i.e. higher number) than the idle task.
* OS_ERR_xxx An error code returned by OSTaskCreate()
*
* Returns : none
*
* Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/
/**
* @brief 初始化統計任務
*
* 本函式負責初始化統計任務的相關引數,並建立統計任務
* 統計任務用於收集系統執行時的資訊,如CPU利用率等
*
* @param p_err 指向錯誤程式碼的指標,用於返回函式執行結果
*/
void OS_StatTaskInit (OS_ERR *p_err)
{
// 初始化統計任務的計數器和標誌
OSStatTaskCtr = 0u;
OSStatTaskCtrRun = 0u;
OSStatTaskCtrMax = 0u;
OSStatTaskRdy = OS_STATE_NOT_RDY; /* 統計任務尚未就緒 */
OSStatResetFlag = OS_FALSE;
// 如果配置了堆疊檢查,則初始化中斷服務程式堆疊使用情況變數
#if (OS_CFG_STAT_TASK_STK_CHK_EN > 0u) && (OS_CFG_ISR_STK_SIZE > 0u)
OSISRStkFree = 0u;
OSISRStkUsed = 0u;
#endif
// 檢查統計任務的堆疊指標是否有效
if (OSCfg_StatTaskStkBasePtr == (CPU_STK *)0) {
*p_err = OS_ERR_STAT_STK_INVALID;
return;
}
// 檢查統計任務的堆疊大小是否符合最小要求
if (OSCfg_StatTaskStkSize < OSCfg_StkSizeMin) {
*p_err = OS_ERR_STAT_STK_SIZE_INVALID;
return;
}
// 檢查統計任務的優先順序是否在有效範圍內
if (OSCfg_StatTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {
*p_err = OS_ERR_STAT_PRIO_INVALID;
return;
}
// 建立統計任務
OSTaskCreate(&OSStatTaskTCB,
#if (OS_CFG_DBG_EN == 0u)
(CPU_CHAR *)0,
#else
(CPU_CHAR *)"uC/OS-III Stat Task",
#endif
OS_StatTask,
(void *)0,
OSCfg_StatTaskPrio,
OSCfg_StatTaskStkBasePtr,
OSCfg_StatTaskStkLimit,
OSCfg_StatTaskStkSize,
0u,
0u,
(void *)0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
p_err);
}
#endif