Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
Docs
提交
9c57ecf5
D
Docs
项目概览
OpenHarmony
/
Docs
大约 1 年 前同步成功
通知
159
Star
292
Fork
28
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
Docs
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
9c57ecf5
编写于
12月 01, 2021
作者:
O
openharmony_ci
提交者:
Gitee
12月 01, 2021
浏览文件
操作
浏览文件
下载
差异文件
!901 feat: 新增perf/lms 文档
Merge pull request !901 from LiteOS/lms
上级
8a65d80f
3ad1dd47
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
1279 addition
and
0 deletion
+1279
-0
zh-cn/device-dev/kernel/Readme-CN.md
zh-cn/device-dev/kernel/Readme-CN.md
+3
-0
zh-cn/device-dev/kernel/kernel-mini-debug-lms.md
zh-cn/device-dev/kernel/kernel-mini-debug-lms.md
+293
-0
zh-cn/device-dev/kernel/kernel-small-debug-lms.md
zh-cn/device-dev/kernel/kernel-small-debug-lms.md
+477
-0
zh-cn/device-dev/kernel/kernel-small-debug-perf.md
zh-cn/device-dev/kernel/kernel-small-debug-perf.md
+506
-0
未找到文件。
zh-cn/device-dev/kernel/Readme-CN.md
浏览文件 @
9c57ecf5
...
...
@@ -51,6 +51,7 @@
-
[
踩内存检测
](
kernel-mini-memory-debug-cet.md
)
-
[
异常调测
](
kernel-mini-memory-exception.md
)
-
[
Trace调测
](
kernel-mini-memory-trace.md
)
-
[
LMS调测
](
kernel-mini-debug-lms.md
)
-
[
附录
](
kernel-mini-app.md
)
-
[
内核编码规范
](
kernel-mini-appx-code.md
)
-
[
基本数据结构
](
kernel-mini-appx-data.md
)
...
...
@@ -165,6 +166,8 @@
-
[
魔法键使用方法
](
kernel-small-debug-shell-magickey.md
)
-
[
用户态异常信息说明
](
kernel-small-debug-shell-error.md
)
-
[
Trace调测
](
kernel-small-debug-trace.md
)
-
[
Perf调测
](
kernel-small-debug-perf.md
)
-
[
LMS调测
](
kernel-small-debug-lms.md
)
-
[
进程调测
](
kernel-small-debug-process.md
)
-
[
CPU占用率
](
kernel-small-debug-process-cpu.md
)
-
[
内存调测
](
kernel-small-debug-memory.md
)
...
...
zh-cn/device-dev/kernel/kernel-mini-debug-lms.md
0 → 100644
浏览文件 @
9c57ecf5
# LMS调测
-
[
基本概念
](
#section1
)
-
[
运行机制
](
#section2
)
-
[
接口说明
](
#section3
)
-
[
开发指导
](
#section4
)
- [开发流程](#section4.1.1)
- [编程实例](#section4.1.2)
- [实例代码](#section4.1.3)
- [结果验证](#section4.1.4)
## 基本概念 <a name = "section1"></a>
LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double Free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
## 运行机制<a name = "section2"></a>
LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。
-
内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头结点区的影子内存设置为“不可读写”状态。
-
内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。
-
编译代码时,会在代码中的读写指令前插入检测函数,对地址的合法性进行检验。主要是检测访问内存的影子内存的状态值,若检测到影子内存为不可读写,则会报溢出错误;若检测到影子内存为已释放,则会报释放后使用错误。
-
在内存释放时,会检测被释放地址的影子内存状态值,若检测到影子内存非可读写,则会报重复释放错误。
## 接口说明<a name = "section3"></a>
OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能,接口详细信息可以查看
[
API
](
https://gitee.com/openharmony/kernel_liteos_m/blob/master/components/lms/los_lms.h
)
参考。
1.
支持多内存池检测;
2.
支持LOS_MemAlloc、LOS_MemAllocAlign、LOS_MemRealloc申请出的内存检测;
3.
支持安全函数的访问检测(默认开启);
4.
支持libc 高频函数的访问检测,包括:memset、memcpy、memmove、strcat、strcpy、strncat、strncpy。
5.
可动态设置的功能如下:
**表 1**
LMS模块接口说明
<a
name=
"lmsApi"
></a>
<table><thead
align=
"left"
><tr
id=
"row0"
><th
class=
"cellrowborder"
valign=
"top"
width=
"30%"
id=
"mcps1.2.4.1.1"
><p
id=
"p0"
><a
name=
"p0"
></a><a
name=
"p0"
></a>
功能分类
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"28%"
id=
"mcps1.2.4.1.2"
><p
id=
"p1"
><a
name=
"p1"
></a><a
name=
"p1"
></a>
接口名
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"57.34573457345735%"
id=
"mcps1.2.4.1.3"
><p
id=
"p2"
><a
name=
"p2"
></a><a
name=
"p2"
></a>
描述
</p>
</th>
<tr
id=
"row6"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p15"
><a
name=
"p15"
></a><a
name=
"p15"
></a>
添加指定内存池被检测
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p16"
><a
name=
"p16"
></a><a
name=
"p16"
></a>
LOS_LmsCheckPoolAdd
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p17"
><a
name=
"p17"
></a><a
name=
"p17"
></a>
将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
删除指定内存池不被检测
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsCheckPoolDel
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
不检测指定内存池地址范围内的合法性校验。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
使能指定内存段锁保护
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsAddrProtect
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
为某段内存地址上锁,设置为不可读写,一旦访问则报错。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
去能指定内存段锁保护
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsAddrDisableProtect
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
为某段内存地址解锁,设置为可读写。
</p>
</td>
</tr>
</tbody>
</table>
## 开发指导<a name = "section4"></a>
### 开发流程<a name = "section4.1.1"></a>
开启LMS调测的典型流程如下:
1.
配置LMS模块相关宏。
配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel->Enable Lite Memory Sanitizer"中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| ------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ |
| LOSCFG_KERNEL_LMS | Enable Lms Feature | Lms模块的裁剪开关 | YES/NO |
| LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms check pool max num | LMS支持的检测内存池最大个数 | INT |
| LOSCFG_LMS_LOAD_CHECK | Enable lms read check | LMS内存读检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_STORE_CHECK | Enable lms write check | LMS内存写检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_CHECK_STRICT | Enable lms strict check, byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO |
2.
在被检测模块的编译脚本中,增加LMS检测编译选项-fsanitize=kernel-address。
3.
为避免编译器优化,增加-O0编译选项。
4.
gcc与clang编译选项存在差异,参照如下示例:
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
]
} else {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
"-mllvm",
"-asan-instrumentation-with-call-threshold=0",
"-mllvm",
"-asan-stack=0",
"-mllvm",
"-asan-globals=0",
]
}
```
5.
重新编译,查看串口输出。如果检测到内存问题,会输出检测结果。
### 编程实例<a name = "section4.1.2"></a>
本实例实现如下功能:
1.
创建一个用于Lms测试的任务。
2.
构造内存溢出错误和释放后使用错误。
3.
添加-fsanitize=kernel-address后编译执行,观察输出结果
### 示例代码<a name = "section4.1.3"></a>
实例代码如下:
```
C
#define PAGE_SIZE (0x1000U)
#define INDEX_MAX 20
UINT32 g_lmsTestTaskId;
char g_testLmsPool[2 * PAGE_SIZE];
STATIC VOID testPoolInit(void)
{
UINT32 ret = LOS_MemInit(g_testLmsPool, 2 * PAGE_SIZE);
if (ret != 0) {
PRINT_ERR("%s failed, ret = 0x%x\n", __FUNCTION__, ret);
return;
}
}
static VOID LmsTestOsmallocOverflow(VOID)
{
PRINTK("\n######%s start ######\n", __FUNCTION__);
UINT32 i;
CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX);
PRINTK("str[%2d]=0x%2x ", INDEX_MAX, str[INDEX_MAX]); /* trigger heap overflow at str[INDEX_MAX] */
PRINTK("\n######%s stop ######\n", __FUNCTION__);
}
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);
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));
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;
}
return LOS_OK;
}
```
### 结果验证<a name = "section4.1.4"></a>
输出结果如下:
```
c
######LmsTestOsmallocOverflow start ######
[
ERR
]
*****
Kernel
Address
Sanitizer
Error
Detected
Start
*****
[
ERR
]
Heap
buffer
overflow
error
detected
[
ERR
]
Illegal
READ
address
at
:
[
0x4157a3c8
]
[
ERR
]
Shadow
memory
address
:
[
0x4157be3c
:
4
]
Shadow
memory
value
:
[
2
]
OsBackTrace
fp
=
0x402c0f88
runTask
->
taskName
=
LmsTestCaseTask
runTask
->
taskID
=
2
*******
backtrace
begin
*******
traceback
fp
fixed
,
trace
using
fp
=
0x402c0fd0
traceback
0
--
lr
=
0x400655a4
fp
=
0x402c0ff8
traceback
1
--
lr
=
0x40065754
fp
=
0x402c1010
traceback
2
--
lr
=
0x40044bd0
fp
=
0x402c1038
traceback
3
--
lr
=
0x40004e14
fp
=
0xcacacaca
[
LMS
]
Dump
info
around
address
[
0x4157a3c8
]
:
[
0x4157a3a0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3a
|
0
]
:
1
1
[
0x4157a3a8
]
:
ba
dc
cd
ab
00
00
00
00
|
[
0x4157be3a
|
4
]
:
2
2
[
0x4157a3b0
]
:
20
00
00
80
00
00
00
00
|
[
0x4157be3b
|
0
]
:
2
0
[
0x4157a3b8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3b
|
4
]
:
0
0
[
0x4157a3c0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3c
|
0
]
:
0
0
[
0x4157a3c8
]
:
[
ba
]
dc
cd
ab
a8
a3
57
41
|
[
0x4157be3c
|
4
]
:
[
2
]
2
[
0x4157a3d0
]
:
2
c
1
a
00
00
00
00
00
00
|
[
0x4157be3d
|
0
]
:
2
3
[
0x4157a3d8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3d
|
4
]
:
3
3
[
0x4157a3e0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
0
]
:
3
3
[
0x4157a3e8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
4
]
:
3
3
[
0x4157a3f0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3f
|
0
]
:
3
3
[
ERR
]
*****
Kernel
Address
Sanitizer
Error
Detected
End
*****
str
[
20
]
=
0xffffffba
######LmsTestOsmallocOverflow stop ######
###### LmsTestUseAfterFree start ######
[
ERR
]
*****
Kernel
Address
Sanitizer
Error
Detected
Start
*****
[
ERR
]
Use
after
free
error
detected
[
ERR
]
Illegal
READ
address
at
:
[
0x4157a3d4
]
[
ERR
]
Shadow
memory
address
:
[
0x4157be3d
:
2
]
Shadow
memory
value
:
[
3
]
OsBackTrace
fp
=
0x402c0f90
runTask
->
taskName
=
LmsTestCaseTask
runTask
->
taskID
=
2
*******
backtrace
begin
*******
traceback
fp
fixed
,
trace
using
fp
=
0x402c0fd8
traceback
0
--
lr
=
0x40065680
fp
=
0x402c0ff8
traceback
1
--
lr
=
0x40065758
fp
=
0x402c1010
traceback
2
--
lr
=
0x40044bd0
fp
=
0x402c1038
traceback
3
--
lr
=
0x40004e14
fp
=
0xcacacaca
[
LMS
]
Dump
info
around
address
[
0x4157a3d4
]
:
[
0x4157a3a8
]
:
ba
dc
cd
ab
00
00
00
00
|
[
0x4157be3a
|
4
]
:
2
2
[
0x4157a3b0
]
:
20
00
00
80
00
00
00
00
|
[
0x4157be3b
|
0
]
:
2
0
[
0x4157a3b8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3b
|
4
]
:
0
0
[
0x4157a3c0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3c
|
0
]
:
0
0
[
0x4157a3c8
]
:
ba
dc
cd
ab
a8
a3
57
41
|
[
0x4157be3c
|
4
]
:
2
2
[
0x4157a3d0
]
:
2
c
1
a
00
00
[
00
]
00
00
00
|
[
0x4157be3d
|
0
]
:
2
[
3
]
[
0x4157a3d8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3d
|
4
]
:
3
3
[
0x4157a3e0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
0
]
:
3
3
[
0x4157a3e8
]
:
ba
dc
cd
ab
c8
a3
57
41
|
[
0x4157be3e
|
4
]
:
2
2
[
0x4157a3f0
]
:
0
c
1
a
00
00
00
00
00
00
|
[
0x4157be3f
|
0
]
:
2
3
[
0x4157a3f8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3f
|
4
]
:
3
3
[
ERR
]
*****
Kernel
Address
Sanitizer
Error
Detected
End
*****
str
[
0
]
=
0
x
0
######LmsTestUseAfterFree stop ######
```
输出的关键信息包括:
-
错误类型:
-
Heap buffer overflow堆内存越界
-
Use after free 释放后使用
-
错误操作:
-
Illegal Read非法读
-
Illegal Write非法写
-
Illegal Double free重复释放
-
上下文:
-
当前任务信息(taskName, taskId)
-
回溯栈(backtrace)
-
出错地址的内存信息:
-
内存的值、及对应影子内存的值
-
内存地址:内存值| [影子内存地址 | 影子内存字节内偏移]:影子内存值
-
影子内存值:0(可读可写)、3(已释放)、2(红区)、1(填充值)。
zh-cn/device-dev/kernel/kernel-small-debug-lms.md
0 → 100644
浏览文件 @
9c57ecf5
# LMS调测
-
[
基本概念
](
#section1
)
-
[
运行机制
](
#section2
)
-
[
接口说明
](
#section3
)
- [内核态](#section3.1)
- [用户态](#section3.2)
-
[
开发指导
](
#section4
)
- [内核态开发流程](#section4.1.1)
- [内核态编程实例](#section4.1.2)
- [内核态实例代码](#section4.1.3)
- [内核态结果验证](#section4.1.4)
- [用户态开发流程](#section4.2.1)
- [用户态编程实例](#section4.2.2)
- [用户态实例代码](#section4.2.3)
- [用户态结果验证](#section4.2.4)
## 基本概念 <a name = "section1"></a>
LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double Free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
## 运行机制<a name = "section2"></a>
LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。
-
内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头结点区的影子内存设置为“不可读写”状态。
-
内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。
-
编译代码时,会在代码中的读写指令前插入检测函数,对地址的合法性进行检验。主要是检测访问内存的影子内存的状态值,若检测到影子内存为不可读写,则会报溢出错误;若检测到影子内存为已释放,则会报释放后使用错误。
-
在内存释放时,会检测被释放地址的影子内存状态值,若检测到影子内存非可读写,则会报重复释放错误。
## 接口说明<a name = "section3"></a>
### 内核态<a name = "section3.1"></a>
OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信息可以查看
[
API
](
https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_lms.h
)
参考。
1.
支持多内存池检测;
2.
支持LOS_MemAlloc、LOS_MemAllocAlign、LOS_MemRealloc申请出的内存检测;
3.
支持安全函数的访问检测(默认开启);
4.
支持libc 高频函数的访问检测,包括:memset、memcpy、memmove、strcat、strcpy、strncat、strncpy。
5.
可动态设置的功能如下:
**表 1**
LMS模块接口说明
<a
name=
"lmsApi"
></a>
<table><thead
align=
"left"
><tr
id=
"row0"
><th
class=
"cellrowborder"
valign=
"top"
width=
"30%"
id=
"mcps1.2.4.1.1"
><p
id=
"p0"
><a
name=
"p0"
></a><a
name=
"p0"
></a>
功能分类
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"28%"
id=
"mcps1.2.4.1.2"
><p
id=
"p1"
><a
name=
"p1"
></a><a
name=
"p1"
></a>
接口名
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"57.34573457345735%"
id=
"mcps1.2.4.1.3"
><p
id=
"p2"
><a
name=
"p2"
></a><a
name=
"p2"
></a>
描述
</p>
</th>
<tr
id=
"row6"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p15"
><a
name=
"p15"
></a><a
name=
"p15"
></a>
添加指定内存池被检测
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p16"
><a
name=
"p16"
></a><a
name=
"p16"
></a>
LOS_LmsCheckPoolAdd
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p17"
><a
name=
"p17"
></a><a
name=
"p17"
></a>
将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
删除指定内存池不被检测
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsCheckPoolDel
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
不检测指定内存池地址范围内的合法性校验。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
使能指定内存段锁保护
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsAddrProtect
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
为某段内存地址上锁,设置为不可读写,一旦访问则报错。
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
去能指定内存段锁保护
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_LmsAddrDisableProtect
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
为某段内存地址解锁,设置为可读写。
</p>
</td>
</tr>
</tbody>
</table>
### 用户态<a name = "section3.2"></a>
用户态仅提供LMS检测库,不提供对外接口。
## 开发指导<a name = "section4"></a>
### 内核态开发流程<a name = "section4.1.1"></a>
开启LMS调测的典型流程如下:
1.
配置LMS模块相关宏。
配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel->Enable Lite Memory Sanitizer"中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| ------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ |
| LOSCFG_KERNEL_LMS | Enable Lms Feature | Lms模块的裁剪开关 | YES/NO |
| LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms check pool max num | LMS支持的检测内存池最大个数 | INT |
| LOSCFG_LMS_LOAD_CHECK | Enable lms read check | LMS内存读检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_STORE_CHECK | Enable lms write check | LMS内存写检测的裁剪开关 | YES/NO |
| LOSCFG_LMS_CHECK_STRICT | Enable lms strict check, byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO |
2.
在被检测模块的编译脚本中,增加LMS检测编译选项-fsanitize=kernel-address。
3.
为避免编译器优化,增加-O0编译选项。
4.
gcc与clang编译选项存在差异,参照如下示例:
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
]
} else {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
"-mllvm",
"-asan-instrumentation-with-call-threshold=0",
"-mllvm",
"-asan-stack=0",
"-mllvm",
"-asan-globals=0",
]
}
```
5.
重新编译,查看串口输出。如果检测到内存问题,会输出检测结果。
### 内核态编程实例<a name = "section4.1.2"></a>
本实例实现如下功能:
1.
创建一个用于Lms测试的任务。
2.
构造内存溢出错误和释放后使用错误。
3.
添加-fsanitize=kernel-address后编译执行,观察输出结果
### 内核态示例代码<a name = "section4.1.3"></a>
实例代码如下:
```
C
#define PAGE_SIZE (0x1000U)
#define INDEX_MAX 20
UINT32 g_lmsTestTaskId;
char g_testLmsPool[2 * PAGE_SIZE];
STATIC VOID testPoolInit(void)
{
UINT32 ret = LOS_MemInit(g_testLmsPool, 2 * PAGE_SIZE);
if (ret != 0) {
PRINT_ERR("%s failed, ret = 0x%x\n", __FUNCTION__, ret);
return;
}
}
static VOID LmsTestOsmallocOverflow(VOID)
{
PRINTK("\n######%s start ######\n", __FUNCTION__);
UINT32 i;
CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX);
PRINTK("str[%2d]=0x%2x ", INDEX_MAX, str[INDEX_MAX]); /* trigger heap overflow at str[INDEX_MAX] */
PRINTK("\n######%s stop ######\n", __FUNCTION__);
}
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);
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));
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;
}
return LOS_OK;
}
LOS_MODULE_INIT(Example_Lms_test, LOS_INIT_LEVEL_KMOD_EXTENDED);
```
### 内核态结果验证<a name = "section4.1.4"></a>
输出结果如下:
```
c
######LmsTestOsmallocOverflow start ######
[
ERR
][
KProcess
:
LmsTestCaseTask
]
*****
Kernel
Address
Sanitizer
Error
Detected
Start
*****
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Heap
buffer
overflow
error
detected
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Illegal
READ
address
at
:
[
0x4157a3c8
]
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Shadow
memory
address
:
[
0x4157be3c
:
4
]
Shadow
memory
value
:
[
2
]
OsBackTrace
fp
=
0x402c0f88
runTask
->
taskName
=
LmsTestCaseTask
runTask
->
taskID
=
2
*******
backtrace
begin
*******
traceback
fp
fixed
,
trace
using
fp
=
0x402c0fd0
traceback
0
--
lr
=
0x400655a4
fp
=
0x402c0ff8
traceback
1
--
lr
=
0x40065754
fp
=
0x402c1010
traceback
2
--
lr
=
0x40044bd0
fp
=
0x402c1038
traceback
3
--
lr
=
0x40004e14
fp
=
0xcacacaca
[
LMS
]
Dump
info
around
address
[
0x4157a3c8
]
:
[
0x4157a3a0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3a
|
0
]
:
1
1
[
0x4157a3a8
]
:
ba
dc
cd
ab
00
00
00
00
|
[
0x4157be3a
|
4
]
:
2
2
[
0x4157a3b0
]
:
20
00
00
80
00
00
00
00
|
[
0x4157be3b
|
0
]
:
2
0
[
0x4157a3b8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3b
|
4
]
:
0
0
[
0x4157a3c0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3c
|
0
]
:
0
0
[
0x4157a3c8
]
:
[
ba
]
dc
cd
ab
a8
a3
57
41
|
[
0x4157be3c
|
4
]
:
[
2
]
2
[
0x4157a3d0
]
:
2
c
1
a
00
00
00
00
00
00
|
[
0x4157be3d
|
0
]
:
2
3
[
0x4157a3d8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3d
|
4
]
:
3
3
[
0x4157a3e0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
0
]
:
3
3
[
0x4157a3e8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
4
]
:
3
3
[
0x4157a3f0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3f
|
0
]
:
3
3
[
ERR
][
KProcess
:
LmsTestCaseTask
]
*****
Kernel
Address
Sanitizer
Error
Detected
End
*****
str
[
20
]
=
0xffffffba
######LmsTestOsmallocOverflow stop ######
###### LmsTestUseAfterFree start ######
[
ERR
][
KProcess
:
LmsTestCaseTask
]
*****
Kernel
Address
Sanitizer
Error
Detected
Start
*****
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Use
after
free
error
detected
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Illegal
READ
address
at
:
[
0x4157a3d4
]
[
ERR
][
KProcess
:
LmsTestCaseTask
]
Shadow
memory
address
:
[
0x4157be3d
:
2
]
Shadow
memory
value
:
[
3
]
OsBackTrace
fp
=
0x402c0f90
runTask
->
taskName
=
LmsTestCaseTask
runTask
->
taskID
=
2
*******
backtrace
begin
*******
traceback
fp
fixed
,
trace
using
fp
=
0x402c0fd8
traceback
0
--
lr
=
0x40065680
fp
=
0x402c0ff8
traceback
1
--
lr
=
0x40065758
fp
=
0x402c1010
traceback
2
--
lr
=
0x40044bd0
fp
=
0x402c1038
traceback
3
--
lr
=
0x40004e14
fp
=
0xcacacaca
[
LMS
]
Dump
info
around
address
[
0x4157a3d4
]
:
[
0x4157a3a8
]
:
ba
dc
cd
ab
00
00
00
00
|
[
0x4157be3a
|
4
]
:
2
2
[
0x4157a3b0
]
:
20
00
00
80
00
00
00
00
|
[
0x4157be3b
|
0
]
:
2
0
[
0x4157a3b8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3b
|
4
]
:
0
0
[
0x4157a3c0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3c
|
0
]
:
0
0
[
0x4157a3c8
]
:
ba
dc
cd
ab
a8
a3
57
41
|
[
0x4157be3c
|
4
]
:
2
2
[
0x4157a3d0
]
:
2
c
1
a
00
00
[
00
]
00
00
00
|
[
0x4157be3d
|
0
]
:
2
[
3
]
[
0x4157a3d8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3d
|
4
]
:
3
3
[
0x4157a3e0
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3e
|
0
]
:
3
3
[
0x4157a3e8
]
:
ba
dc
cd
ab
c8
a3
57
41
|
[
0x4157be3e
|
4
]
:
2
2
[
0x4157a3f0
]
:
0
c
1
a
00
00
00
00
00
00
|
[
0x4157be3f
|
0
]
:
2
3
[
0x4157a3f8
]
:
00
00
00
00
00
00
00
00
|
[
0x4157be3f
|
4
]
:
3
3
[
ERR
][
KProcess
:
LmsTestCaseTask
]
*****
Kernel
Address
Sanitizer
Error
Detected
End
*****
str
[
0
]
=
0
x
0
######LmsTestUseAfterFree stop ######
```
输出的关键信息包括:
-
错误类型:
-
Heap buffer overflow堆内存越界
-
Use after free 释放后使用
-
错误操作:
-
Illegal Read非法读
-
Illegal Write非法写
-
Illegal Double free重复释放
-
上下文:
-
当前任务信息(taskName, taskId)
-
回溯栈(backtrace)
-
出错地址的内存信息:
-
内存的值、及对应影子内存的值
-
内存地址:内存值| [影子内存地址 | 影子内存字节内偏移]:影子内存值
-
影子内存值:0(可读可写)、3(已释放)、2(红区)、1(填充值)。
### 用户态开发流程<a name = "section4.2.1"></a>
在待检测的app编译脚本中,添加如下参数即可, 完整示例可参见/kernel/liteos_a/apps/lms/BUILD.gn
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
"-funwind-tables",
"-fasynchronous-unwind-tables",
]
} else {
cflags_c = [
"-O0",
"-fsanitize=kernel-address",
"-mllvm",
"-asan-instrumentation-with-call-threshold=0",
"-mllvm",
"-asan-stack=0",
"-mllvm",
"-asan-globals=0",
"-funwind-tables",
"-fasynchronous-unwind-tables",
]
}
ldflags = [
"-rdynamic",
"-lunwind",
"-lusrlms",
"-Wl,--wrap=realloc",
"-Wl,--wrap=calloc",
"-Wl,--wrap=malloc",
"-Wl,--wrap=free",
"-Wl,--wrap=valloc",
"-Wl,--wrap=aligned_alloc",
"-Wl,--wrap=memset",
"-Wl,--wrap=memcpy",
"-Wl,--wrap=memmove",
"-Wl,--wrap=strcpy",
"-Wl,--wrap=strcat",
]
deps = [ "//kernel/liteos_a/kernel/extended/lms/usr:usrlmslib" ]
```
### 用户态编程实例<a name = "section4.2.2"></a>
本实例实现如下功能:
1. 构造内存溢出错误和释放后使用错误。
2. 添加对应编译选项后,重新编译执行
### 用户态示例代码<a name = "section4.2.3"></a>
实例代码如下
```
c
static void BufWriteTest(void
*
buf, int start, int end)
{
for (int i = start; i <= end; i++) {
((char
*
)buf)[i] = 'a';
}
}
static void BufReadTest(void
*
buf, int start, int end)
{
char tmp;
for (int i = start; i <= end; i++) {
tmp = ((char
*
)buf)[i];
}
}
static void LmsMallocTest(void)
{
printf("
\n
-------- LmsMallocTest Start --------
\n
");
char
*buf = (char *
)malloc(16);
BufReadTest(buf, -1, 16);
free(buf);
printf("
\n
-------- LmsMallocTest End --------
\n
");
}
static void LmsFreeTest(void)
{
printf("
\n
-------- LmsFreeTest Start --------
\n
");
char
*buf = (char *
)malloc(16);
free(buf);
BufReadTest(buf, 1, 1);
free(buf);
printf("
\n
-------- LmsFreeTest End --------
\n
");
}
int main(int argc, char
* const *
argv)
{
printf("
\n
############### Lms Test start ###############
\n
");
char
*tmp = (char *
)malloc(5000);
LmsMallocTest();
LmsFreeTest();
printf("
\n
############### Lms Test End ###############
\n
");
}
```
### 用户态结果验证<a name = "section4.2.4"></a>
输出结果如下:
```
c
****
* Lite Memory Sanitizer Error Detected *
****
Heap buffer overflow error detected!
Illegal READ address at: [0x1f8b3edf]
Shadow memory address: [0x3d34d3ed : 6] Shadow memory value: [2]
Accessable heap addr 0
Heap red zone 2
Heap freed buffer 3
Dump info around address [0x1f8b3edf]:
[0x1f8b3eb8]: 74 55 8b 1f 74 55 8b 1f | [0x3d34d3eb | 4]: 0 0
[0x1f8b3ec0]: f8 9c 8b 1f 00 00 00 00 | [0x3d34d3ec | 0]: 0 0
[0x1f8b3ec8]: 00 00 00 00 9c fc fc fc | [0x3d34d3ec | 4]: 0 0
[0x1f8b3ed0]: 21 00 00 00 41 00 00 00 | [0x3d34d3ed | 0]: 0 0
[0x1f8b3ed8]: 60 55 8b 1f 60 55 8b [1f]| [0x3d34d3ed | 4]: 2 [2]
[0x1f8b3ee0]: 50 4e 0b 00 00 00 00 00 | [0x3d34d3ee | 0]: 0 0
[0x1f8b3ee8]: 09 00 00 00 00 00 00 00 | [0x3d34d3ee | 4]: 0 0
[0x1f8b3ef0]: 00 00 00 00 08 03 09 00 | [0x3d34d3ef | 0]: 2 2
[0x1f8b3ef8]: 00 00 00 00 00 00 00 00 | [0x3d34d3ef | 4]: 2 2
****
* Lite Memory Sanitizer Error Detected End *
****
Backtrace() returned 5 addresses
#01:
<LMS_ReportError
+0
x284
>
[0x4d6c] -> ./sample_usr_lms
#02:
<
(
null
)+0
x2004074
>
[0x4074] -> ./sample_usr_lms
#03:
<
(
null
)+0
x2003714
>
[0x3714] -> ./sample_usr_lms
#04:
<main
+0
x40
>
[0x363c] -> ./sample_usr_lms
#05:
<
(
null
)+0
x1f856f30
>
[0x56f30] -> /lib/libc.so
-------- LMS_malloc_test End --------
****
* Lite Memory Sanitizer Error Detected *
****
Use after free error detected!
Illegal Double free address at: [0x1f8b3ee0]
Shadow memory address: [0x3d34d3ee : 0] Shadow memory value: [3]
Accessable heap addr 0
Heap red zone 2
Heap freed buffer 3
Dump info around address [0x1f8b3ee0]:
[0x1f8b3ec0]: f8 9c 8b 1f 00 00 00 00 | [0x3d34d3ec | 0]: 0 0
[0x1f8b3ec8]: 00 00 00 00 fc fd fc fc | [0x3d34d3ec | 4]: 0 0
[0x1f8b3ed0]: 21 00 00 00 20 01 00 00 | [0x3d34d3ed | 0]: 0 0
[0x1f8b3ed8]: 60 55 8b 1f 60 55 8b 1f | [0x3d34d3ed | 4]: 2 2
[0x1f8b3ee0]: [20] 60 9a 1f 40 61 9a 1f | [0x3d34d3ee | 0]: [3] 3
[0x1f8b3ee8]: 60 62 9a 1f 80 63 9a 1f | [0x3d34d3ee | 4]: 3 3
[0x1f8b3ef0]: 20 40 8b 1f 20 20 8b 1f | [0x3d34d3ef | 0]: 3 3
[0x1f8b3ef8]: 00 00 00 00 00 00 00 00 | [0x3d34d3ef | 4]: 3 3
[0x1f8b3f00]: 00 00 00 00 00 00 00 00 | [0x3d34d3f0 | 0]: 3 3
****
* Lite Memory Sanitizer Error Detected End *
****
Backtrace() returned 5 addresses
#01:
<LMS_ReportError
+0
x284
>
[0x4d6c] -> ./sample_usr_lms
#02:
<LMS_free
+0
xcc
>
[0x5548] -> ./sample_usr_lms
#03:
<
(
null
)+0
x2003fc4
>
[0x3fc4] -> ./sample_usr_lms
#04:
<main
+0
x68
>
[0x3664] -> ./sample_usr_lms
#05:
<
(
null
)+0
x1f856f30
>
[0x56f30] -> /lib/libc.so
-------- LMS_free_test End --------
```
输出的Backtrace中包含地址所在的文件名,用户需查找对应文件中地址对应的代码行号。
zh-cn/device-dev/kernel/kernel-small-debug-perf.md
0 → 100644
浏览文件 @
9c57ecf5
# Perf调测
-
[
基本概念
](
#section1
)
-
[
运行机制
](
#section2
)
-
[
接口说明
](
#section3
)
- [内核态](#section3.1)
- [用户态](#section3.2)
-
[
开发指导
](
#section4
)
- [内核态开发流程](#section4.1.1)
- [内核态编程实例](#section4.1.2)
- [内核态实例代码](#section4.1.3)
- [内核态结果验证](#section4.1.4)
- [用户态开发流程](#section4.2.1)
- [用户态编程实例](#section4.2.2)
- [用户态实例代码](#section4.2.3)
- [用户态结果验证](#section4.2.4)
## 基本概念 <a name = "section1"></a>
Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样事件进行计数和上下文采集,统计出热点分布(hot spot)和热路径(hot path)。
## 运行机制<a name = "section2"></a>
基于事件采样原理,以性能事件为基础,当事件发生时,相应的事件计数器溢出发生中断,在中断处理函数中记录事件信息,包括当前的pc、当前运行的任务ID以及调用栈等信息。
Perf提供2种工作模式,计数模式和采样模式。
计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径。
## 接口说明<a name = "section3"></a>
### 内核态<a name = "section3.1"></a>
OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细信息可以查看
[
API
](
https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_perf.h
)
参考。
**表 1**
Perf模块接口说明
<a
name=
"perfApi"
></a>
<table><thead
align=
"left"
><tr
id=
"row0"
><th
class=
"cellrowborder"
valign=
"top"
width=
"30%"
id=
"mcps1.2.4.1.1"
><p
id=
"p0"
><a
name=
"p0"
></a><a
name=
"p0"
></a>
功能分类
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"28%"
id=
"mcps1.2.4.1.2"
><p
id=
"p1"
><a
name=
"p1"
></a><a
name=
"p1"
></a>
接口名
</p>
</th>
<th
class=
"cellrowborder"
valign=
"top"
width=
"57.34573457345735%"
id=
"mcps1.2.4.1.3"
><p
id=
"p2"
><a
name=
"p2"
></a><a
name=
"p2"
></a>
描述
</p>
</th>
</tr>
</thead>
<tbody><tr
id=
"row1"
><td
class=
"cellrowborder"
rowspan=
"2"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p3"
><a
name=
"p3"
></a><a
name=
"p3"
></a>
开启/停止Perf采样
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p4"
><a
name=
"p4"
></a><a
name=
"p4"
></a>
LOS_PerfStart
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p5"
><a
name=
"p5"
></a><a
name=
"p5"
></a>
开启采样
</p>
</td>
</tr>
<tr
id=
"row2"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p6"
><a
name=
"p6"
></a><a
name=
"p6"
></a>
LOS_PerfStop
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p7"
><a
name=
"p7"
></a><a
name=
"p7"
></a>
停止采样
</p>
</td>
</tr>
<tr
id=
"row6"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p15"
><a
name=
"p15"
></a><a
name=
"p15"
></a>
配置Perf采样事件
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p16"
><a
name=
"p16"
></a><a
name=
"p16"
></a>
LOS_PerfConfig
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p17"
><a
name=
"p17"
></a><a
name=
"p17"
></a>
配置采样事件的类型、周期等
</p>
</td>
</tr>
<tr
id=
"row7"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p18"
><a
name=
"p18"
></a><a
name=
"p18"
></a>
读取采样数据
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p19"
><a
name=
"p19"
></a><a
name=
"p19"
></a>
LOS_PerfDataRead
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p20"
><a
name=
"p20"
></a><a
name=
"p20"
></a>
读取采样数据到指定地址
</p>
</td>
</tr>
<tr
id=
"row8"
><td
class=
"cellrowborder"
rowspan=
"2"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p21"
><a
name=
"p21"
></a><a
name=
"p21"
></a>
注册采样数据缓冲区的钩子函数
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p22"
><a
name=
"p22"
></a><a
name=
"p22"
></a>
LOS_PerfNotifyHookReg
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.3 "
><p
id=
"p23"
><a
name=
"p23"
></a><a
name=
"p23"
></a>
注册缓冲区水线到达的处理钩子
</p>
</td>
</tr>
<tr
id=
"row9"
><td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.1 "
><p
id=
"p24"
><a
name=
"p24"
></a><a
name=
"p24"
></a>
LOS_PerfFlushHookReg
</p>
</td>
<td
class=
"cellrowborder"
valign=
"top"
headers=
"mcps1.2.4.1.2 "
><p
id=
"p25"
><a
name=
"p25"
></a><a
name=
"p25"
></a>
注册缓冲区刷cache的钩子
</p>
</td>
</tr>
</tbody>
</table>
1.
Perf采样事件的结构体为PerfConfigAttr,详细字段含义及取值详见kernel
\i
nclude
\l
os_perf.h。
2.
采样数据缓冲区为环形buffer,buffer中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。
3.
缓冲区有限,用户可通过注册水线到达的钩子进行buffer溢出提醒或buffer读操作。默认水线值为buffer总大小的1/2。
示例如下:
```
c
VOID
Example_PerfNotifyHook
(
VOID
)
{
CHAR
buf
[
LOSCFG_PERF_BUFFER_SIZE
]
=
{
0
};
UINT32
len
;
PRINT_DEBUG
(
"perf buffer reach the waterline!
\n
"
);
len
=
LOS_PerfDataRead
(
buf
,
LOSCFG_PERF_BUFFER_SIZE
);
OsPrintBuff
(
buf
,
len
);
/* print data */
}
LOS_PerfNotifyHookReg
(
Example_PerfNotifyHook
);
```
4.
若perf采样的buffer涉及到cpu 跨cache,则用户可通过注册刷cache的钩子,进行cache同步。
示例如下:
```
c
VOID
Example_PerfFlushHook
(
VOID
*
addr
,
UINT32
size
)
{
OsCacheFlush
(
addr
,
size
);
/* platform interface */
}
LOS_PerfNotifyHookReg
(
Example_PerfFlushHook
);
```
刷cache接口视具体的平台自行配置。
### 用户态<a name = "section3.2"></a>
新增perf字符设备,位于"/dev/perf",通过对设备节点的read
\w
rite
\i
octl,实现用户态perf的读写和控制:
-
read: 用户态读取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)
```
分别对应Perf启动(LOS_PerfStart)、停止(LOS_PerfStop)
具体的使用方法参见
[
用户态编程实例
](
#section4.2.2
)
## 开发指导<a name = "section4"></a>
### 内核态开发流程<a name = "section4.1.1"></a>
开启Perf调测的典型流程如下:
1.
配置Perf模块相关宏。
配置Perf控制宏LOSCFG_KERNEL_PERF,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel->Enable Perf Feature"中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| ------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ |
| LOSCFG_KERNEL_PERF | Enable Perf Feature | Perf模块的裁剪开关 | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_TICK | Time-consuming Calc Methods->By Tick | Perf计时单位为tick | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_CYCLE | Time-consuming Calc Methods->By Cpu Cycle | Perf计时单位为cycle | YES/NO |
| LOSCFG_PERF_BUFFER_SIZE | Perf Sampling Buffer Size | Perf采样buffer的大小 | INT |
| LOSCFG_PERF_HW_PMU | Enable Hardware Pmu Events for Sampling | 使能硬件PMU事件,需要目标平台支持硬件PMU | YES/NO |
| LOSCFG_PERF_TIMED_PMU | Enable Hrtimer Period Events for Sampling | 使能高精度周期事件,需要目标平台支持高精度定时器 | YES/NO |
| LOSCFG_PERF_SW_PMU | Enable Software Events for Sampling | 使能软件事件,需要开启LOSCFG_KERNEL_HOOK | INT |
2.
调用LOS_PerfConfig配置需要采样的事件。
Perf提供2种模式的配置,及3大类型的事件配置:
2种模式:计数模式(仅统计事件发生次数)、采样模式(收集上下文如任务ID、pc、backtrace等)。
3种事件类型:CPU硬件事件(cycle、branch、icache、dcache等)、高精度周期事件(cpu clock)、OS软件事件(task switch、mux pend、irq等)。
3.
在需要采样的代码起始点调用LOS_PerfStart(UINT32 sectionId), 入参sectionId标记不同的采样回话id。
4.
在需要采样的代码结束点调用LOS_PerfStop。
5.
调用输出缓冲区数据的接口LOS_PerfDataRead读取采样数据,并使用IDE工具进行解析。
### 内核态编程实例<a name = "section4.1.2"></a>
本实例实现如下功能:
1.
创建perf测试任务。
2.
配置采样事件。
3.
启动perf。
4.
执行需要统计的算法。
5.
停止perf。
6.
输出统计结果。
### 内核态示例代码<a name = "section4.1.3"></a>
前提条件:在menuconfig菜单中完成perf模块的配置。
实例代码如下:
```
C
#include "los_perf.h"
STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num)
{
UINT32 i = 0;
PRINTK("num: ");
for (i = 0; i < num; i++) {
PRINTK(" %02d", i);
}
PRINTK("\n");
PRINTK("hex: ");
for (i = 0; i < num; i++) {
PRINTK(" %02x", buf[i]);
}
PRINTK("\n");
}
STATIC VOID perfTestHwEvent(VOID)
{
UINT32 ret;
CHAR *buf = NULL;
UINT32 len;
PerfConfigAttr attr = {
.eventsCfg = {
.type = PERF_EVENT_TYPE_HW,
.events = {
[0] = {PERF_COUNT_HW_CPU_CYCLES, 0xFFFF},
[1] = {PERF_COUNT_HW_BRANCH_INSTRUCTIONS, 0xFFFFFF00},
},
.eventsNr = 2,
.predivided = 1, /* cycle counter increase every 64 cycles */
},
.taskIds = {0},
.taskIdsNr = 0,
.needSample = 0,
.sampleType = PERF_RECORD_IP | PERF_RECORD_CALLCHAIN,
};
ret = LOS_PerfConfig(&attr);
if (ret != LOS_OK) {
PRINT_ERR("perf config error %u\n", ret);
return;
}
PRINTK("------count mode------\n");
LOS_PerfStart(0);
test(); /* this is any test function*/
LOS_PerfStop();
PRINTK("--------sample mode------ \n");
attr.needSample = 1;
LOS_PerfConfig(&attr);
LOS_PerfStart(2);
test(); /* this is any test function*/
LOS_PerfStop();
buf = LOS_MemAlloc(m_aucSysMem1, LOSCFG_PERF_BUFFER_SIZE);
if (buf == NULL) {
PRINT_ERR("buffer alloc failed\n");
return;
}
/* get sample data */
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;
}
return LOS_OK;
}
LOS_MODULE_INIT(perfTestHwEvent, LOS_INIT_LEVEL_KMOD_EXTENDED);
```
### 内核态结果验证<a name = "section4.1.4"></a>
输出结果如下:
```
c
--------
count
mode
----------
[
EMG
]
[
cycles
]
eventType
:
0xff
:
5466989440
[
EMG
]
[
branches
]
eventType
:
0xc
:
602166445
-------
sample
mode
----------
[
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
...
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
00
00
00
6
b
65
72
6
e
65
6
c
00
00
01
00
00
00
cc
55
30
40
08
00
00
00
6
b
65
72
6
e
65
6
c
00
00
```
-
针对计数模式,系统在perf stop后会打印:
事件名称(cycles)、事件类型(0xff)、事件发生的次数(5466989440)。
当采样事件为硬件PMU事件时,打印的事件类型为实际的硬件事件id,非enum PmuHWId中定义的抽象类型。
-
针对采样模式,系统在perf stop后会打印采样数据的地址和长度:
dump section data, addr: (0x8000000) length: (0x5000)
用户可以通过JTAG口导出该片内存,再使用IDE线下工具解析。
或者通过LOS_PerfDataRead将数据读到指定地址,进行查看或进一步处理。示例中OsPrintBuff为测试接口,其按字节打印Read到的采样数据,num表示第几个字节,hex表示该字节中的数值。
### 用户态开发流程<a name = "section4.2.1"></a>
通过在menuconfig配置"Driver->Enable PERF DRIVER",开启Perf驱动。该配置仅在内核Enable Perf Feature后,才可在Driver的选项中可见。
1.
打开“/dev/perf”字符文件,进行读写和IOCTL操作;
2.
系统提供用户态的perf命令,该命令位于/bin目录下,cd bin 后可执行如下命令:
-
./perf start [id] 启动perf采样, id 为可选项,默认值为0
-
./perf stop 停止perf采样
-
./perf read
\<
nBytes
\>
从采样缓冲区中读取nBytes数据并打印内容
-
./perf list 罗列-e支持的具体事件
-
./perf stat/record [option]
\<
command
\>
计数/采样模式命令
option可选如下:
-
-e,配置采样事件。可使用./perf list 中罗列的同类型事件。
-
-p,配置事件采样周期。
-
-o, 指定perf采样数据结果保存的文件路径。
-
-t,任务Id过滤(白名单),只采取指定任务中的上下文。如果不指定改参数,则默认采集所有的任务。
-
-s, 配置采样的具体上下文类型,可查阅los_perf.h中定义的PerfSampleType。
-
-P, 进程Id过滤(白名单),只采取指定进程中的上下文。如果不指定改参数,则默认采集所有进程。
-
-d, 是否进行分频(事件每发生64次累计+1),该选项仅在硬件cycle事件上生效。
command 为待统计的子程序。
用户态命令行的典型使用方法如下:
./perf list 查看可使用的事件列表, 输出如下:
```
c
cycles
[
Hardware
event
]
instruction
[
Hardware
event
]
dcache
[
Hardware
event
]
dcache
-
miss
[
Hardware
event
]
icache
[
Hardware
event
]
icache
-
miss
[
Hardware
event
]
branch
[
Hardware
event
]
branch
-
miss
[
Hardware
event
]
clock
[
Timed
event
]
task
-
switch
[
Software
event
]
irq
-
in
[
Software
event
]
mem
-
alloc
[
Software
event
]
mux
-
pend
[
Software
event
]
```
./perf stat -e cycles os_dump, 输出如下:
```
c
type:
0
events
[
0
]
:
255
,
0xffff
predivided
:
0
sampleType
:
0x0
needSample
:
0
usage
os_dump
[
--
help
|
-
l
|
SERVICE
]
--
help
:
shows
this
help
-
l
:
only
list
services
,
do
not
dump
them
SERVICE
:
dumps
only
service
SERVICE
time
used
:
0
.
05
8000
(
s
)
[
cycles
]
eventType
:
0xff
[
core
0
]
:
21720647
[
cycles
]
eventType
:
0xff
[
core
1
]
:
13583830
```
./perf record -e cycles os_dump, 输出如下:
```
c
type:
0
events
[
0
]
:
255
,
0xffff
predivided
:
0
sampleType
:
0x60
needSample
:
1
usage
os_dump
[
--
help
|
-
l
|
SERVICE
]
--
help
:
shows
this
help
-
l
:
only
list
services
,
do
not
dump
them
SERVICE
:
dumps
only
service
SERVICE
dump
perf
data
,
addr
:
0x408643d8
length
:
0x5000
time
used
:
0
.
05
9000
(
s
)
save
perf
data
success
at
/
storage
/
data
/
perf
.
data
```
> ![](../public_sys-resources/icon-note.gif)**说明**
> 在进行./perf stat/record命令后,用户可多次执行./perf start 和 ./perf stop进行采样, 采样的事件配置为最近一次执行./perf stat/record 中设置的参数。
### 用户态编程实例<a name = "section4.2.2"></a>
本实例实现如下功能:
1.
打开perf字符设备。
4.
写perf配置事件。
3.
启动perf。
5.
停止perf。
6.
读取perf采样数据。
### 用户态示例代码<a name = "section4.2.3"></a>
实例代码如下
```
c
#include "fcntl.h"
#include "user_copy.h"
#include "sys/ioctl.h"
#include "fs/driver.h"
#include "los_dev_perf.h"
#include "los_perf.h"
#include "los_init.h"
/* perf ioctl */
#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
;
ssize_t
len
;
int
fd
=
open
(
"/dev/perf"
,
O_RDWR
);
if
(
fd
==
-
1
)
{
printf
(
"Perf open failed.
\n
"
);
exit
(
EXIT_FAILURE
);
}
PerfConfigAttr
attr
=
{
.
eventsCfg
=
{
#ifdef LOSCFG_PERF_HW_PMU
.
type
=
PERF_EVENT_TYPE_HW
,
.
events
=
{
[
0
]
=
{
PERF_COUNT_HW_CPU_CYCLES
,
0xFFFF
},
},
#elif defined LOSCFG_PERF_TIMED_PMU
.
type
=
PERF_EVENT_TYPE_TIMED
,
.
events
=
{
[
0
]
=
{
PERF_COUNT_CPU_CLOCK
,
100
},
},
#elif defined LOSCFG_PERF_SW_PMU
.
type
=
PERF_EVENT_TYPE_SW
,
.
events
=
{
[
0
]
=
{
PERF_COUNT_SW_TASK_SWITCH
,
1
},
},
#endif
.
eventsNr
=
1
,
/* 1 event */
.
predivided
=
0
,
},
.
taskIds
=
{
0
},
.
taskIdsNr
=
0
,
.
processIds
=
{
0
},
.
processIdsNr
=
0
,
.
needSample
=
1
,
.
sampleType
=
PERF_RECORD_IP
|
PERF_RECORD_CALLCHAIN
,
};
(
void
)
write
(
fd
,
&
attr
,
sizeof
(
PerfConfigAttr
));
/* perf config */
ioctl
(
fd
,
PERF_START
,
NULL
);
/* perf start */
test
();
ioctl
(
fd
,
PERF_STOP
,
NULL
);
/* perf stop */
buf
=
(
char
*
)
malloc
(
LOSCFG_PERF_BUFFER_SIZE
);
if
(
buf
==
NULL
)
{
printf
(
"no memory for read perf 0x%x
\n
"
,
LOSCFG_PERF_BUFFER_SIZE
);
return
-
1
;
}
len
=
read
(
fd
,
buf
,
LOSCFG_PERF_BUFFER_SIZE
);
OsPrintBuff
(
buf
,
len
);
/* print data */
free
(
buf
);
close
(
fd
);
return
0
;
}
```
### 用户态结果验证<a name = "section4.2.4"></a>
输出结果如下:
```
c
[
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
...
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
00
00
00
6
b
65
72
6
e
65
6
c
00
00
01
00
00
00
cc
55
30
40
08
00
00
00
6
b
65
72
6
e
65
6
c
00
00
```
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录