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

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

Merge pull request !12944 from zhangdengyu/cherry-pick-1672124112
...@@ -12,7 +12,7 @@ Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样 ...@@ -12,7 +12,7 @@ Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样
Perf提供2种工作模式,计数模式和采样模式。 Perf提供2种工作模式,计数模式和采样模式。
计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径 计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径
## 接口说明 ## 接口说明
...@@ -26,19 +26,19 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细 ...@@ -26,19 +26,19 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
| 功能分类 | 接口描述 | | 功能分类 | 接口描述 |
| -------- | -------- | | -------- | -------- |
| 开启/停止Perf采样 | LOS_PerfStart:开启采样<br/>LOS_PerfStop:停止采样 | | 开启/停止Perf采样 | LOS_PerfInit : 初始化Perf<br/>LOS_PerfStart:开启采样<br/>LOS_PerfStop:停止采样 |
| 配置Perf采样事件 | LOS_PerfConfig:配置采样事件的类型、周期等 | | 配置Perf采样事件 | LOS_PerfConfig:配置采样事件的类型、周期等 |
| 读取采样数据 | LOS_PerfDataRead:读取采样数据到指定地址 | | 读取采样数据 | LOS_PerfDataRead:读取采样数据到指定地址 |
| 注册采样数据缓冲区的钩子函数 | LOS_PerfNotifyHookReg:注册缓冲区水线到达的处理钩子<br/>LOS_PerfFlushHookReg:注册缓冲区刷cache的钩子 | | 注册采样数据缓冲区的钩子函数 | 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中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。 2. 采样数据缓冲区为环形buffer,buffer中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。
3. 缓冲区有限,用户可通过注册水线到达的钩子进行buffer溢出提醒或buffer读操作。默认水线值为buffer总大小的1/2。 示例如下: 3. 缓冲区有限,用户可通过注册水线到达的钩子进行buffer溢出提醒或buffer读操作。默认水线值为buffer总大小的1/2。 示例如下:
``` ```c
VOID Example_PerfNotifyHook(VOID) VOID Example_PerfNotifyHook(VOID)
{ {
CHAR buf[LOSCFG_PERF_BUFFER_SIZE] = {0}; CHAR buf[LOSCFG_PERF_BUFFER_SIZE] = {0};
...@@ -52,7 +52,7 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细 ...@@ -52,7 +52,7 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
4. 若perf采样的buffer涉及到CPU跨cache,则用户可通过注册刷cache的钩子,进行cache同步。 示例如下: 4. 若perf采样的buffer涉及到CPU跨cache,则用户可通过注册刷cache的钩子,进行cache同步。 示例如下:
``` ```c
VOID Example_PerfFlushHook(VOID *addr, UINT32 size) VOID Example_PerfFlushHook(VOID *addr, UINT32 size)
{ {
OsCacheFlush(addr, size); /* platform interface */ OsCacheFlush(addr, size); /* platform interface */
...@@ -75,7 +75,7 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细 ...@@ -75,7 +75,7 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
- ioctl: 用户态Perf控制操作,包括 - ioctl: 用户态Perf控制操作,包括
``` ```c
#define PERF_IOC_MAGIC 'T' #define PERF_IOC_MAGIC 'T'
#define PERF_START _IO(PERF_IOC_MAGIC, 1) #define PERF_START _IO(PERF_IOC_MAGIC, 1)
#define PERF_STOP _IO(PERF_IOC_MAGIC, 2) #define PERF_STOP _IO(PERF_IOC_MAGIC, 2)
...@@ -142,9 +142,11 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细 ...@@ -142,9 +142,11 @@ OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细
前提条件:在menuconfig菜单中完成perf模块的配置。 前提条件:在menuconfig菜单中完成perf模块的配置。
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下: 实例代码如下:
``` ```c
#include "los_perf.h" #include "los_perf.h"
STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num) STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num)
{ {
...@@ -192,7 +194,7 @@ STATIC VOID perfTestHwEvent(VOID) ...@@ -192,7 +194,7 @@ STATIC VOID perfTestHwEvent(VOID)
PRINTK("--------sample mode------ \n"); PRINTK("--------sample mode------ \n");
attr.needSample = 1; attr.needSample = 1;
LOS_PerfConfig(&attr); LOS_PerfConfig(&attr);
LOS_PerfStart(2); LOS_PerfStart(2); // 2: set the section id to 2.
test(); /* this is any test function*/ test(); /* this is any test function*/
LOS_PerfStop(); LOS_PerfStop();
buf = LOS_MemAlloc(m_aucSysMem1, LOSCFG_PERF_BUFFER_SIZE); buf = LOS_MemAlloc(m_aucSysMem1, LOSCFG_PERF_BUFFER_SIZE);
...@@ -205,18 +207,19 @@ STATIC VOID perfTestHwEvent(VOID) ...@@ -205,18 +207,19 @@ STATIC VOID perfTestHwEvent(VOID)
OsPrintBuff(buf, len); /* print data */ OsPrintBuff(buf, len); /* print data */
(VOID)LOS_MemFree(m_aucSysMem1, buf); (VOID)LOS_MemFree(m_aucSysMem1, buf);
} }
UINT32 Example_Perf_test(VOID){ UINT32 Example_Perf_test(VOID)
{
UINT32 ret; UINT32 ret;
TSK_INIT_PARAM_S perfTestTask; TSK_INIT_PARAM_S perfTestTask;
/* 创建用于perf测试的任务 */ /* 创建用于perf测试的任务 */
memset(&perfTestTask, 0, sizeof(TSK_INIT_PARAM_S)); memset(&perfTestTask, 0, sizeof(TSK_INIT_PARAM_S));
perfTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)perfTestHwEvent; perfTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)perfTestHwEvent;
perfTestTask.pcName = "TestPerfTsk"; /* 测试任务名称 */ perfTestTask.pcName = "TestPerfTsk"; /* 测试任务名称 */
perfTestTask.uwStackSize = 0x800; perfTestTask.uwStackSize = 0x800; // 0x8000: perf test task stack size
perfTestTask.usTaskPrio = 5; perfTestTask.usTaskPrio = 5; // 5: perf test task priority
perfTestTask.uwResved = LOS_TASK_STATUS_DETACHED; perfTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_perfTestTaskId, &perfTestTask); ret = LOS_TaskCreate(&g_perfTestTaskId, &perfTestTask);
if(ret != LOS_OK){ if (ret != LOS_OK) {
PRINT_ERR("PerfTestTask create failed.\n"); PRINT_ERR("PerfTestTask create failed.\n");
return LOS_NOK; return LOS_NOK;
} }
...@@ -268,11 +271,11 @@ 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 ...@@ -268,11 +271,11 @@ 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可选如下: - option可选如下:
- -e,配置采样事件。可使用./perf list 中罗列的同类型事件。 - -e,配置采样事件。可使用./perf list 中罗列的同类型事件。
- -p,配置事件采样周期。 - -p,配置事件采样周期。
- -o, 指定perf采样数据结果保存的文件路径。 - -o指定perf采样数据结果保存的文件路径。
- -t,任务Id过滤(白名单),只采取指定任务中的上下文。如果不指定改参数,则默认采集所有的任务。 - -t,任务Id过滤(白名单),只采取指定任务中的上下文。如果不指定改参数,则默认采集所有的任务。
- -s, 配置采样的具体上下文类型,可查阅los_perf.h中定义的PerfSampleType。 - -s配置采样的具体上下文类型,可查阅los_perf.h中定义的PerfSampleType。
- -P, 进程Id过滤(白名单),只采取指定进程中的上下文。如果不指定改参数,则默认采集所有进程。 - -P进程Id过滤(白名单),只采取指定进程中的上下文。如果不指定改参数,则默认采集所有进程。
- -d, 是否进行分频(事件每发生64次累计+1),该选项仅在硬件cycle事件上生效。 - -d是否进行分频(事件每发生64次累计+1),该选项仅在硬件cycle事件上生效。
- command 为待统计的子程序。 - command 为待统计的子程序。
用户态命令行的典型使用方法如下: 用户态命令行的典型使用方法如下:
...@@ -355,7 +358,7 @@ save perf data success at /storage/data/perf.data ...@@ -355,7 +358,7 @@ save perf data success at /storage/data/perf.data
实例代码如下: 实例代码如下:
``` ```c
#include "fcntl.h" #include "fcntl.h"
#include "user_copy.h" #include "user_copy.h"
#include "sys/ioctl.h" #include "sys/ioctl.h"
...@@ -367,6 +370,7 @@ save perf data success at /storage/data/perf.data ...@@ -367,6 +370,7 @@ save perf data success at /storage/data/perf.data
#define PERF_IOC_MAGIC 'T' #define PERF_IOC_MAGIC 'T'
#define PERF_START _IO(PERF_IOC_MAGIC, 1) #define PERF_START _IO(PERF_IOC_MAGIC, 1)
#define PERF_STOP _IO(PERF_IOC_MAGIC, 2) #define PERF_STOP _IO(PERF_IOC_MAGIC, 2)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char *buf = NULL; char *buf = NULL;
......
...@@ -42,11 +42,12 @@ LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这 ...@@ -42,11 +42,12 @@ LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这
**示例代码** **示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试.
代码实现如下: 代码实现如下:
``` ```c
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "los_memory.h" #include "los_memory.h"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- 内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小; - 内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小;
- 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量 - 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量
- 其他统计信息:调用接口LOS_MemInfoGet时,会扫描内存池的节点信息,统计出相关信息。 - 其他统计信息:调用接口LOS_MemInfoGet时,会扫描内存池的节点信息,统计出相关信息。
...@@ -25,7 +25,7 @@ LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能, ...@@ -25,7 +25,7 @@ LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能,
关键结构体介绍: 关键结构体介绍:
``` ```c
typedef struct { typedef struct {
UINT32 totalUsedSize; // 内存池的内存使用量 UINT32 totalUsedSize; // 内存池的内存使用量
UINT32 totalFreeSize; // 内存池的剩余内存大小 UINT32 totalFreeSize; // 内存池的剩余内存大小
...@@ -38,7 +38,7 @@ typedef struct { ...@@ -38,7 +38,7 @@ typedef struct {
} LOS_MEM_POOL_STATUS; } 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\*最大空闲内存块大小/剩余内存大小)得出此时的动态内存池碎片率。 - 内存碎片率计算:同样调用LOS_MemInfoGet接口,可以获取内存池的剩余内存大小和最大空闲内存块大小,然后根据公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)得出此时的动态内存池碎片率。
...@@ -53,13 +53,12 @@ typedef struct { ...@@ -53,13 +53,12 @@ typedef struct {
3. 利用公式算出使用率及碎片率。 3. 利用公式算出使用率及碎片率。
**示例代码** **示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
代码实现如下: 代码实现如下:
``` ```c
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "los_task.h" #include "los_task.h"
...@@ -77,8 +76,7 @@ void MemInfoTaskFunc(void) ...@@ -77,8 +76,7 @@ void MemInfoTaskFunc(void)
unsigned char fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize; unsigned char fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize;
/* 算出内存池当前的使用率百分比 */ /* 算出内存池当前的使用率百分比 */
unsigned char usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool); 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, printf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
poolStatus.totalFreeSize, poolStatus.usageWaterLine);
} }
int MemTest(void) int MemTest(void)
...@@ -93,9 +91,9 @@ int MemTest(void) ...@@ -93,9 +91,9 @@ int MemTest(void)
ret = LOS_TaskCreate(&taskID, &taskStatus); ret = LOS_TaskCreate(&taskID, &taskStatus);
if (ret != LOS_OK) { if (ret != LOS_OK) {
printf("task create failed\n"); printf("task create failed\n");
return -1; return LOS_NOK;
} }
return 0; return LOS_OK;
} }
``` ```
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
2. LOS_RECORD_LR_CNT:记录的LR层数,默认3层;每层LR消耗sizeof(void \*)字节数的内存。 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_MemAlloc接口有封装; - 外部可能对LOS_MemAlloc接口有封装;
- LOS_RECORD_LR_CNT 配置的LR层数有限; - LOS_RECORD_LR_CNT 配置的LR层数有限;
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
该调测功能可以分析关键的代码逻辑中是否存在内存泄漏。开启这个功能,每次申请内存时,会记录LR信息。在需要检测的代码段前后,调用LOS_MemUsedNodeShow接口,每次都会打印指定内存池已使用的全部节点信息,对比前后两次的节点信息,新增的节点信息就是疑似泄漏的内存节点。通过LR,可以找到具体申请的代码位置,进一步确认是否泄漏。 该调测功能可以分析关键的代码逻辑中是否存在内存泄漏。开启这个功能,每次申请内存时,会记录LR信息。在需要检测的代码段前后,调用LOS_MemUsedNodeShow接口,每次都会打印指定内存池已使用的全部节点信息,对比前后两次的节点信息,新增的节点信息就是疑似泄漏的内存节点。通过LR,可以找到具体申请的代码位置,进一步确认是否泄漏。
调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等手段查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。 调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等工具查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。
``` ```
...@@ -62,9 +62,10 @@ node size LR[0] LR[1] LR[2] ...@@ -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 <stdio.h>
#include <string.h> #include <string.h>
#include "los_memory.h" #include "los_memory.h"
......
...@@ -6,16 +6,16 @@ ...@@ -6,16 +6,16 @@
CPU(中央处理器,Central Processing Unit)占用率分为系统CPU占用率、进程CPU占用率、任务CPU占用率和中断CPU占用率。用户通过系统级的CPU占用率,判断当前系统负载是否超出设计规格。通过系统中各个进程/任务/中断的CPU占用情况,判断各个进程/任务/中断的CPU占用率是否符合设计的预期。 CPU(中央处理器,Central Processing Unit)占用率分为系统CPU占用率、进程CPU占用率、任务CPU占用率和中断CPU占用率。用户通过系统级的CPU占用率,判断当前系统负载是否超出设计规格。通过系统中各个进程/任务/中断的CPU占用情况,判断各个进程/任务/中断的CPU占用率是否符合设计的预期。
- 系统CPU占用率(CPU Percent) - 系统CPU占用率(CPU Percent)
指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示系统满负荷运转。 指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示系统满负荷运转。
- 进程CPU占用率 - 进程CPU占用率
指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该进程。 指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该进程。
- 任务CPU占用率 - 任务CPU占用率
指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该任务。 指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该任务。
- 中断CPU占用率 - 中断CPU占用率
指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该中断。 指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该中断。
## 运行机制 ## 运行机制
...@@ -57,6 +57,7 @@ OpenHarmony 提供以下四种CPU占用率的信息查询: ...@@ -57,6 +57,7 @@ OpenHarmony 提供以下四种CPU占用率的信息查询:
| 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 | | 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 |
| 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 | | 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 |
| 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 | | 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 |
| 重置 | LOS_CpupReset | 重置CPU 占用率相关数据 |
### 开发流程 ### 开发流程
...@@ -102,10 +103,11 @@ CPU占用率的典型开发流程: ...@@ -102,10 +103,11 @@ CPU占用率的典型开发流程:
**示例代码** **示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
代码实现如下: 代码实现如下:
``` ```c
#include "los_task.h" #include "los_task.h"
#include "los_cpup.h" #include "los_cpup.h"
#define MODE 4 #define MODE 4
...@@ -113,27 +115,27 @@ UINT32 g_cpuTestTaskID; ...@@ -113,27 +115,27 @@ UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID) VOID ExampleCpup(VOID)
{ {
printf("entry cpup test example\n"); printf("entry cpup test example\n");
while(1) { while (1) {
usleep(100); usleep(100); // 100: delay for 100ms
} }
} }
UINT32 ItCpupTest(VOID) UINT32 ItCpupTest(VOID)
{ {
UINT32 ret; UINT32 ret;
UINT32 cpupUse; UINT32 cpupUse;
TSK_INIT_PARAM_S cpupTestTask = { 0 }; TSK_INIT_PARAM_S cpupTestTask = {0};
memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S)); memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S));
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleCpup; cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleCpup;
cpupTestTask.pcName = "TestCpupTsk"; cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800; cpupTestTask.uwStackSize = 0x800; // 0x800: cpup test task stack size
cpupTestTask.usTaskPrio = 5; cpupTestTask.usTaskPrio = 5; // 5: cpup test task priority
ret = LOS_TaskCreate(&g_cpuTestTaskID, &cpupTestTask); ret = LOS_TaskCreate(&g_cpuTestTaskID, &cpupTestTask);
if(ret != LOS_OK) { if (ret != LOS_OK) {
printf("cpupTestTask create failed .\n"); printf("cpupTestTask create failed .\n");
return LOS_NOK; return LOS_NOK;
} }
usleep(100); usleep(100); // 100: delay for 100ms
/* 获取当前系统历史CPU占用率 */ /* 获取当前系统历史CPU占用率 */
cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S); cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S);
......
...@@ -88,7 +88,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -88,7 +88,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
``` ```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** > ![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); - 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);
...@@ -101,7 +101,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -101,7 +101,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则, - 针对中断事件的Trace, 提供中断号过滤,用于解决某些场景下特定中断号频繁触发导致其他事件被覆盖的情况,用户可自定义中断过滤的规则,
示例如下: 示例如下:
``` ```c
BOOL Example_HwiNumFilter(UINT32 hwiNum) BOOL Example_HwiNumFilter(UINT32 hwiNum)
{ {
if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) { if ((hwiNum == TIMER_INT) || (hwiNum == DMA_INT)) {
...@@ -117,7 +117,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -117,7 +117,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
### 用户态 ### 用户态
新增trace字符设备,位于"/dev/trace",通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制: 新增trace字符设备,位于"/dev/trace"通过对设备节点的read、write、ioctl,实现用户态trace的读写和控制:
- read: 用户态读取Trace记录数据 - read: 用户态读取Trace记录数据
...@@ -126,7 +126,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -126,7 +126,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
- ioctl: 用户态Trace控制操作,包括 - ioctl: 用户态Trace控制操作,包括
``` ```c
#define TRACE_IOC_MAGIC 'T' #define TRACE_IOC_MAGIC 'T'
#define TRACE_START _IO(TRACE_IOC_MAGIC, 1) #define TRACE_START _IO(TRACE_IOC_MAGIC, 1)
#define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2) #define TRACE_STOP _IO(TRACE_IOC_MAGIC, 2)
...@@ -148,7 +148,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -148,7 +148,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
开启Trace调测的典型流程如下: 开启Trace调测的典型流程如下:
1. 配置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选项 | 含义 | 设置值 | | 配置项 | menuconfig选项 | 含义 | 设置值 |
| -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- |
...@@ -156,7 +156,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -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_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_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_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_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_FRAME_MAX_PARAMS | Record&nbsp;max&nbsp;params | 配置记录事件的最大参数个数 | INT |
| LOSCFG_TRACE_BUFFER_SIZE | Trace&nbsp;record&nbsp;buffer&nbsp;size | 配置Trace的缓冲区大小 | INT | | LOSCFG_TRACE_BUFFER_SIZE | Trace&nbsp;record&nbsp;buffer&nbsp;size | 配置Trace的缓冲区大小 | INT |
...@@ -165,7 +165,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -165,7 +165,7 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
3. (可选)调用LOS_TraceStop停止Trace后,清除缓冲区LOS_TraceReset(系统默认已启动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。 5. 在需要记录事件的代码起始点调用LOS_TraceStart。
...@@ -203,10 +203,11 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以 ...@@ -203,10 +203,11 @@ LiteOS-A内核的Trace模块提供下面几种功能,接口详细信息可以
### 内核态示例代码 ### 内核态示例代码
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下: 实例代码如下:
``` ```c
#include "los_trace.h" #include "los_trace.h"
UINT32 g_traceTestTaskId; UINT32 g_traceTestTaskId;
VOID Example_Trace(VOID) VOID Example_Trace(VOID)
...@@ -227,18 +228,19 @@ VOID Example_Trace(VOID) ...@@ -227,18 +228,19 @@ VOID Example_Trace(VOID)
LOS_TraceStop(); LOS_TraceStop();
LOS_TraceRecordDump(FALSE); LOS_TraceRecordDump(FALSE);
} }
UINT32 Example_Trace_test(VOID){ UINT32 Example_Trace_test(VOID)
{
UINT32 ret; UINT32 ret;
TSK_INIT_PARAM_S traceTestTask; TSK_INIT_PARAM_S traceTestTask;
/* 创建用于trace测试的任务 */ /* 创建用于trace测试的任务 */
memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S)); memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace; traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */ traceTestTask.pcName = "TestTraceTsk"; /* 测试任务名称 */
traceTestTask.uwStackSize = 0x800; traceTestTask.uwStackSize = 0x800; // 0x800: trace test task stacksize
traceTestTask.usTaskPrio = 5; traceTestTask.usTaskPrio = 5; // 5: trace test task priority
traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED; traceTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask); ret = LOS_TaskCreate(&g_traceTestTaskId, &traceTestTask);
if(ret != LOS_OK){ if (ret != LOS_OK) {
dprintf("TraceTestTask create failed .\n"); dprintf("TraceTestTask create failed .\n");
return LOS_NOK; return LOS_NOK;
} }
...@@ -276,13 +278,13 @@ Index Time(cycles) EventType CurTask Identity params ...@@ -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。 - 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的输出项为例,进行说明。 下面以序号为0的输出项为例,进行说明。
...@@ -298,7 +300,7 @@ Index Time(cycles) EventType CurTask Identity params ...@@ -298,7 +300,7 @@ Index Time(cycles) EventType CurTask Identity params
- Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义: - Identity和params的含义需要查看TASK_SWITCH_PARAMS宏定义:
``` ```c
#define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \ #define TASK_SWITCH_PARAMS(taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus) \
taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus taskId, oldPriority, oldTaskStatus, newPriority, newTaskStatus
``` ```
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、踩内存分析以及backtrace功能等维测手段,可以提高用户态内存相关问题的定位效率。 Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、踩内存分析以及backtrace功能等维测手段,可以提高用户态内存相关问题的定位效率。
采用了对malloc/free接口进行插桩,保存关键节点信息,然后程序在申请和释放内存时进行内存节点完整性校验,最后在程序结束时通过统计节点信息得到内存统计信息并根据统计信息判断内存是否泄漏的设计思想 采用了对malloc/free接口进行插桩,保存关键节点信息,然后程序在申请和释放内存时进行内存节点完整性校验,最后在程序结束时通过统计节点信息得到内存统计信息并根据统计信息判断内存是否泄漏的设计思想
## 运行机制 ## 运行机制
...@@ -28,7 +28,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、 ...@@ -28,7 +28,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
![zh-cn_image_0000001165890518](figures/zh-cn_image_0000001165890518.png) ![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字段进行匹配,如果相同则删除该内存节点控制块信息。 释放内存时,将free等接口的入参指针与node的ptr字段进行匹配,如果相同则删除该内存节点控制块信息。
...@@ -46,7 +46,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、 ...@@ -46,7 +46,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
### 内存完整性检查 ### 内存完整性检查
- 使用malloc申请内存(小于等于0x1c000bytes时通过堆分配算法分配) - 使用malloc申请内存(小于等于0x1c000 bytes时通过堆分配算法分配)
用户程序申请堆内存时,在堆内存节点处添加校验值等信息,如果校验值异常,则很有可能是前一块堆内存使用越界导致的(目前无法识别校验值被野指针破坏的场景)。在内存申请、释放时校验内存节点校验值的正确性,若内存节点被破坏,校验失败时则输出tid、pid及当前被踩节点前一块堆内存申请时保存的调用栈信息,通过addr2line工具可获得具体的代码行信息,辅助用户解决问题。 用户程序申请堆内存时,在堆内存节点处添加校验值等信息,如果校验值异常,则很有可能是前一块堆内存使用越界导致的(目前无法识别校验值被野指针破坏的场景)。在内存申请、释放时校验内存节点校验值的正确性,若内存节点被破坏,校验失败时则输出tid、pid及当前被踩节点前一块堆内存申请时保存的调用栈信息,通过addr2line工具可获得具体的代码行信息,辅助用户解决问题。
**图4** node节点头信息添加校验值 **图4** node节点头信息添加校验值
...@@ -59,7 +59,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、 ...@@ -59,7 +59,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
![zh-cn_image_0000001165890904](figures/zh-cn_image_0000001165890904.png) ![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(无可读可写权限),可以有效防止内存越界读写问题:越界读写数据时由于无读写权限而导致用户程序异常,根据异常调用栈信息可找到相应的代码逻辑。 当malloc通过mmap申请大块内存时,在返回给用户使用的内存区间头和尾分别多申请一个页,一个页PAGE_SIZE当前为0x1000,这两个页分别通过mprotect接口设置权限为PROT_NONE(无可读可写权限),可以有效防止内存越界读写问题:越界读写数据时由于无读写权限而导致用户程序异常,根据异常调用栈信息可找到相应的代码逻辑。
**图6** malloc通过mmap机制申请内存的内存布局 **图6** malloc通过mmap机制申请内存的内存布局
...@@ -119,7 +119,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、 ...@@ -119,7 +119,7 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
代码功能:显式调用调测模块的相关接口对用户代码进行内存校验。 代码功能:显式调用调测模块的相关接口对用户代码进行内存校验。
``` ```c
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
...@@ -127,7 +127,8 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、 ...@@ -127,7 +127,8 @@ Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、
#define MALLOC_LEAK_SIZE 0x300 #define MALLOC_LEAK_SIZE 0x300
void func(void) { void func(void)
{
char *ptr = malloc(MALLOC_LEAK_SIZE); char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE); memset(ptr, '3', MALLOC_LEAK_SIZE);
} }
...@@ -293,14 +294,15 @@ kill -37 <pid> # 检查堆内存头节点是否完整 ...@@ -293,14 +294,15 @@ kill -37 <pid> # 检查堆内存头节点是否完整
代码功能:构造内存问题利用命令行方式进行内存调测。 代码功能:构造内存问题利用命令行方式进行内存调测。
``` ```c
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#define MALLOC_LEAK_SIZE 0x300 #define MALLOC_LEAK_SIZE 0x300
void func(void) { void func(void)
{
char *ptr = malloc(MALLOC_LEAK_SIZE); char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE); memset(ptr, '3', MALLOC_LEAK_SIZE);
} }
...@@ -504,7 +506,7 @@ Now using addr2line ... ...@@ -504,7 +506,7 @@ Now using addr2line ...
### UAF(Use after free) ### UAF(Use after free)
- 申请小块内存(不大于0x1c000字节 - 申请小块内存(不大于0x1c000 bytes
free之后: free之后:
读操作:读取free之后的内存大概率是魔术数字(0xFEFEFEFE) 读操作:读取free之后的内存大概率是魔术数字(0xFEFEFEFE)
...@@ -515,7 +517,7 @@ Now using addr2line ... ...@@ -515,7 +517,7 @@ Now using addr2line ...
写操作:无法校验。 写操作:无法校验。
- 申请大块内存(大于0x1c000) - 申请大块内存(大于0x1c000 bytes
堆内存由malloc通过调用mmap接口申请,free之后若仍访问该内存,则用户程序异常(该内存区间已被unmap)。 堆内存由malloc通过调用mmap接口申请,free之后若仍访问该内存,则用户程序异常(该内存区间已被unmap)。
...@@ -526,7 +528,8 @@ Double free时,用户程序将会异常退出。 ...@@ -526,7 +528,8 @@ Double free时,用户程序将会异常退出。
### 堆内存节点被踩 ### 堆内存节点被踩
- 申请小块内存(不大于0x1c000) - 申请小块内存(不大于0x1c000 bytes)
堆内存节点被踩时,用户程序将会异常退出,并输出破坏被踩节点的可能的堆内存申请调用栈,对于野指针踩内存情况无法校验出来。例如用户程序mem_check中存在堆内存越界踩的情况,利用命令行方式可以获得踩内存的可能的具体位置。 堆内存节点被踩时,用户程序将会异常退出,并输出破坏被踩节点的可能的堆内存申请调用栈,对于野指针踩内存情况无法校验出来。例如用户程序mem_check中存在堆内存越界踩的情况,利用命令行方式可以获得踩内存的可能的具体位置。
...@@ -541,7 +544,7 @@ Double free时,用户程序将会异常退出。 ...@@ -541,7 +544,7 @@ Double free时,用户程序将会异常退出。
可以通过调用栈解析脚本对调用栈信息进行解析。 可以通过调用栈解析脚本对调用栈信息进行解析。
- 申请大块内存(大于0x1c000) - 申请大块内存(大于0x1c000 bytes
堆内存由malloc通过mmap接口申请,申请得到的堆内存块前后各置一个size为PAGE_SIZE大小的区间,设置无读写权限,读写操作会触发用户程序异常。 堆内存由malloc通过mmap接口申请,申请得到的堆内存块前后各置一个size为PAGE_SIZE大小的区间,设置无读写权限,读写操作会触发用户程序异常。
...@@ -3,7 +3,7 @@ ...@@ -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模块提供下面几种功能: OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能:
...@@ -20,7 +20,7 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能: ...@@ -20,7 +20,7 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能:
LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。 LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。
- 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头点区的影子内存设置为“不可读写”状态。 - 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头点区的影子内存设置为“不可读写”状态。
- 内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。 - 内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。
...@@ -105,14 +105,15 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信 ...@@ -105,14 +105,15 @@ OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信
2. 构造内存溢出错误和释放后使用错误。 2. 构造内存溢出错误和释放后使用错误。
3. 添加-fsanitize=kernel-address后编译执行,观察输出结果 3. 添加-fsanitize=kernel-address后编译执行,观察输出结果
#### 内核态示例代码 #### 内核态示例代码
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。
实例代码如下: 实例代码如下:
``` ```c
#define PAGE_SIZE (0x1000U) #define PAGE_SIZE (0x1000U)
#define INDEX_MAX 20 #define INDEX_MAX 20
UINT32 g_lmsTestTaskId; UINT32 g_lmsTestTaskId;
...@@ -138,7 +139,7 @@ static VOID LmsTestUseAfterFree(VOID) ...@@ -138,7 +139,7 @@ static VOID LmsTestUseAfterFree(VOID)
PRINTK("\n######%s start ######\n", __FUNCTION__); PRINTK("\n######%s start ######\n", __FUNCTION__);
UINT32 i; UINT32 i;
CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX); 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("str[%2d]=0x%2x ", 0, str[0]); /* trigger use after free at str[0] */
PRINTK("\n######%s stop ######\n", __FUNCTION__); PRINTK("\n######%s stop ######\n", __FUNCTION__);
} }
...@@ -148,18 +149,19 @@ VOID LmsTestCaseTask(VOID) ...@@ -148,18 +149,19 @@ VOID LmsTestCaseTask(VOID)
LmsTestOsmallocOverflow(); LmsTestOsmallocOverflow();
LmsTestUseAfterFree(); LmsTestUseAfterFree();
} }
UINT32 Example_Lms_test(VOID){ UINT32 Example_Lms_test(VOID)
{
UINT32 ret; UINT32 ret;
TSK_INIT_PARAM_S lmsTestTask; TSK_INIT_PARAM_S lmsTestTask;
/* 创建用于lms测试的任务 */ /* 创建用于lms测试的任务 */
memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S)); memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S));
lmsTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LmsTestCaseTask; lmsTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LmsTestCaseTask;
lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */ lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */
lmsTestTask.uwStackSize = 0x800; lmsTestTask.uwStackSize = 0x800; // 0x800: lms test task stack size
lmsTestTask.usTaskPrio = 5; lmsTestTask.usTaskPrio = 5; // 5: lms test task priority
lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED; lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED;
ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask); ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask);
if(ret != LOS_OK){ if (ret != LOS_OK) {
PRINT_ERR("LmsTestTask create failed .\n"); PRINT_ERR("LmsTestTask create failed .\n");
return LOS_NOK; return LOS_NOK;
} }
...@@ -257,7 +259,7 @@ str[ 0]=0x 0 ...@@ -257,7 +259,7 @@ 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)
``` ```
...@@ -308,14 +310,14 @@ if ("$ohos_build_compiler_specified" == "gcc") { ...@@ -308,14 +310,14 @@ if ("$ohos_build_compiler_specified" == "gcc") {
1. 构造内存溢出错误和释放后使用错误。 1. 构造内存溢出错误和释放后使用错误。
2. 添加对应编译选项后,重新编译执行 2. 添加对应编译选项后,重新编译执行
#### 用户态示例代码 #### 用户态示例代码
实例代码如下: 实例代码如下:
``` ```c
static void BufWriteTest(void *buf, int start, int end) static void BufWriteTest(void *buf, int start, int end)
{ {
for (int i = start; i <= end; i++) { for (int i = start; i <= end; i++) {
...@@ -332,7 +334,7 @@ static void BufReadTest(void *buf, int start, int end) ...@@ -332,7 +334,7 @@ static void BufReadTest(void *buf, int start, int end)
static void LmsMallocTest(void) static void LmsMallocTest(void)
{ {
printf("\n-------- LmsMallocTest Start --------\n"); printf("\n-------- LmsMallocTest Start --------\n");
char *buf = (char *)malloc(16); char *buf = (char *)malloc(16); // 16: buffer size for test
BufReadTest(buf, -1, 16); BufReadTest(buf, -1, 16);
free(buf); free(buf);
printf("\n-------- LmsMallocTest End --------\n"); printf("\n-------- LmsMallocTest End --------\n");
...@@ -340,7 +342,7 @@ static void LmsMallocTest(void) ...@@ -340,7 +342,7 @@ static void LmsMallocTest(void)
static void LmsFreeTest(void) static void LmsFreeTest(void)
{ {
printf("\n-------- LmsFreeTest Start --------\n"); printf("\n-------- LmsFreeTest Start --------\n");
char *buf = (char *)malloc(16); char *buf = (char *)malloc(16); // 16: buffer size for test
free(buf); free(buf);
BufReadTest(buf, 1, 1); BufReadTest(buf, 1, 1);
free(buf); free(buf);
...@@ -349,7 +351,7 @@ static void LmsFreeTest(void) ...@@ -349,7 +351,7 @@ static void LmsFreeTest(void)
int main(int argc, char * const * argv) int main(int argc, char * const * argv)
{ {
printf("\n############### Lms Test start ###############\n"); printf("\n############### Lms Test start ###############\n");
char *tmp = (char *)malloc(5000); char *tmp = (char *)malloc(5000); // 5000: temp buffer size
LmsMallocTest(); LmsMallocTest();
LmsFreeTest(); LmsFreeTest();
printf("\n############### Lms Test End ###############\n"); printf("\n############### Lms Test End ###############\n");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册