Abstract: The Central Processing Unit Percentage (CPUP) is divided into system CPU usage and task CPU usage. You can check whether the current system load exceeds the design limit based on the system-level CPU usage. You can check whether the CPU usage of each task meets the design expectation based on the CPU usage of each task.

This article is shared by Huawei cloud community “Hongmeng light kernel M core source code analysis series 15 CPU usage CPUP (1)”, author: Zhushy.

Central Processing Unit Percentage (CPU usage) CPUP is divided into system CPU usage and task CPU usage. You can check whether the current system load exceeds the design limit based on the system-level CPU usage. You can check whether the CPU usage of each task meets the design expectation based on the CPU usage of each task.

System CPU usage refers to the CPU usage of the system in a period of time. It indicates the idle and busy state of the system in a period of time and the CPU load. The CPU usage ranges from 0 to 100, and its precision (adjustable by configuration) is a percentage. 100 indicates that the system is running at full capacity.

Task CPU usage Indicates the CPU usage of a task. It indicates how busy a task is in a period of time. The CPU usage of a task ranges from 0 to 100, and its precision (adjustable by configuration) is a percentage. 100 indicates that the system has been running this task for a period of time.

This article through the analysis of hongmeng light kernel CPUP extension module source code. The source code in this article, using the OpenHarmonyLitEOS-M kernel as an example, is available on the open source site gitee.com/openharmony… To obtain.

The CPUP module uses task-level recording to record the start time of a task, the time when a task is switched over, and the time when a task is switched out or exited. Each time when a task exits, the system accumulates the duration occupied by the entire task. Next, take a look at the source code for common operations supported by the CPUP module.

1. CPUP structure definition and common macro definition

1.1 CPUP structure definition

In the file components\cpup\los_cpup.h, the cpup control block structure is OsCpupCB, and the structure source code is as follows. AllTime records the cycle number of the task since the start of the system, startTime records the startTime of the task. HistoryTime [] The 10 elements of the historyTime array record the number of cycles that each task has run since system startup in each second of the last 10 seconds. For explanations of other structure members, see the comments section.

typedef struct { UINT32 cpupID; /**< UINT16 status; /**< task status */ UINT64 allTime; /**< total running time */ UINT64 startTime; /**< Task start time */ UINT64 historyTime[OS_CPUP_HISTORY_RECORD_NUM]; OS_CPUP_HISTORY_RECORD_NUM = 10 */} OsCpupCB;Copy the code

In addition, a structure, CPUP_INFO_S, is defined as follows:

typedef struct tagCpupInfo { UINT16 usStatus; /**< Save the status of the current running task */ UINT32 uwUsage; /**< usage, value range is [0,1000]. */} CPUP_INFO_S;Copy the code

1.2 CPUP enumeration definitions

The CPUP header components\ CPUP \los_cpup.h also provides enumerations of the CPUP usage type, CPUP_TYPE_E, and the CPUP statistics interval mode, CPUP_MODE_E.

Typedef enum {SYS_CPU_USAGE = 0, /* system CPUP */ TASK_CPU_USAGE, /* task CPUP */} CPUP_TYPE_E; Typedef enum {CPUP_IN_10S = 0, /* CPUP statistical period 10s */ CPUP_IN_1S, /* CPUP statistical period 1s */ CPUP_LESS_THAN_1S, /* CPUP statistical period <1s */} CPUP_MODE_E;Copy the code

2. Initialize CPUP

CPUP is disabled by default. You can enable CPUP by using the macro LOSCFG_BASE_CORE_CPUP. With CPUP enabled, OsCpupInit() is called in kernel\ SRC \los_init.c to initialize the CPUP module at system boot time. Next, let’s examine the CPUP initialization code.

(1) Calculate the memory size required by the CPUP structure pool, and then apply for CPUP memory. If the application fails, an error message is returned. After successful initialization at ⑵, set the initialization flag g_cpupInitFlg.

LITE_OS_SEC_TEXT_INIT UINT32 OsCpupInit() { UINT32 size; size = g_taskMaxNum * sizeof(OsCpupCB); ⑴ g_CPUP = (OsCpupCB *)LOS_MemAlloc(m_aucSysMem0, size); if (g_cpup == NULL) { return LOS_ERRNO_CPUP_NO_MEMORY; } (VOID)memset_s(g_cpup, size, 0, size); G_cpupInitFlg [2] = 1; return LOS_OK; }Copy the code

3. Common CPUP operations

3.1 CPUP Internal Interfaces

Let’s first examine the internal interfaces, which are called by external interfaces starting with LOS_.

3.1.1 OsTskCycleStart Records the start time of the task

After the external interface is executed, the CPUP module invokes this internal interface to set the start time of the next task.

(1) Determine whether CPUP has been initialized. If not, exit the function. Get the task number of the new task at (2). ⑶ set the task number and start time of the CPUP structure corresponding to the task.

LITE_OS_SEC_TEXT_MINOR VOID OsTskCycleStart(VOID) { UINT32 taskID; ⑴ if (g_cpupInitFlg == 0) {return; } ⑵ taskID = g_lostask.newtask ->taskID; (3) g_cpup [taskID] cpupID = taskID; g_cpup[taskID].startTime = LOS_SysCycleGet(); return; }Copy the code

3.1.2 OsTskCycleEnd Records the end time of the task

Before executing an external interface, the CPUP module invokes this internal interface, obtains the end time of the current task, and counts the total running time of the current task.

(1) Determine whether CPUP has been initialized. If not, exit the function. (2) gets the task number of the current task. If the start time of the task is 0, exit the function. (4) Obtain the current cycle number of the system. ⑸ If the cycle number obtained is less than the start time of task CPUP, add the cycle number of each tick to the cycle number obtained. ⑹ Calculate the total running time of the current task and set the start time to 0.

LITE_OS_SEC_TEXT_MINOR VOID OsTskCycleEnd(VOID) { UINT32 taskID; UINT64 cpuCycle; ⑴ if (g_cpupInitFlg == 0) {return; } ⑵ taskID = g_lostask.runtask ->taskID; If (g_cpup[taskID]. StartTime == 0) {return; } ⑷ ⑷ cpuCycle = LOS_SysCycleGet(); ⑸ ⑸ if (cpuCycle < g_cpup[taskID]. StartTime) {cpuCycle += g_cyclesPerTick; } g_cpup[taskID]. AllTime += (cpuCycle -g_cpup [taskID].startTime); g_cpup[taskID].startTime = 0; return; }Copy the code

3.1.3 Update the historical running time of the OsTskCycleEndStart task when the task is switched

This function is executed during a task scheduling switch, calculates the total running time of the current running task, records the start time of the new task, and updates the historical running time of all tasks. The schematic diagram of the function is as follows:

(1) Determine whether CPUP has been initialized. If not, exit the function. (2) Obtain the task number of the current task, and then obtain the current cycle number of the system. (3) If the start time of the current task is not 0, calculate the total time of the current task, and set the start time to 0. (4) Obtain the task number of the new task; (5) set the task number and start time of the corresponding CPUP structure of the task. ⑹ If the recording interval is longer than the system clock (i.e. the number of cycles per second), update the last recording time. This means that each element in each task’s historyTime[] array represents the number of cycles that the task has run over a period of more than 1s, which is not very precise. The historical CPUP running time of each task is recorded. ⑻ updates the current index of the historical runtime array.

LITE_OS_SEC_TEXT_MINOR VOID OsTskCycleEndStart(VOID) { UINT32 taskID; UINT64 cpuCycle; UINT16 loopNum; ⑴ if (g_cpupInitFlg == 0) {return; } ⑵ taskID = g_lostask.runtask ->taskID; cpuCycle = LOS_SysCycleGet(); (3) if (g_cpup [taskID] startTime! = 0) { if (cpuCycle < g_cpup[taskID].startTime) { cpuCycle += g_cyclesPerTick; } g_cpup[taskID].allTime += (cpuCycle - g_cpup[taskID].startTime); g_cpup[taskID].startTime = 0; } ⑷ ⑷ taskID = g_lostask.newtask ->taskID; 5] g_cpup [taskID] cpupID = taskID; g_cpup[taskID].startTime = cpuCycle; If ((cpucycle-g_lastrecordtime) > OS_CPUP_RECORD_PERIOD) {g_lastRecordTime = cpuCycle; for (loopNum = 0; loopNum < g_taskMaxNum; LoopNum ++) {⑺ g_cpup[loopNum]. HistoryTime [g_hisPos] = g_cpup[loopNum]. AllTime; } ⑻ if (g_hisPos == (OS_CPUP_historY_record_num-1)) {g_hisPos = 0; } else { g_hisPos++; } } return; }Copy the code

3.1.4 OsGetPrePos Retrieves an index position in the historical run time array

If curPos is 0, OS_CPUP_HISTORY_RECORD_NUM – 1 is the last index of the array. Otherwise returns minus 1 returns.

LITE_OS_SEC_TEXT_MINOR static inline UINT16 OsGetPrePos(UINT16 curPos)
{
    return (curPos == 0) ? (OS_CPUP_HISTORY_RECORD_NUM - 1) : (curPos - 1);
}
Copy the code

OsGetPositions gets the current and previous index positions of the historical running time array

Retrieves the current and last index position of the historical run time array according to the CPUP statistical interval pattern.

(2) If the interval mode is 1 second, the current index position of g_hisPos is the previous index position, and the previous index position of prePos needs to continue to the previous one. If the interval pattern is less than 1 second, the current index curPos is the upper index of g_hisPos, and the upper index prePos is 0. If the interval mode is 10 seconds, the current index curPos is equal to g_hisPos and the previous index prePos is 0. (4) Set the efferent parameters.

LITE_OS_SEC_TEXT_MINOR static VOID OsGetPositions(UINT16 mode, UINT16* curPosAddr, UINT16* prePosAddr) { UINT16 curPos; UINT16 prePos = 0; (1) curPos = g_hisPos; ⑵ if (mode == CPUP_IN_1S) {curPos = OsGetPrePos(curPos); prePos = OsGetPrePos(curPos); } else if (mode == CPUP_LESS_THAN_1S) {curPos = OsGetPrePos(curPos); } ⑷ *curPosAddr = curPos; *prePosAddr = prePos; }Copy the code

3.2 CPUP External interfaces

Let’s first analyze the external interface. The interface description is as follows:

3.2.1 LOS_SysCpuUsage

This function collects statistics on the CPU usage of the current system. The return value is calculated based on the kilobit rate. The value ranges from 0 to 1000. The schematic diagram of the function is as follows:

(1) Check whether CPUP has been initialized. If not, return an error code. (2) call the function OsTskCycleEnd() to get the end time of the current task and calculate the total running time. (3) Calculate the total running time of all tasks. If the total running time is not 0, perform (4) to calculate the CPU usage of the system. The OsTskCycleStart() function is called at ⑸ to set the start time of CPUP statistics for the new task.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_SysCpuUsage(VOID) { UINT64 cpuCycleAll = 0; UINT32 cpupRet = 0; UINT16 loopNum; UINT32 intSave; ⑴ if (g_cpupInitFlg == 0) {return LOS_ERRNO_CPUP_NO_INIT; } intSave = LOS_IntLock(); 2 OsTskCycleEnd (); ⑶ for (loopNum = 0; loopNum < g_taskMaxNum; loopNum++) { cpuCycleAll += g_cpup[loopNum].allTime; } ⑷ ⑷ if (cpuCycleAll) {cpupRet = LOS_CPUP_PRECISION - (UINT32)(LOS_CPUP_PRECISION * g_cpup[g_idleTaskID].alltime) / cpuCycleAll); } [5] OsTskCycleStart (); LOS_IntRestore(intSave); return cpupRet; }Copy the code

3.2.2 LOS_HistorySysCpuUsage

This function obtains the historical CPU usage of the system. For the historical CPU usage, you need to enter the interval mode parameter, which can be 10 seconds, 1 second, or less than 1 second.

(1) Check whether CPUP has been initialized. If not, return an error code. (2) call the function OsTskCycleEnd() to get the end time of the current task and calculate the total running time. The OsGetPositions() function is called at ⑶ to calculate the index position of the historical run time array. (4) Calculate the total running time of each task within the cycle. If the time interval mode is 1 second, the difference between the two historical running times is taken as the number of running time of the task within 1 second. For an interval mode of 10 seconds, historyTime[curPos] represents the number of times a task has run since system startup 10 seconds ago, and the calculated difference is the number of times a task has run in 10 seconds. If the interval mode is less than 1 second, historyTime[curPos] represents the number of times that tasks have been running since system startup one second ago, and the calculated difference is the number of times that tasks have been running in less than 1 second. Calculate the total running time within the idle task cycle at ⑸. ⑹ If the total time is not 0, calculate the historical CPU usage of the system. Finally, the function OsTskCycleStart() is called to set the start time of CPUP statistics for the new task. Please refer to the schematic diagram for understanding:

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_HistorySysCpuUsage(UINT16 mode) { UINT64 cpuCycleAll = 0; UINT64 idleCycleAll = 0; UINT32 cpupRet = 0; UINT16 loopNum; UINT16 curPos; UINT16 prePos = 0; UINT32 intSave; ⑴ if (g_cpupInitFlg == 0) {return LOS_ERRNO_CPUP_NO_INIT; } // get end time of current task intSave = LOS_IntLock(); 2 OsTskCycleEnd (); (3) OsGetPositions (mode, & curPos, & prePos); for (loopNum = 0; loopNum < g_taskMaxNum; LoopNum ++) {⑷ if (mode == CPUP_IN_1S) {cpuCycleAll += g_cpup[loopNum].historyTime[curPos] - g_cpup[loopNum].historyTime[prePos]; } else { cpuCycleAll += g_cpup[loopNum].allTime - g_cpup[loopNum].historyTime[curPos]; }} ⑸ if (mode == CPUP_IN_1S) {idleCycleAll += g_cpup[g_idleTaskID].historyTime[curPos] - g_cpup[g_idleTaskID].historyTime[prePos]; } else { idleCycleAll += g_cpup[g_idleTaskID].allTime - g_cpup[g_idleTaskID].historyTime[curPos]; } ⑹ if (cpuCycleAll) {cpupRet = (LOS_CPUP_PRECISION - (UINT32)((LOS_CPUP_PRECISION * idleCycleAll)/cpuCycleAll)); } OsTskCycleStart(); LOS_IntRestore(intSave); return cpupRet; }Copy the code

3.2.3 LOS_TaskCpuUsage

LOS_SysCpuUsage() ¶ LOS_SysCpuUsage() ¶ LOS_SysCpuUsage() ¶ LOS_SysCpuUsage() ¶

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskCpuUsage(UINT32 taskID) { UINT64 cpuCycleAll = 0; UINT16 loopNum; UINT32 intSave; UINT32 cpupRet = 0; if (g_cpupInitFlg == 0) { return LOS_ERRNO_CPUP_NO_INIT; } if (OS_TSK_GET_INDEX(taskID) >= g_taskMaxNum) { return LOS_ERRNO_CPUP_TSK_ID_INVALID; } if (g_cpup[taskID].cpupID ! = taskID) { return LOS_ERRNO_CPUP_THREAD_NO_CREATED; } if ((g_cpup[taskID].status & OS_TASK_STATUS_UNUSED) || (g_cpup[taskID].status == 0)) { return LOS_ERRNO_CPUP_THREAD_NO_CREATED; } intSave = LOS_IntLock(); OsTskCycleEnd(); for (loopNum = 0; loopNum < g_taskMaxNum; loopNum++) { if ((g_cpup[loopNum].status & OS_TASK_STATUS_UNUSED) || (g_cpup[loopNum].status == 0)) { continue; } cpuCycleAll += g_cpup[loopNum].allTime; } if (cpuCycleAll) { cpupRet = (UINT32)((LOS_CPUP_PRECISION * g_cpup[taskID].allTime) / cpuCycleAll); } OsTskCycleStart(); LOS_IntRestore(intSave); return cpupRet; }Copy the code

3.2.4 LOS_HistoryTaskCpuUsage

LOS_HistorySysCpuUsage() ¶ LOS_HistorySysCpuUsage() ¶ LOS_HistorySysCpuUsage() ¶

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_HistoryTaskCpuUsage(UINT32 taskID, UINT16 mode) { UINT64 cpuCycleAll = 0; UINT64 cpuCycleCurTsk = 0; UINT16 loopNum, curPos; UINT16 prePos = 0; UINT32 intSave; UINT32 cpupRet = 0; if (g_cpupInitFlg == 0) { return LOS_ERRNO_CPUP_NO_INIT; } if (OS_TSK_GET_INDEX(taskID) >= g_taskMaxNum) { return LOS_ERRNO_CPUP_TSK_ID_INVALID; } if (g_cpup[taskID].cpupID ! = taskID) { return LOS_ERRNO_CPUP_THREAD_NO_CREATED; } if ((g_cpup[taskID].status & OS_TASK_STATUS_UNUSED) || (g_cpup[taskID].status == 0)) { return LOS_ERRNO_CPUP_THREAD_NO_CREATED; } intSave = LOS_IntLock(); OsTskCycleEnd(); OsGetPositions(mode, &curPos, &prePos); for (loopNum = 0; loopNum < g_taskMaxNum; loopNum++) { if ((g_cpup[loopNum].status & OS_TASK_STATUS_UNUSED) || (g_cpup[loopNum].status == 0)) { continue; } if (mode == CPUP_IN_1S) { cpuCycleAll += g_cpup[loopNum].historyTime[curPos] - g_cpup[loopNum].historyTime[prePos]; } else { cpuCycleAll += g_cpup[loopNum].allTime - g_cpup[loopNum].historyTime[curPos]; } } if (mode == CPUP_IN_1S) { cpuCycleCurTsk += g_cpup[taskID].historyTime[curPos] - g_cpup[taskID].historyTime[prePos];  } else { cpuCycleCurTsk += g_cpup[taskID].allTime - g_cpup[taskID].historyTime[curPos]; } if (cpuCycleAll) { cpupRet = (UINT32)((LOS_CPUP_PRECISION * cpuCycleCurTsk) / cpuCycleAll); } OsTskCycleStart(); LOS_IntRestore(intSave); return cpupRet; }Copy the code

3.2.5 LOS_AllTaskCpuUsage

This function obtains the CPU usage of all tasks. The obtained CPU usage information is stored in the memory area pointed to by the outgoing parameter CPUP_INFO_S *cpupInfo. The sizeof the memory area must be equal to sizeof(CPUP_INFO_S) * g_taskMaxNum. You also need to pass in interval mode parameters, which can be 10 seconds, 1 second, or less than 1 second.

(1) Check whether CPUP has been initialized. If not, return an error code. The outgoing parameter cpupInfo pointer cannot be empty; otherwise, an error code is returned. (2) call the function OsTskCycleEnd() to get the end time of the current task and calculate the total running time. The OsGetPositions() function is called at ⑶ to calculate the index position of the historical run time array. (4) Calculate the total running time of each task within the cycle. If the time interval mode is 1 second, take the value of the difference between the two historical running times; otherwise, take the value of XX. ⑸ set the status of each task, and then calculate the CPU usage of each task. Finally, the function OsTskCycleStart() is called to set the start time of CPUP statistics for the new task.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_AllTaskCpuUsage(CPUP_INFO_S *cpupInfo, UINT16 mode) { UINT16 loopNum; UINT16 curPos; UINT16 prePos = 0; UINT32 intSave; UINT64 cpuCycleAll = 0; UINT64 cpuCycleCurTsk = 0; ⑴ if (g_cpupInitFlg == 0) {return LOS_ERRNO_CPUP_NO_INIT; } if (cpupInfo == NULL) { return LOS_ERRNO_CPUP_TASK_PTR_NULL; } intSave = LOS_IntLock(); 2 OsTskCycleEnd (); (3) OsGetPositions (mode, & curPos, & prePos); for (loopNum = 0; loopNum < g_taskMaxNum; loopNum++) { if ((g_cpup[loopNum].status & OS_TASK_STATUS_UNUSED) || (g_cpup[loopNum].status == 0)) { continue; } if (mode == CPUP_IN_1S) { cpuCycleAll += g_cpup[loopNum].historyTime[curPos] - g_cpup[loopNum].historyTime[prePos]; } else { cpuCycleAll += g_cpup[loopNum].allTime - g_cpup[loopNum].historyTime[curPos]; }} ⑷ (loopNum = 0; loopNum < g_taskMaxNum; loopNum++) { if ((g_cpup[loopNum].status & OS_TASK_STATUS_UNUSED) || (g_cpup[loopNum].status == 0)) { continue; } if (mode == CPUP_IN_1S) { cpuCycleCurTsk += g_cpup[loopNum].historyTime[curPos] - g_cpup[loopNum].historyTime[prePos];  } else { cpuCycleCurTsk += g_cpup[loopNum].allTime - g_cpup[loopNum].historyTime[curPos]; } ⑸ cpupInfo[loopNum]. UsStatus = g_cpup[loopNum]. Status; if (cpuCycleAll) { cpupInfo[loopNum].uwUsage = (UINT32)((LOS_CPUP_PRECISION * cpuCycleCurTsk) / cpuCycleAll); } cpuCycleCurTsk = 0; } OsTskCycleStart(); LOS_IntRestore(intSave); return LOS_OK; }Copy the code

3.2.6 LOS_CpupUsageMonitor

This function gets the historical CPU usage and prints it, passing in three arguments:

CPU usage type, CPUP time period mode, and task id. You need to specify a valid task ID only for the TASK CPU usage.

In (1), the CPU usage type is system CPU usage. In (2), the CPUP time cycle mode is printed. (4) (* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * When the CPU usage type is the CPU usage of the specified task, check the validity of the task id and verify whether the task is created. ⑹ Print the CPUP time cycle mode. Where ⑺, call LOS_HistoryTaskCpuUsage() to obtain the historical CPU usage of the specified task, and then run the ⑻ output of the CPU usage. The output is in the range of 0,100.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CpupUsageMonitor(CPUP_TYPE_E type, CPUP_MODE_E mode, UINT32 taskID) { UINT32 ret; LosTaskCB *taskCB = NULL; Switch (type) {⑴ case SYS_CPU_USAGE: ⑵ if (mode == CPUP_IN_10S) {PRINTK("\nSysCpuUsage in 10s: "); } else if (mode == CPUP_IN_1S) { PRINTK("\nSysCpuUsage in 1s: "); } else { PRINTK("\nSysCpuUsage in <1s: "); } ⑶ ret = LOS_HistorySysCpuUsage(mode); ⑷ PRINTK("%d.%d", ret/LOS_CPUP_PRECISION_MULT, ret % LOS_CPUP_PRECISION_MULT); break; ⑸ case TASK_CPU_USAGE: if (taskID > LOSCFG_BASE_CORE_TSK_LIMIT) {PRINT_ERR("\nThe taskID is invalid.\n"); return OS_ERROR; } taskCB = OS_TCB_FROM_TID(taskID); if ((taskCB->taskStatus & OS_TASK_STATUS_UNUSED)) { PRINT_ERR("\nThe taskid is invalid.\n"); return OS_ERROR; } if (mode == CPUP_IN_10S) {PRINTK("\nCPUusage of taskID %d in 10s: ", taskID); } else if (mode == CPUP_IN_1S) { PRINTK("\nCPUusage of taskID %d in 1s: ", taskID); } else { PRINTK("\nCPUusage of taskID %d in <1s: ", taskID); } ⑺ ret = LOS_HistoryTaskCpuUsage(taskID, mode); ⑻ PRINTK("%u.%u", ret/LOS_CPUP_PRECISION_MULT, ret % LOS_CPUP_PRECISION_MULT); break; default: PRINT_ERR("\nThe type is invalid.\n"); return OS_ERROR; } return LOS_OK; }Copy the code

summary

This article leads us to analyze the CPUP extension module of hongmeng light kernel source code. Thank you for reading. If you have any questions or suggestions, please leave a comment on my blog. Thank you.

Click to follow, the first time to learn about Huawei cloud fresh technology ~