未验证 提交 24604266 编写于 作者: O openharmony_ci 提交者: Gitee

!12944 内核文档优化:调测部分

Merge pull request !12944 from zhangdengyu/cherry-pick-1672124112
......@@ -12,7 +12,7 @@ Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样
Perf提供2种工作模式,计数模式和采样模式。
计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径
计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径
## 接口说明
......@@ -24,21 +24,21 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
**表1** Perf模块接口说明
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 开启/停止Perf采样 | LOS_PerfStart:开启采样<br/>LOS_PerfStop:停止采样 |
| 配置Perf采样事件 | LOS_PerfConfig:配置采样事件的类型、周期等 |
| 读取采样数据 | LOS_PerfDataRead:读取采样数据到指定地址 |
| 注册采样数据缓冲区的钩子函数 | LOS_PerfNotifyHookReg:注册缓冲区水线到达的处理钩子<br/>LOS_PerfFlushHookReg:注册缓冲区刷cache的钩子 |
| 开启/停止Perf采样 | LOS_PerfInit : 初始化Perf<br/>LOS_PerfStart:开启采样<br/>LOS_PerfStop:停止采样 |
| 配置Perf采样事件 | LOS_PerfConfig:配置采样事件的类型、周期等 |
| 读取采样数据 | LOS_PerfDataRead:读取采样数据到指定地址 |
| 注册采样数据缓冲区的钩子函数 | LOS_PerfNotifyHookReg:注册缓冲区水线到达的处理钩子<br/>LOS_PerfFlushHookReg:注册缓冲区刷cache的钩子 |
1. Perf采样事件的结构体为PerfConfigAttr,详细字段含义及取值详见kernel\include\los_perf.h
1. Perf采样事件的结构体为PerfConfigAttr,详细字段含义及取值详见 [kernel\include\los_perf.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_perf.h)
2. 采样数据缓冲区为环形buffer,buffer中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。
3. 缓冲区有限,用户可通过注册水线到达的钩子进行buffer溢出提醒或buffer读操作。默认水线值为buffer总大小的1/2。 示例如下:
```
```c
VOID Example_PerfNotifyHook(VOID)
{
CHAR buf[LOSCFG_PERF_BUFFER_SIZE] = {0};
......@@ -51,8 +51,8 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
```
4. 若perf采样的buffer涉及到CPU跨cache,则用户可通过注册刷cache的钩子,进行cache同步。 示例如下:
```
```c
VOID Example_PerfFlushHook(VOID *addr, UINT32 size)
{
OsCacheFlush(addr, size); /* platform interface */
......@@ -74,8 +74,8 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
- write: 用户态采样事件配置
- ioctl: 用户态Perf控制操作,包括
```
```c
#define PERF_IOC_MAGIC 'T'
#define PERF_START _IO(PERF_IOC_MAGIC, 1)
#define PERF_STOP _IO(PERF_IOC_MAGIC, 2)
......@@ -97,15 +97,15 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
1. 配置Perf模块相关宏。
配置Perf控制宏LOSCFG_KERNEL_PERF,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel-&gt;Enable Perf Feature"中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| -------- | -------- | -------- | -------- |
| LOSCFG_KERNEL_PERF | Enable&nbsp;Perf&nbsp;Feature | Perf模块的裁剪开关 | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_TICK | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Tick | Perf计时单位为tick | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_CYCLE | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Cpu&nbsp;Cycle | Perf计时单位为cycle | YES/NO |
| LOSCFG_PERF_BUFFER_SIZE | Perf&nbsp;Sampling&nbsp;Buffer&nbsp;Size | Perf采样buffer的大小 | INT |
| LOSCFG_PERF_HW_PMU | Enable&nbsp;Hardware&nbsp;Pmu&nbsp;Events&nbsp;for&nbsp;Sampling | 使能硬件PMU事件,需要目标平台支持硬件PMU | YES/NO |
| LOSCFG_PERF_TIMED_PMU | Enable&nbsp;Hrtimer&nbsp;Period&nbsp;Events&nbsp;for&nbsp;Sampling | 使能高精度周期事件,需要目标平台支持高精度定时器 | YES/NO |
| LOSCFG_PERF_SW_PMU | Enable&nbsp;Software&nbsp;Events&nbsp;for&nbsp;Sampling | 使能软件事件,需要开启LOSCFG_KERNEL_HOOK | YES/NO |
| LOSCFG_KERNEL_PERF | Enable&nbsp;Perf&nbsp;Feature | Perf模块的裁剪开关 | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_TICK | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Tick | Perf计时单位为tick | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_CYCLE | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Cpu&nbsp;Cycle | Perf计时单位为cycle | YES/NO |
| LOSCFG_PERF_BUFFER_SIZE | Perf&nbsp;Sampling&nbsp;Buffer&nbsp;Size | Perf采样buffer的大小 | INT |
| LOSCFG_PERF_HW_PMU | Enable&nbsp;Hardware&nbsp;Pmu&nbsp;Events&nbsp;for&nbsp;Sampling | 使能硬件PMU事件,需要目标平台支持硬件PMU | YES/NO |
| LOSCFG_PERF_TIMED_PMU | Enable&nbsp;Hrtimer&nbsp;Period&nbsp;Events&nbsp;for&nbsp;Sampling | 使能高精度周期事件,需要目标平台支持高精度定时器 | YES/NO |
| LOSCFG_PERF_SW_PMU | Enable&nbsp;Software&nbsp;Events&nbsp;for&nbsp;Sampling | 使能软件事件,需要开启LOSCFG_KERNEL_HOOK | YES/NO |
2. 调用LOS_PerfConfig配置需要采样的事件。
Perf提供2种模式的配置,及3大类型的事件配置:
......@@ -142,9 +142,11 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
前提条件:在menuconfig菜单中完成perf模块的配置。
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下:
```
```c
#include "los_perf.h"
STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num)
{
......@@ -176,7 +178,7 @@ STATIC VOID perfTestHwEvent(VOID)
.predivided = 1, /* cycle counter increase every 64 cycles */
},
.taskIds = {0},
.taskIdsNr = 0,
.taskIdsNr = 0,
.needSample = 0,
.sampleType = PERF_RECORD_IP | PERF_RECORD_CALLCHAIN,
};
......@@ -192,7 +194,7 @@ STATIC VOID perfTestHwEvent(VOID)
PRINTK("--------sample mode------ \n");
attr.needSample = 1;
LOS_PerfConfig(&attr);
LOS_PerfStart(2);
LOS_PerfStart(2); // 2: set the section id to 2.
test(); /* this is any test function*/
LOS_PerfStop();
buf = LOS_MemAlloc(m_aucSysMem1, LOSCFG_PERF_BUFFER_SIZE);
......@@ -201,24 +203,25 @@ STATIC VOID perfTestHwEvent(VOID)
return;
}
/* get sample data */
len = LOS_PerfDataRead(buf, LOSCFG_PERF_BUFFER_SIZE);
len = LOS_PerfDataRead(buf, LOSCFG_PERF_BUFFER_SIZE);
OsPrintBuff(buf, len); /* print data */
(VOID)LOS_MemFree(m_aucSysMem1, buf);
}
UINT32 Example_Perf_test(VOID){
UINT32 ret;
TSK_INIT_PARAM_S perfTestTask;
/* 创建用于perf测试的任务 */
memset(&perfTestTask, 0, sizeof(TSK_INIT_PARAM_S));
perfTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)perfTestHwEvent;
perfTestTask.pcName = "TestPerfTsk"; /* 测试任务名称 */
perfTestTask.uwStackSize = 0x800;
perfTestTask.usTaskPrio = 5;
perfTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_perfTestTaskId, &perfTestTask);
if(ret != LOS_OK){
PRINT_ERR("PerfTestTask create failed.\n");
return LOS_NOK;
UINT32 Example_Perf_test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S perfTestTask;
/* 创建用于perf测试的任务 */
memset(&perfTestTask, 0, sizeof(TSK_INIT_PARAM_S));
perfTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)perfTestHwEvent;
perfTestTask.pcName = "TestPerfTsk"; /* 测试任务名称 */
perfTestTask.uwStackSize = 0x800; // 0x8000: perf test task stack size
perfTestTask.usTaskPrio = 5; // 5: perf test task priority
perfTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_perfTestTaskId, &perfTestTask);
if (ret != LOS_OK) {
PRINT_ERR("PerfTestTask create failed.\n");
return LOS_NOK;
}
return LOS_OK;
}
......@@ -229,7 +232,7 @@ LOS_MODULE_INIT(perfTestHwEvent, LOS_INIT_LEVEL_KMOD_EXTENDED);
#### 内核态结果验证
输出结果如下:
```
--------count mode----------
[EMG] [cycles] eventType: 0xff: 5466989440
......@@ -268,18 +271,18 @@ hex: 00 ef ef ef 00 00 00 00 14 00 00 00 60 00 00 00 00 00 00 00 70 88 36 40 08
- option可选如下:
- -e,配置采样事件。可使用./perf list 中罗列的同类型事件。
- -p,配置事件采样周期。
- -o, 指定perf采样数据结果保存的文件路径。
- -o指定perf采样数据结果保存的文件路径。
- -t,任务Id过滤(白名单),只采取指定任务中的上下文。如果不指定改参数,则默认采集所有的任务。
- -s, 配置采样的具体上下文类型,可查阅los_perf.h中定义的PerfSampleType。
- -P, 进程Id过滤(白名单),只采取指定进程中的上下文。如果不指定改参数,则默认采集所有进程。
- -d, 是否进行分频(事件每发生64次累计+1),该选项仅在硬件cycle事件上生效。
- -s配置采样的具体上下文类型,可查阅los_perf.h中定义的PerfSampleType。
- -P进程Id过滤(白名单),只采取指定进程中的上下文。如果不指定改参数,则默认采集所有进程。
- -d是否进行分频(事件每发生64次累计+1),该选项仅在硬件cycle事件上生效。
- command 为待统计的子程序。
用户态命令行的典型使用方法如下:
./perf list 查看可使用的事件列表, 输出如下:
```
cycles [Hardware event]
instruction [Hardware event]
......@@ -298,7 +301,7 @@ mux-pend [Software event]
./perf stat -e cycles os_dump, 输出如下:
```
type: 0
events[0]: 255, 0xffff
......@@ -316,7 +319,7 @@ time used: 0.058000(s)
./perf record -e cycles os_dump, 输出如下:
```
type: 0
events[0]: 255, 0xffff
......@@ -354,8 +357,8 @@ save perf data success at /storage/data/perf.data
#### 用户态示例代码
实例代码如下:
```
```c
#include "fcntl.h"
#include "user_copy.h"
#include "sys/ioctl.h"
......@@ -367,6 +370,7 @@ save perf data success at /storage/data/perf.data
#define PERF_IOC_MAGIC 'T'
#define PERF_START _IO(PERF_IOC_MAGIC, 1)
#define PERF_STOP _IO(PERF_IOC_MAGIC, 2)
int main(int argc, char **argv)
{
char *buf = NULL;
......@@ -425,7 +429,7 @@ int main(int argc, char **argv)
#### 用户态结果验证
输出结果如下
```
[EMG] dump section data, addr: 0x8000000 length: 0x800000
num: 00 01 02 03 04 05 06 07 08 09 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 ...
......
......@@ -42,11 +42,12 @@ LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这
**示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试.
代码实现如下:
```
```c
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
......@@ -70,12 +71,12 @@ void MemIntegrityTest(void)
编译运行输出log如下:
```
[ERR][OsMemMagicCheckPrint], 2028, memory check error!
memory used but magic num wrong, magic num = 0x00000000 /* 提示信息,检测到哪个字段被破坏了,用例构造了将下个节点的头4个字节清零,即魔鬼数字字段 */
broken node head: 0x20003af0 0x00000000 0x80000020, prev node head: 0x20002ad4 0xabcddcba 0x80000020
broken node head: 0x20003af0 0x00000000 0x80000020, prev node head: 0x20002ad4 0xabcddcba 0x80000020
/* 被破坏节点和其前节点关键字段信息,分别为其前节点地址、节点的魔鬼数字、节点的sizeAndFlag;可以看出被破坏节点的魔鬼数字字段被清零,符合用例场景 */
broken node head LR info: /* 节点的LR信息需要开启内存检测功能才有有效输出 */
......
......@@ -7,7 +7,7 @@
- 内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小;
- 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量
- 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量
- 其他统计信息:调用接口LOS_MemInfoGet时,会扫描内存池的节点信息,统计出相关信息。
......@@ -24,8 +24,8 @@ LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能,
关键结构体介绍:
```
```c
typedef struct {
UINT32 totalUsedSize; // 内存池的内存使用量
UINT32 totalFreeSize; // 内存池的剩余内存大小
......@@ -38,7 +38,7 @@ typedef struct {
} LOS_MEM_POOL_STATUS;
```
- 内存水线获取:调用LOS_MemInfoGet接口,第1个参数是内存池首地址,第2个参数是LOS_MEM_POOL_STATUS类型的句柄,其中字段usageWaterLine即水线值。
- 内存水线获取:调用 LOS_MemInfoGet(VOID *pool, LOS_MEM_POOL_STATUS *poolStatus)接口,第1个参数是内存池首地址,第2个参数是LOS_MEM_POOL_STATUS类型的句柄,其中字段usageWaterLine即水线值。
- 内存碎片率计算:同样调用LOS_MemInfoGet接口,可以获取内存池的剩余内存大小和最大空闲内存块大小,然后根据公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)得出此时的动态内存池碎片率。
......@@ -53,13 +53,12 @@ typedef struct {
3. 利用公式算出使用率及碎片率。
**示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
代码实现如下:
```
```c
#include <stdio.h>
#include <string.h>
#include "los_task.h"
......@@ -77,8 +76,7 @@ void MemInfoTaskFunc(void)
unsigned char fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize;
/* 算出内存池当前的使用率百分比 */
unsigned char usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool);
printf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize,
poolStatus.totalFreeSize, poolStatus.usageWaterLine);
printf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
}
int MemTest(void)
......@@ -93,9 +91,9 @@ int MemTest(void)
ret = LOS_TaskCreate(&taskID, &taskStatus);
if (ret != LOS_OK) {
printf("task create failed\n");
return -1;
return LOS_NOK;
}
return 0;
return LOS_OK;
}
```
......@@ -106,7 +104,7 @@ int MemTest(void)
编译运行输出的结果如下:
```
usage = 22, fragment = 3, maxFreeSize = 49056, totalFreeSize = 50132, waterLine = 1414
```
......@@ -12,7 +12,7 @@
2. LOS_RECORD_LR_CNT:记录的LR层数,默认3层;每层LR消耗sizeof(void \*)字节数的内存。
3. LOS_OMIT_LR_CNT:忽略的LR层数,默认2层,即从调用LOS_MemAlloc的函数开始记录,可根据实际情况调整。为啥需要这个配置?有3点原因如下:
3. LOS_OMIT_LR_CNT:忽略的LR层数,默认2层,即从调用LOS_MemAlloc的函数开始记录,可根据实际情况调整。需要此配置原因如下:
- LOS_MemAlloc接口内部也有函数调用;
- 外部可能对LOS_MemAlloc接口有封装;
- LOS_RECORD_LR_CNT 配置的LR层数有限;
......@@ -27,17 +27,17 @@
该调测功能可以分析关键的代码逻辑中是否存在内存泄漏。开启这个功能,每次申请内存时,会记录LR信息。在需要检测的代码段前后,调用LOS_MemUsedNodeShow接口,每次都会打印指定内存池已使用的全部节点信息,对比前后两次的节点信息,新增的节点信息就是疑似泄漏的内存节点。通过LR,可以找到具体申请的代码位置,进一步确认是否泄漏。
调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等手段查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。
调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等工具查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。
```
node size LR[0] LR[1] LR[2]
0x10017320: 0x528 0x9b004eba 0x9b004f60 0x9b005002
0x10017848: 0xe0 0x9b02c24e 0x9b02c246 0x9b008ef0
0x10017928: 0x50 0x9b008ed0 0x9b068902 0x9b0687c4
node size LR[0] LR[1] LR[2]
0x10017320: 0x528 0x9b004eba 0x9b004f60 0x9b005002
0x10017848: 0xe0 0x9b02c24e 0x9b02c246 0x9b008ef0
0x10017928: 0x50 0x9b008ed0 0x9b068902 0x9b0687c4
0x10017978: 0x24 0x9b008ed0 0x9b068924 0x9b0687c4
0x1001799c: 0x30 0x9b02c24e 0x9b02c246 0x9b008ef0
0x100179cc: 0x5c 0x9b02c24e 0x9b02c246 0x9b008ef0
0x1001799c: 0x30 0x9b02c24e 0x9b02c246 0x9b008ef0
0x100179cc: 0x5c 0x9b02c24e 0x9b02c246 0x9b008ef0
```
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
......@@ -62,9 +62,10 @@ node size LR[0] LR[1] LR[2]
**示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试.
代码实现如下:
```
```c
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
......@@ -86,29 +87,29 @@ void MemLeakTest(void)
编译运行输出log如下:
```
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
```
对比两次log,差异如下,这些内存节点就是疑似泄漏的内存块:
```
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
```
......
......@@ -6,16 +6,16 @@
CPU(中央处理器,Central Processing Unit)占用率分为系统CPU占用率、进程CPU占用率、任务CPU占用率和中断CPU占用率。用户通过系统级的CPU占用率,判断当前系统负载是否超出设计规格。通过系统中各个进程/任务/中断的CPU占用情况,判断各个进程/任务/中断的CPU占用率是否符合设计的预期。
- 系统CPU占用率(CPU Percent)
指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示系统满负荷运转。
指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示系统满负荷运转。
- 进程CPU占用率
指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该进程。
指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该进程。
- 任务CPU占用率
指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该任务。
指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该任务。
- 中断CPU占用率
指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该中断。
指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该中断。
## 运行机制
......@@ -50,13 +50,14 @@ OpenHarmony 提供以下四种CPU占用率的信息查询:
**表1** CPUP模块接口
| 功能分类 | 接口**名称** | 描述 |
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 系统CPU占用率 | LOS_HistorySysCpuUsage | 获取系统历史CPU占用率 |
| 进程CPU占用率 | LOS_HistoryProcessCpuUsage | 获取指定进程历史CPU占用率 |
| 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 |
| 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 |
| 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 |
| 系统CPU占用率 | LOS_HistorySysCpuUsage | 获取系统历史CPU占用率 |
| 进程CPU占用率 | LOS_HistoryProcessCpuUsage | 获取指定进程历史CPU占用率 |
| 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 |
| 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 |
| 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 |
| 重置 | LOS_CpupReset | 重置CPU 占用率相关数据 |
### 开发流程
......@@ -102,48 +103,49 @@ CPU占用率的典型开发流程:
**示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
代码实现如下:
```
```c
#include "los_task.h"
#include "los_cpup.h"
#include "los_cpup.h"
#define MODE 4
UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID)
{
UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID)
{
printf("entry cpup test example\n");
while(1) {
usleep(100);
while (1) {
usleep(100); // 100: delay for 100ms
}
}
UINT32 ItCpupTest(VOID)
{
UINT32 ItCpupTest(VOID)
{
UINT32 ret;
UINT32 cpupUse;
TSK_INIT_PARAM_S cpupTestTask = { 0 };
TSK_INIT_PARAM_S cpupTestTask = {0};
memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S));
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleCpup;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800;
cpupTestTask.usTaskPrio = 5;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800; // 0x800: cpup test task stack size
cpupTestTask.usTaskPrio = 5; // 5: cpup test task priority
ret = LOS_TaskCreate(&g_cpuTestTaskID, &cpupTestTask);
if(ret != LOS_OK) {
if (ret != LOS_OK) {
printf("cpupTestTask create failed .\n");
return LOS_NOK;
}
usleep(100);
usleep(100); // 100: delay for 100ms
/* 获取当前系统历史CPU占用率 */
cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S);
cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S);
printf("the history system cpu usage in all time:%u.%u\n",
cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
/* 获取指定任务的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskID, CPU_LESS_THAN_1S);
/* 获取指定任务的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskID, CPU_LESS_THAN_1S);
printf("cpu usage of the cpupTestTask in all time:\n TaskID: %d\n usage: %u.%u\n",
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
}
```
......@@ -151,7 +153,7 @@ UINT32 ItCpupTest(VOID)
编译运行得到的结果为:
```
entry cpup test example
the history system cpu usage in all time: 3.0
......
......@@ -45,7 +45,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- IDENTITY类型UINTPTR,表示事件操作的主体对象。
- Params类型UINTPTR,表示事件的参数。
示例:
```
假设需要新增对文件(fd1、fd2)读写操作的简易插桩,
自定义读操作为type:1, 写操作为type:2,则
......@@ -63,7 +63,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- TYPE用于设置具体的事件类型,可以在头文件los_trace.h中的enum LOS_TRACE_TYPE中自定义事件类型。定义方法和规则可以参考其他事件类型。
- IDENTITY和Params的类型及含义同简易插桩。
示例:
```
1.在enum LOS_TRACE_MASK中定义事件掩码,即模块级别的事件类型。
定义规范为TRACE_#MOD#_FLAG,#MOD#表示模块名。
......@@ -86,9 +86,9 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
LOS_TRACE(FS_READ, fp, fd, flag, size); // 读文件的代码桩,
#TYPE#之后的入参就是上面3中的FS_READ_PARAMS函数的入参
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见kernel\include\los_trace.h
> 预置的Trace事件及参数均可以通过上述方式进行裁剪,参数详见 [kernel\include\los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h)
- Trace Mask事件过滤接口LOS_TraceEventMaskSet(UINT32 mask),其入参mask仅高28位生效(对应LOS_TRACE_MASK中某模块的使能位),仅用于模块的过滤,暂不支持针对某个特定事件的细粒度过滤。例如:LOS_TraceEventMaskSet(0x202),则实际设置生效的mask为0x200(TRACE_QUE_FLAG),QUE模块的所有事件均会被采集。一般建议使用的方法为: LOS_TraceEventMaskSet(TRACE_EVENT_FLAG | TRACE_MUX_FLAG | TRACE_SEM_FLAG | TRACE_QUE_FLAG);
......@@ -100,8 +100,8 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则,
示例如下:
```
```c
BOOL Example_HwiNumFilter(UINT32 hwiNum)
{
if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) {
......@@ -117,7 +117,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
### 用户态
新增trace字符设备,位于"/dev/trace",通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制:
新增trace字符设备,位于"/dev/trace"通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制:
- read: 用户态读取Trace记录数据
......@@ -125,8 +125,8 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- ioctl: 用户态Trace控制操作,包括
```
```c
#define TRACE_IOC_MAGIC 'T'
#define TRACE_START _IO(TRACE_IOC_MAGIC, 1)
#define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2)
......@@ -148,7 +148,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
开启Trace调测的典型流程如下:
1. 配置Trace模块相关宏。
配置Trace控制宏LOSCFG_KERNEL_TRACE,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel-&gt;Enable Hook Feature-&gt;Enable Trace Feature"中打开:
配置Trace控制宏LOSCFG_KERNEL_TRACE,默认关,在 kernel/liteos_a 目录下执行 make update_config 命令配置 "Kernel-&gt;Enable Hook Feature-&gt;Enable Trace Feature" 中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| -------- | -------- | -------- | -------- |
......@@ -156,7 +156,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
| LOSCFG_RECORDER_MODE_OFFLINE | Trace&nbsp;work&nbsp;mode&nbsp;-&gt;Offline&nbsp;mode | Trace工作模式为离线模式 | YES/NO |
| LOSCFG_RECORDER_MODE_ONLINE | Trace&nbsp;work&nbsp;mode&nbsp;-&gt;Online&nbsp;mode | Trace工作模式为在线模式 | YES/NO |
| LOSCFG_TRACE_CLIENT_INTERACT | Enable&nbsp;Trace&nbsp;Client&nbsp;Visualization&nbsp;and&nbsp;Control | 使能与Trace&nbsp;IDE&nbsp;(dev&nbsp;tools)的交互,包括数据可视化和流程控制 | YES/NO |
| LOSCFG_TRACE_FRAME_CORE_MSG | Enable&nbsp;Record&nbsp;more&nbsp;extended&nbsp;content&nbsp;-<br>&gt;Record&nbsp;cpuid,&nbsp;hardware&nbsp;interrupt<br>&nbsp;status,&nbsp;task&nbsp;lock&nbsp;status | 记录CPUID、中断状态、锁任务状态 | YES/NO |
| LOSCFG_TRACE_FRAME_CORE_MSG | Enable&nbsp;Record&nbsp;more&nbsp;extended content<br>->Record&nbsp;cpuid,&nbsp;hardware&nbsp;interrupt status,&nbsp;task&nbsp;lock&nbsp;status | 记录CPUID、中断状态、锁任务状态 | YES/NO |
| LOSCFG_TRACE_FRAME_EVENT_COUNT | Enable&nbsp;Record&nbsp;more&nbsp;extended&nbsp;content<br>&nbsp;-&gt;Record&nbsp;event&nbsp;count,<br>&nbsp;which&nbsp;indicate&nbsp;the&nbsp;sequence&nbsp;of&nbsp;happend&nbsp;events | 记录事件的次序编号 | YES/NO |
| LOSCFG_TRACE_FRAME_MAX_PARAMS | Record&nbsp;max&nbsp;params | 配置记录事件的最大参数个数 | INT |
| LOSCFG_TRACE_BUFFER_SIZE | Trace&nbsp;record&nbsp;buffer&nbsp;size | 配置Trace的缓冲区大小 | INT |
......@@ -165,7 +165,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
3. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动trace)。
4. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见los_trace.h 中的LOS_TRACE_MASK定义。
4. (可选)调用LOS_TraceEventMaskSet设置需要追踪的事件掩码(系统默认的事件掩码仅使能中断与任务事件),事件掩码参见 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的LOS_TRACE_MASK定义。
5. 在需要记录事件的代码起始点调用LOS_TraceStart。
......@@ -203,50 +203,52 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
### 内核态示例代码
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下:
```
```c
#include "los_trace.h"
UINT32 g_traceTestTaskId;
VOID Example_Trace(VOID)
{
UINT32 ret;
{
UINT32 ret;
LOS_TaskDelay(10);
/* 开启trace */
ret = LOS_TraceStart();
if (ret != LOS_OK) {
dprintf("trace start error\n");
return;
}
/* 触发任务切换的事件 */
LOS_TaskDelay(1);
LOS_TaskDelay(1);
LOS_TaskDelay(1);
/* 停止trace */
LOS_TraceStop();
ret = LOS_TraceStart();
if (ret != LOS_OK) {
dprintf("trace start error\n");
return;
}
/* 触发任务切换的事件 */
LOS_TaskDelay(1);
LOS_TaskDelay(1);
LOS_TaskDelay(1);
/* 停止trace */
LOS_TraceStop();
LOS_TraceRecordDump(FALSE);
}
UINT32 Example_Trace_test(VOID){
UINT32 ret;
TSK_INIT_PARAM_S traceTestTask;
/* 创建用于trace测试的任务 */
memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */
traceTestTask.uwStackSize = 0x800;
traceTestTask.usTaskPrio = 5;
traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
if(ret != LOS_OK){
dprintf("TraceTestTask create failed .\n");
return LOS_NOK;
}
/* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */
LOS_TraceStop();
LOS_TraceReset();
/* 开启任务模块事件记录 */
LOS_TraceEventMaskSet(TRACE_TASK_FLAG);
UINT32 Example_Trace_test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S traceTestTask;
/* 创建用于trace测试的任务 */
memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */
traceTestTask.uwStackSize = 0x800; // 0x800: trace test task stacksize
traceTestTask.usTaskPrio = 5; // 5: trace test task priority
traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
if (ret != LOS_OK) {
dprintf("TraceTestTask create failed .\n");
return LOS_NOK;
}
/* 系统默认情况下已启动trace, 因此可先关闭trace后清除缓存区后,再重启trace */
LOS_TraceStop();
LOS_TraceReset();
/* 开启任务模块事件记录 */
LOS_TraceEventMaskSet(TRACE_TASK_FLAG);
return LOS_OK;
}
LOS_MODULE_INIT(Example_Trace_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
......@@ -262,7 +264,7 @@ LOS_MODULE_INIT(Example_Trace_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
***TraceInfo begin***
clockFreq = 50000000
CurEvtIndex = 7
Index Time(cycles) EventType CurTask Identity params
Index Time(cycles) EventType CurTask Identity params
0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4 0x0
1 0x366d74ae 0x45 0x0 0x1 0x0 0x8 0x1f
2 0x36940da6 0x45 0x1 0xc 0x1f 0x4 0x9
......@@ -276,13 +278,13 @@ Index Time(cycles) EventType CurTask Identity params
输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。
- EventType:表示的具体事件可查阅头文件los_trace.h中的enum LOS_TRACE_TYPE。
- EventType:表示的具体事件可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的enum LOS_TRACE_TYPE。
- CurrentTask:表示当前运行在哪个任务中,值为taskid。
- Identity:表示事件操作的主体对象,可查阅头文件los_trace.h中的\#TYPE\#_PARAMS。
- Identity:表示事件操作的主体对象,可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的\#TYPE\#_PARAMS。
- params:表示的事件参数可查阅头文件los_trace.h中的\#TYPE\#_PARAMS。
- params:表示的事件参数可查阅头文件 [los_trace.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_trace.h) 中的\#TYPE\#_PARAMS。
下面以序号为0的输出项为例,进行说明。
......@@ -298,7 +300,7 @@ Index Time(cycles) EventType CurTask Identity params
- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义:
```
```c
#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \
taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
```
......
......@@ -5,7 +5,7 @@
Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、踩内存分析以及backtrace功能等维测手段,可以提高用户态内存相关问题的定位效率。
采用了对malloc/free接口进行插桩,保存关键节点信息,然后程序在申请和释放内存时进行内存节点完整性校验,最后在程序结束时通过统计节点信息得到内存统计信息并根据统计信息判断内存是否泄漏的设计思想
采用了对malloc/free接口进行插桩,保存关键节点信息,然后程序在申请和释放内存时进行内存节点完整性校验,最后在程序结束时通过统计节点信息得到内存统计信息并根据统计信息判断内存是否泄漏的设计思想
## 运行机制
......@@ -28,7 +28,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
![zh-cn_image_0000001165890518](figures/zh-cn_image_0000001165890518.png)
其中,TID表示线程ID;PID表示进程ID;ptr表示申请的内存地址;size表示申请的内存大小;lr[n]表示函数调用栈地址,变量n可以根据具体场景的需要进行配置。
其中,TID表示线程ID;PID表示进程ID;ptr表示申请的内存地址;size表示申请的内存大小;lr[n]表示函数调用栈地址,变量n的大小可以根据具体场景的需要进行配置。
释放内存时,将free等接口的入参指针与node的ptr字段进行匹配,如果相同则删除该内存节点控制块信息。
......@@ -46,7 +46,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
### 内存完整性检查
- 使用malloc申请内存(小于等于0x1c000bytes时通过堆分配算法分配)
- 使用malloc申请内存(小于等于0x1c000 bytes时通过堆分配算法分配)
用户程序申请堆内存时,在堆内存节点处添加校验值等信息,如果校验值异常,则很有可能是前一块堆内存使用越界导致的(目前无法识别校验值被野指针破坏的场景)。在内存申请、释放时校验内存节点校验值的正确性,若内存节点被破坏,校验失败时则输出tid、pid及当前被踩节点前一块堆内存申请时保存的调用栈信息,通过addr2line工具可获得具体的代码行信息,辅助用户解决问题。
**图4** node节点头信息添加校验值
......@@ -59,7 +59,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
![zh-cn_image_0000001165890904](figures/zh-cn_image_0000001165890904.png)
- 使用malloc申请内存(大于0x1c000bytes时通过mmap申请)
- 使用malloc申请内存(大于0x1c000 bytes时通过mmap申请)
当malloc通过mmap申请大块内存时,在返回给用户使用的内存区间头和尾分别多申请一个页,一个页PAGE_SIZE当前为0x1000,这两个页分别通过mprotect接口设置权限为PROT_NONE(无可读可写权限),可以有效防止内存越界读写问题:越界读写数据时由于无读写权限而导致用户程序异常,根据异常调用栈信息可找到相应的代码逻辑。
**图6** malloc通过mmap机制申请内存的内存布局
......@@ -119,7 +119,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
代码功能:显式调用调测模块的相关接口对用户代码进行内存校验。
```
```c
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
......@@ -127,7 +127,8 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
#define MALLOC_LEAK_SIZE 0x300
void func(void) {
void func(void)
{
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE);
}
......@@ -158,15 +159,15 @@ $ clang -o mem_check mem_check.c -funwind-tables -rdynamic -g -mfloat-abi=softfp
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 本编译示例基于将编译器的路径写入环境变量中,即.bashrc文件中。
>
>
> - 编译用户程序及所需的lib库时,需要添加编译选项-funwind-tables,-rdynamic,-g,用于栈回溯。
>
>
> - -mfloat-abi=softfp,-mcpu=cortex-a7,-mfpu=neon-vfpv4编译选项用于指定具体的芯片架构、浮点数计算优化、fpu,与具体的libc库使用的编译选项保持一致,否则链接时可能出现找不到libc库文件。
>
>
> - -target arm-liteos用于指定编译器相关库文件路径。
>
>
> - --sysroot=/home/&lt;user-name&gt;/directory/out/hispark_taurus/ipcamera_hispark_taurus/sysroot用于指定编译器库文件搜索根目录,假设OpenHarmony工程代码存放路径为/home/&lt;user-name&gt;/directory。其中out/hispark_taurus/ipcamera_hispark_taurus路径为在编译时,hb set命令指定的具体产品,本示例选择的是ipcamera_hispark_taurus产品。
>
>
> - $(clang -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos -print-file-name=libunwind.a)用于指定相应的unwind库的路径。
......@@ -175,7 +176,7 @@ $ clang -o mem_check mem_check.c -funwind-tables -rdynamic -g -mfloat-abi=softfp
```
OHOS # ./mem_check
OHOS #
OHOS #
==PID:4== Heap memory statistics(bytes): // 堆内存统计信息
[Check point]: // check点调用栈
#00: <main+0x38>[0x86c] -> mem_check
......@@ -293,14 +294,15 @@ kill -37 <pid> # 检查堆内存头节点是否完整
代码功能:构造内存问题利用命令行方式进行内存调测。
```
```c
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#define MALLOC_LEAK_SIZE 0x300
void func(void) {
void func(void)
{
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE);
}
......@@ -325,9 +327,9 @@ int main()
```
OHOS # ./mem_check --mwatch // 利用task命令可以查到mem_check进程的pid为4
OHOS #
OHOS #
OHOS # kill -35 4 // 查看堆内存统计信息
OHOS #
OHOS #
==PID:4== Heap memory statistics(bytes):
[Check point]:
#00: <arm_signal_process+0x5c>[0x58dfc] -> /lib/libc.so
......@@ -337,7 +339,7 @@ OHOS #
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
OHOS # kill -36 4 // 检查是否存在堆内存泄漏
OHOS #
OHOS #
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
......@@ -355,7 +357,7 @@ OHOS #
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
OHOS # kill -37 4 // 检查堆内存头节点的完整性
OHOS #
OHOS #
Check heap integrity ok!
```
......@@ -391,109 +393,109 @@ Now using addr2line ...
##### 使用mrecord参数命令
1. 执行用户程序并指定记录内存调测信息的文件路径
```
OHOS # ./mem_check --mrecord /storage/check.txt
```
2. 利用kill -35 &lt;pid&gt;统计内存信息,该信息将会输出到文件中,使用cat命令查看
```
OHOS # kill -35 4
OHOS # Memory statistics information saved in /storage/pid(4)_check.txt
OHOS # cat /storage/pid(4)_check.txt
==PID:4== Heap memory statistics(bytes):
[Check point]:
#00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so
[TID: 18, Used: 0x640]
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
```
3. 利用kill -36 &lt;pid&gt;校验内存完整性,该信息将会输出到文件中,使用cat命令查看
```
OHOS # kill -36 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
OHOS # cat /storage/pid(4)_check.txt
==PID:4== Heap memory statistics(bytes):
[Check point]:
#00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so
[TID: 18, Used: 0x640]
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so
#01: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x14>[0x724] -> mem_check
#01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x6ec] -> mem_check
#01: <main+0x30>[0x740] -> mem_check
#02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
4. 利用kill -9 &lt;pid&gt;杀掉当前进程,进程退出后会默认校验内存完整性,该信息将会输出到文件中,使用cat命令查看
```
OHOS # kill -9 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
Check heap integrity ok!
OHOS # cat /storage/pid(4)_check.txt
OHOS #
OHOS #
==PID:4== Heap memory statistics(bytes):
[Check point]:
#00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so
[TID: 18, Used: 0x640]
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so
#01: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x14>[0x724] -> mem_check
#01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x6ec] -> mem_check
#01: <main+0x30>[0x740] -> mem_check
#02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so
#01: <exit+0x28>[0x11b2c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x14>[0x724] -> mem_check
#01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x6ec] -> mem_check
#01: <main+0x30>[0x740] -> mem_check
#02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
......@@ -504,7 +506,7 @@ Now using addr2line ...
### UAF(Use after free)
- 申请小块内存(不大于0x1c000字节
- 申请小块内存(不大于0x1c000 bytes
free之后:
读操作:读取free之后的内存大概率是魔术数字(0xFEFEFEFE)
......@@ -515,7 +517,7 @@ Now using addr2line ...
写操作:无法校验。
- 申请大块内存(大于0x1c000)
- 申请大块内存(大于0x1c000 bytes
堆内存由malloc通过调用mmap接口申请,free之后若仍访问该内存,则用户程序异常(该内存区间已被unmap)。
......@@ -526,22 +528,23 @@ Double free时,用户程序将会异常退出。
### 堆内存节点被踩
- 申请小块内存(不大于0x1c000)
- 申请小块内存(不大于0x1c000 bytes)
堆内存节点被踩时,用户程序将会异常退出,并输出破坏被踩节点的可能的堆内存申请调用栈,对于野指针踩内存情况无法校验出来。例如用户程序mem_check中存在堆内存越界踩的情况,利用命令行方式可以获得踩内存的可能的具体位置。
```
OHOS # ./mem_check --mwatch
OHOS #
OHOS # ./mem_check --mwatch
OHOS #
==PID:6== Memory integrity information:
[TID:28 allocated addr: 0x272e1ea0, size: 0x120] The possible attacker was allocated from:
#00: <malloc+0x808>[0x640e8] -> /lib/libc.so
#01: <threadFunc1+0x7c>[0x21d0] -> mem_check
#01: <threadFunc1+0x7c>[0x21d0] -> mem_check
```
可以通过调用栈解析脚本对调用栈信息进行解析。
- 申请大块内存(大于0x1c000)
- 申请大块内存(大于0x1c000 bytes
堆内存由malloc通过mmap接口申请,申请得到的堆内存块前后各置一个size为PAGE_SIZE大小的区间,设置无读写权限,读写操作会触发用户程序异常。
......@@ -3,7 +3,7 @@
## 基本概念
LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double Free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能:
......@@ -20,7 +20,7 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能:
LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。
- 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头点区的影子内存设置为“不可读写”状态。
- 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头点区的影子内存设置为“不可读写”状态。
- 内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。
......@@ -38,12 +38,12 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信
**表1** LMS模块接口说明
| 功能分类 | 接口名 | 描述 |
| 功能分类 | 接口名 | 描述 |
| -------- | -------- | -------- |
| 添加指定内存池被检测 | LOS_LmsCheckPoolAdd | 将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。 |
| 删除指定内存池不被检测 | LOS_LmsCheckPoolDel | 不检测指定内存池地址范围内的合法性校验。 |
| 使能指定内存段锁保护 | LOS_LmsAddrProtect | 为某段内存地址上锁,设置为不可读写,一旦访问则报错。 |
| 去能指定内存段锁保护 | LOS_LmsAddrDisableProtect | 为某段内存地址解锁,设置为可读写。 |
| 添加指定内存池被检测 | LOS_LmsCheckPoolAdd | 将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。 |
| 删除指定内存池不被检测 | LOS_LmsCheckPoolDel | 不检测指定内存池地址范围内的合法性校验。 |
| 使能指定内存段锁保护 | LOS_LmsAddrProtect | 为某段内存地址上锁,设置为不可读写,一旦访问则报错。 |
| 去能指定内存段锁保护 | LOS_LmsAddrDisableProtect | 为某段内存地址解锁,设置为可读写。 |
### 用户态
......@@ -61,19 +61,19 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信
1. 配置LMS模块相关宏。
配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel-&gt;Enable Lite Memory Sanitizer"中打开YES:
| 宏 | menuconfig选项 | 含义 | 取值 |
| 宏 | menuconfig选项 | 含义 | 取值 |
| -------- | -------- | -------- | -------- |
| LOSCFG_KERNEL_LMS | Enable&nbsp;Lms&nbsp;Feature | Lms模块的裁剪开关 | YES/NO |
| LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms&nbsp;check&nbsp;pool&nbsp;max&nbsp;num | LMS支持的检测内存池最大个数 | INT |
| LOSCFG_LMS_LOAD_CHECK | Enable&nbsp;lms&nbsp;read&nbsp;check | LMS内存读检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_STORE_CHECK | Enable&nbsp;lms&nbsp;write&nbsp;check | LMS内存写检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_CHECK_STRICT | Enable&nbsp;lms&nbsp;strict&nbsp;check,&nbsp;byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO |
| LOSCFG_KERNEL_LMS | Enable&nbsp;Lms&nbsp;Feature | Lms模块的裁剪开关 | YES/NO |
| LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms&nbsp;check&nbsp;pool&nbsp;max&nbsp;num | LMS支持的检测内存池最大个数 | INT |
| LOSCFG_LMS_LOAD_CHECK | Enable&nbsp;lms&nbsp;read&nbsp;check | LMS内存读检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_STORE_CHECK | Enable&nbsp;lms&nbsp;write&nbsp;check | LMS内存写检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_CHECK_STRICT | Enable&nbsp;lms&nbsp;strict&nbsp;check,&nbsp;byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO |
2. 在被检测模块的编译脚本中,修改编译选项。
增加LMS检测编译选项-fsanitize=kernel-address。为避免编译器优化,增加-O0编译选项。
gcc与clang编译选项存在差异,参照如下示例:
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
......@@ -105,14 +105,15 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信
2. 构造内存溢出错误和释放后使用错误。
3. 添加-fsanitize=kernel-address后编译执行,观察输出结果
3. 添加-fsanitize=kernel-address后编译执行,观察输出结果
#### 内核态示例代码
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下:
```
```c
#define PAGE_SIZE (0x1000U)
#define INDEX_MAX 20
UINT32 g_lmsTestTaskId;
......@@ -138,31 +139,32 @@ static VOID LmsTestUseAfterFree(VOID)
PRINTK("\n######%s start ######\n", __FUNCTION__);
UINT32 i;
CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX);
LOS_MemFree(g_testLmsPool, str);
(VOID)LOS_MemFree(g_testLmsPool, str);
PRINTK("str[%2d]=0x%2x ", 0, str[0]); /* trigger use after free at str[0] */
PRINTK("\n######%s stop ######\n", __FUNCTION__);
}
VOID LmsTestCaseTask(VOID)
{
{
testPoolInit();
LmsTestOsmallocOverflow();
LmsTestUseAfterFree();
}
UINT32 Example_Lms_test(VOID){
UINT32 ret;
TSK_INIT_PARAM_S lmsTestTask;
/* 创建用于lms测试的任务 */
memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S));
UINT32 Example_Lms_test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S lmsTestTask;
/* 创建用于lms测试的任务 */
memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S));
lmsTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LmsTestCaseTask;
lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */
lmsTestTask.uwStackSize = 0x800;
lmsTestTask.usTaskPrio = 5;
lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask);
if(ret != LOS_OK){
PRINT_ERR("LmsTestTask create failed .\n");
return LOS_NOK;
}
lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */
lmsTestTask.uwStackSize = 0x800; // 0x800: lms test task stack size
lmsTestTask.usTaskPrio = 5; // 5: lms test task priority
lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask);
if (ret != LOS_OK) {
PRINT_ERR("LmsTestTask create failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
LOS_MODULE_INIT(Example_Lms_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
......@@ -172,7 +174,7 @@ LOS_MODULE_INIT(Example_Lms_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
#### 内核态结果验证
输出结果如下:
```
######LmsTestOsmallocOverflow start ######
[ERR][KProcess:LmsTestCaseTask]* Kernel Address Sanitizer Error Detected Start *
......@@ -257,9 +259,9 @@ str[ 0]=0x 0
### 用户态开发流程
在待检测的app编译脚本中,添加如下参数即可, 完整示例可参见/kernel/liteos_a/apps/lms/BUILD.gn。
在待检测的app编译脚本中,添加如下参数即可, 完整示例可参见 [/kernel/liteos_a/apps/lms/BUILD.gn](https://gitee.com/openharmony/kernel_liteos_a/blob/master/apps/lms/BUILD.gn)
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
......@@ -308,14 +310,14 @@ if ("$ohos_build_compiler_specified" == "gcc") {
1. 构造内存溢出错误和释放后使用错误。
2. 添加对应编译选项后,重新编译执行
2. 添加对应编译选项后,重新编译执行
#### 用户态示例代码
实例代码如下:
```
```c
static void BufWriteTest(void *buf, int start, int end)
{
for (int i = start; i <= end; i++) {
......@@ -332,7 +334,7 @@ static void BufReadTest(void *buf, int start, int end)
static void LmsMallocTest(void)
{
printf("\n-------- LmsMallocTest Start --------\n");
char *buf = (char *)malloc(16);
char *buf = (char *)malloc(16); // 16: buffer size for test
BufReadTest(buf, -1, 16);
free(buf);
printf("\n-------- LmsMallocTest End --------\n");
......@@ -340,7 +342,7 @@ static void LmsMallocTest(void)
static void LmsFreeTest(void)
{
printf("\n-------- LmsFreeTest Start --------\n");
char *buf = (char *)malloc(16);
char *buf = (char *)malloc(16); // 16: buffer size for test
free(buf);
BufReadTest(buf, 1, 1);
free(buf);
......@@ -349,7 +351,7 @@ static void LmsFreeTest(void)
int main(int argc, char * const * argv)
{
printf("\n############### Lms Test start ###############\n");
char *tmp = (char *)malloc(5000);
char *tmp = (char *)malloc(5000); // 5000: temp buffer size
LmsMallocTest();
LmsFreeTest();
printf("\n############### Lms Test End ###############\n");
......@@ -360,7 +362,7 @@ int main(int argc, char * const * argv)
#### 用户态结果验证
输出结果如下:
```
* Lite Memory Sanitizer Error Detected *
Heap buffer overflow error detected!
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册