一、统计方法说明
统计各进程的内存占用有两种方式:
- dumpsys meminfo processname
- heap+ion
测试工具大概率会使用第一种方式抓取数据,但是内存占用的详细拆解,需要针对heap+ion的方式进行拆解
二、ion数据
数据抓取
cat d/ion/ion_mm_heap 或 /proc/ion/ion_mm_heap 节点获取Ion的映射信息
数据拆解
MTK有一个解析ion buffer的脚本叫"ion_view_vxx.py"。解析思路如下:
- 先从如下格式的内容中拆解出client(即进程名)、size、pid等基本信息:
buffer heap_id size kmap ref hdl mod mva_cnt mva(dom0) mva(dom1) sec flag pid(alloc_pid) comm(client) v1 v2 v3 v4 dbg_name
time 4 8564945 ms
0x000000004a9f889d 12 2359296 0 2 1 -1 1 5c100000(0) 0(0) 0x0, 0x3, 980( 980) camerahalserver 0x43 0x61 0x6d 0x0 2359296x1-BLOB-Stuff:Hal:Image:STT_main1:1_Blob
解析脚本:
re_buf_list5 = re.compile('^(0x[0-9a-f]+)\s+[-\d]+\s+(\d+)\s+[-\d]+\s+[-\d]+\s+[-\d]+\s+[-\d]+\s+[-\d]+\s+(\S+)\(\S+\)\s+\S+\(\S+\)\s+\S+\s+\S+\s+(\d+)\(\s*(\d+)\)\s+(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+.*)')
符号 | 含义 | 对应字串中的内容 |
---|---|---|
(0x[0-9a-f]+) | 0x字串+多个十六进制数字的重复,并可以使用group(1)获取对应值 | 0x000000004a9f889d |
\s+ | 多个空白符 | |
[-\d]+ | 多位十进制数 | 12 |
\s+ | 多个空白符 | |
(\d+) | 多位十进制数字,并可以使用group(2)获取对应值 | 2359296 |
\s+ | 多个空白符 | |
[-\d]+ | 多位十进制数 | 0 |
\s+ | 多个空白符 | |
[-\d]+ | 多位十进制数 | 2 |
\s+ | 多个空白符 | |
[-\d]+ | 多位十进制数 | 1 |
\s+ | 多个空白符 | |
[-\d]+ | 负号或数字的多位重复 | -1 |
\s+ | 多个空白符 | |
[-\d]+ | 负号或数字的多位重复 | 1 |
\s+ | 多个空白符 | |
(\S+) | 多个非空白符的重复,并可以用group(3)获取对应值 | 5c100000 |
(\S+) | 被()括起来的多个非空白符的重复 | (0) |
\s+ | 多个空白符 | |
\S+ | 多个非空白符的重复 | 0 |
(\S+) | 被()括起来的多个非空白符的重复 | (0) |
\s+ | 多个空白符 | |
\S+ | 多个非空白符的重复 | 0x0, |
\s+ | 多个空白符 | |
\S+ | 多个非空白符的重复 | 0x0, |
\s+ | 多个空白符 | 0x3, |
(\d+) | 多位十进制数字,并可以使用group(4)获取对应值 | 980 |
\s+ | 多个空白符 | |
(\s* | 左括号加多个空白符 | ( |
(\d+)) | 多位十进制数+右括号,并可以使用group(5)获取右括号之前的值 | 980) |
\s+ | 多个空白符 | |
(\S+) | 多个非空白符的重复,并可以用group(6)获取对应值(作为client存储) | camerahalserver |
\s+\S+\s+\S+\s+\S+\s+\S+\s+ | 数个非空白字符串,中间以空白符间隔 | 0x43 0x61 0x6d 0x0 |
(\S+.*)’) | 尽可能多的匹配非空白符,且可以用group(7)获取对应值(作为dbg存储) | 2359296x1-BLOB-Stuff:Hal:Image:STT_main1:1_Blob |
- 再从如下格式的内容中,拆解出client detail补充进数组:
client(0x00000000c423d3cf) camerahalserver (Hal:Image:P1:Fullraw_main1) pid(940) ================>
handle=0x00000000ad8b8dd3, buffer=0x00000000f4cc7233, heap=12, fd= 126, ts: 6344772ms
handle=0x00000000faea8c5a, buffer=0x000000006cde7e02, heap=12, fd= 107, ts: 6344570ms
解析代码如下:
#格式对应上面数据中client行
re_client_begin = re.compile('^client\(0x[0-9a-f]+\)\s+(.*?)\s*\((.*?)\)\s+pid\((\d+)\)')
#格式对应上面数据中handle行
re_client_buffer_R= re.compile('^\s*handle=(0x[0-9a-f]+)\s*\((.*?)\),\s*buf.*?=(0x[0-9a-f]+)')
m = re_client_begin.search(line)
if m:
client = m.group(1) #获取正则表达式中第一个括号内的部分
client_detail = m.group(2)
client_pid = m.group(3)
client_list[client] = client_pid
log('client_begin',client,client_detail,client_pid)
continue
符号 | 含义 | 对应字串中的内容 |
---|---|---|
^ | 标志行首 | 行首 |
client | 代表匹配它本身,匹配client字串 | client |
\( | 匹配字串中的’('字符 | ( |
0x | 代表匹配它本身 | 0x |
[0-9a-f] | 匹配0-9或是a-f范围内的任意字符 | 同下 |
+ | 重复多次 | 00000000c423d3cf |
\) | 匹配字串中的 ‘)’ 字符 | ) |
\s+ | 多个分隔符,空格或是tab | 多个空格 |
( | 从此字符后的第一个字符,到‘)'前的第一个字符,可用group(1)直接获取 | 无 |
.*? | 多个任意字符,取能匹配到的最小字串 | camerahalserver |
) | 对应上面的’(’,完成group(1)的范围框定 | 无 |
\s* | 多个分隔符,空格或是tab | 多个空格 |
\( | 匹配字串中的’('字符 | ( |
(.*?) | 多个任意字符,取能匹配到的最小字串,可用group(2)直接获取匹配到的内容 | Hal:Image:P1:Fullraw_main1 |
\) | 匹配字串中的 ‘)’ 字符 | ) |
\s+ | 多个分隔符,空格或是tab | 多个空格 |
pid | 匹配的’pid’字串 | pid |
\( | 匹配字串中的 ‘)’ 字符 | ( |
(\d+) | 匹配多个数字,同时可用group(3)直接获取匹配到的内容 | 940 |
\) | 匹配字串中的 ‘)’ 字符 | ) |
’ | 标志行尾 | 行尾 |
解析完client行以后,记录client和client detail当前值,并继续解析handle行。
在第一步得到的数组中,根据addr取得对应的client,并将client detail拼接到原本的client字串后,重新更新这一行。
- 将解析完成后返回的buffer,以client、dbg为关键字进行排序。
当前的字串,每一行对应一块ion buffer。将dbg、size完全相同的buffer合并统计。记录单个buffer的大小、总块数、总大小
格式如下:
Level2:
client_detail item total block_size block_cnt
----------------------------------------------------------------------------------------------------------------------------------------
Hal:Image:FD 640x480-YUY2-Hal:Image:FD 2 MB 0 MB 5
Hal:Image:P1:Fullraw_main1 16423680x-BLOB-Hal:Image:P1:Fullraw_main1 125 MB 15 MB 8
Hal:Image:P1:Resizeraw_main1 1280x960-FG_BAYER10-Hal:Image:P1:Resizeraw_main 24 MB 2 MB 11
拆解到这里,即可对比测试机和对比机的具体差异。
ion的优化点多在CapturePipeline、P1的buffer数量上。但这类修改要多验证对性能、流畅度的影响。
三、heap数据
数据抓取
heap数据对应的有RSS/PSS,我们一般采用PSS的数据。
RSS的值可以用 PS -A | grep processname 命令抓取。
xxxxx:/ # ps -A | grep -e camerahalserver -e PID
USER PID PPID VSZ RSS WCHAN ADDR FRZ S NAME
cameraserver 936 1 1303536 42400 binder_ioctl_write_read 0 efg S camerahalserver
PSS的值可以用pmap -x pid 命令抓取
xxxxx:/ # pmap -x 936
936: /vendor/bin/hw/camerahalserver
Address Kbytes PSS Dirty Swap Mode Mapping
0000005a2bfbe000 8 4 0 0 r---- camerahalserver
……
0000007fe5077000 132 36 36 32 rw--- [stack]
---------------- ------ ------ ------ ------
total 1303536 36355 19608 20628
数据拆解
PSS内存使用 cat /proc/pid/smaps 信息进一步拆解。
smaps中抓取到的信息格式如下:
5d1d035000-5d1d037000 r--p 00000000 fc:00 154 /vendor/bin/hw/camerahalserver
Size: 8 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd mr mw me dw
脚本使用比ion部分简单,使用正则表达式获取对应数据并记录即可。像上面这种没有Name项的,可以记录第一行中“ /vendor/bin/hw/camerahalserver”作为关键字串。
将PSS部分的数据筛选出来并统计占用,汇总后与对比机的统计信息做对比,确认是哪部分有明显占用较多的情况
使用MTK的解析工具解析出来的数据,要比只使用PSS值累加的数据大一些。
对比内容发现,MTK的工具解析出来的内容,后面标注[CODE]的内容可以对应PSS的数值相加。但有一些后面标准 [libc_malloc]的内容,从smaps的内容中没有找到对应的数值。目前MTK case的负责人也说不清这块数据的来源。暂且作为遗留问题吧。
tips:
smaps解析出来的内容仍然很多,且不同的系统中有些库放置的路径不一样,直接对比解析结果仍然比较困难。我这里使用的方式是,另写一个脚本,将解析结果中库名前的路径全部删除,然后以库名为关键字排序,直接对比排序结果就能比较直观的确认到差异点。