Franklin1 Android Memtrack GPU 内存分析
1. 基础介绍
提到Android Memtrack,就必须先提到Android Meminfo
1.1 Meminfo
meminfo是 Android 系统提供的内存状态报告工具,用于显示系统或特定应用的内存使用情况。它能帮助开发者诊断内存泄漏、优化应用性能,或了解系统整体内存分配。获取meminfo的方式有如下两种。
- 查看系统整体内存信息
adb shell dumpsys meminfo- 查看特定应用的内存信息
指定应用的包名或者进程号,查看该应用的内存使用详情:
adb shell dumpsys meminfo <包名>
或
adb shell dumpsys meminfo <进程号>示例:
adb shell dumpsys meminfo com.xiaomi.kidspace
或
adb shell dumpsys meminfo 18775对单应用执行dumpsys命令后我们可以得到下面的memory相关信息内容。
Applications Memory Usage (in Kilobytes):
Uptime: 2813643 Realtime: 2813643
** MEMINFO in pid 18775 [com.xiaomi.kidspace] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 81837 81808 12 65 83644 105152 89890 10500
Dalvik Heap 12144 7556 4456 7 14912 30665 6089 24576
Dalvik Other 3218 3120 44 1 3828
Stack 3132 3132 0 0 3140
Ashmem 26 0 0 0 1936
Other dev 167 0 164 0 636
.so mmap 4626 276 24 7 64748
.jar mmap 1132 0 64 0 47572
.apk mmap 1420 56 776 0 5332
.ttf mmap 1433 0 240 0 6552
.dex mmap 33568 20 33536 0 34736
.oat mmap 296 0 0 0 13436
.art mmap 2774 908 1328 82 23260
Other mmap 130 4 92 0 1748
EGL mtrack 123200 123200 0 0 123200
GL mtrack 119340 119340 0 0 119340
Unknown 5906 5424 452 1 6448
TOTAL 394512 344844 41188 163 554468 135817 95979 35076
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 9792 38172
Native Heap: 81808 83644
Code: 34992 172468
Stack: 3132 3140
Graphics: 242540 242540
Private Other: 13768
System: 8480
Unknown: 14504
TOTAL PSS: 394512 TOTAL RSS: 554468 TOTAL SWAP PSS: 163
Objects
Views: 363 ViewRootImpl: 1
AppContexts: 7 Activities: 1
Assets: 34 AssetManagers: 0
Local Binders: 43 Proxy Binders: 79
Parcel memory: 22 Parcel count: 89
Death Recipients: 2 WebViews: 0
SQL
MEMORY_USED: 500
PAGECACHE_OVERFLOW: 125 MALLOC_SIZE: 46
DATABASES
pgsz dbsz Lookaside(b) cache hits cache misses cache size Dbname
PER CONNECTION STATS
4 20 17 0 19 1 /data/user/0/com.xiaomi.kidspace/databases/stat_database
4 20 55 1 20 2 /data/user/0/com.xiaomi.kidspace/databases/filetransfer_database
4 24 69 8 40 6 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics
4 8 0 0 0 (attached) temp
4 24 43 1 17 2 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics (2)
4 24 23 0 24 2 /data/user/0/com.xiaomi.kidspace/databases/mitu-media
4 8 0 0 0 (attached) temp
4 24 33 13 17 2 /data/user/0/com.xiaomi.kidspace/databases/mitu-media (2)
POOL STATS
cache hits cache misses cache size Dbname
0 20 20 /data/user/0/com.xiaomi.kidspace/databases/stat_database
1 21 22 /data/user/0/com.xiaomi.kidspace/databases/filetransfer_database
11 77 88 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics
15 61 76 /data/user/0/com.xiaomi.kidspace/databases/mitu-media其中和GPU相关的有EGL mtrack、GL mtrack以及Graphics。Graphics包括五种数据类型。
MemtrackType::OTHER = 0
MemtrackType::GL = 1
MemtrackType::GRAPHICS = 2
MemtrackType::MULTIMEDIA = 3
MemtrackType::CAMERA = 4MemtrackType::GL对应GL mtrack,MemtrackType::GRAPHICS对应EGL mtrack。
1.2 Memtrack
接下来我们再来说明Android MemTrack是什么。MemTrack是一个由vendor实现的系统服务,允许Android操作系统track GPU内存的分配和使用。Android Meminfo中GL mtrack和EGL mtrack就是从MemTrack中获取而来。时序图如下。

其中关键函数Memtrack::getMemory,我们在memtrack服务代码中有如下实现代码片段:
ndk::ScopedAStatus Memtrack::getMemory(int pid, MemtrackType type,
std::vector<MemtrackRecord>* _aidl_return)
{
MemtrackRecord records = { .flags = 0, .sizeInBytes = 0};
int64_t size = 0;
if (pid < 0) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
_aidl_return->clear();
if (type == MemtrackType::GL) {
if (!getGLMemory(pid, &size)) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NONE));
}
records.flags = MemtrackRecord::FLAG_SMAPS_UNACCOUNTED |
MemtrackRecord::FLAG_PRIVATE |
MemtrackRecord::FLAG_NONSECURE;
records.sizeInBytes = size;
} else if (type == MemtrackType::GRAPHICS) {
if (!getGraphicsMemory(pid, &size)) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NONE));
}
records.flags = MemtrackRecord::FLAG_SMAPS_UNACCOUNTED |
MemtrackRecord::FLAG_SHARED |
MemtrackRecord::FLAG_SYSTEM |
MemtrackRecord::FLAG_NONSECURE;
records.sizeInBytes = size;
}
_aidl_return->push_back(records);
return ndk::ScopedAStatus::ok();
}其中通过函数getGLMemory获取GL mtrack,函数getGraphicsMemory获取EGL mtrack,更多代码实现相关参见文档Memtrack功能实现及代码流程。这里我们分GL mtrack和EGL mtrack两个模块介绍这两部分内存的统计方式。
在介绍这两部分内容前,我们要对GPU内存有一个基础性的了解。通常gpu只是一个so,没有自己的进程空间,而应用使用gpu,是通过调用so的方式,那么gpu实际上是属于每个应用进程的,那么这部分内存也是应用进程自己的进程空间。
1.2.1 GPU内存
1.2.1.1 GPU Internal Memory
GPU Internal Memory通常是指在gpu kernel driver中通过alloc_pages接口申请物理页的内存,再通过gpu mmu插页表使用,通常是指device memory。除此之外,有一类是gpu driver申请的host memory,这一部分是不记在Internal Memory,因为它们是通过malloc这些类似的接口申请。
host memory顾名思义,就是cpu会访问的内存,包括像program、structor、allocator等等。device memory就是gpu要访问的内存,包括像vertex buffer、index buffer、texture、fbo、ring buffer(command stream)、suspend buffer等等。但有些内存cpu和gpu都会访问,比如这里的ring buffer,mali csf架构,ring buffer由cpu组装,通过ring doorbell的方式通知gpu csf读取command stream执行。还有texture,应用使用pixels指针通过接口glTexImage2D上传图片资源到gpu可以访问的内存,glTexImage2D流程比较复杂,分为gpu copy和cpu copy,对于非AFBC压缩格式,也即linear,则通过cpu neon或者linear copy。对于AFBC这种压缩格式,只能通过gpu copy,先用cpu将应用上传的pixels的内容copy到一块cpu和gpu都可以访问的非压缩内存,再将src buffer通过gpu的copy command渲染到一块afbc的内存。那这里定界就没那么清晰,但这些内存都是通过在gpu kernel driver中通过alloc_pages申请,然后通过mmu插页表,这些都是device内存。GL mtrack只统计device memory,包括umd侧通过ioctl到内核以及内核自己通过alloc_pages申请的内存,也即这里的Internal Memory。
1.2.1.2 GPU External Memory
GPU External Memory通常是由应用申请的内存,然后通过gpu driver import进来,通过gpu的mmu插页表来使用,通常包括两部分umm(dma-buf)和user buffer。其中dma-buf是linux kernel的叫法,而对应到Android User Space,通常通过GraphicBuffer或者AHardwareBuffer来表示,这些内存由应用通过graphics allocator服务,进而通过gralloc调用dma-buf接口进行申请,具体细节这里不赘述。而EGL mtrack对应于我们这里的umm(dma-buf),不包括user buffer部分。所以总结下来就是,我们平台认为的EGL mtrack就是应用自己申请的dma-buf,然后又给到gpu插页表使用的。
目前我们分析下来高通平台和我们的做法是一致的。但MTK平台不是,MTK平台采用类似于Android原生的dmabuf_dump工具得到的应用进程所有的dma-buf的pss,这里得介绍下内存统计的pss、rss、vss、uss概念。

这里对我们平台内存水线对齐造成了极大的困扰。因为我们平台EGL部分和高通的做法一致,但是高通平台的GL mtrack又上报的不准确,导致我们没法和高通平台对标。和MTK平台对标,GL mtrack大家都是mali的,可以对齐,但EGL的做法又不一致,导致我们和MTK平台对标Graphics部分内存也存在gap,在O2S/O80平台做了大量的澄清工作,最终是使用MTK平台对标,但EGL mtrack需要使用dmabuf_dump工具进行矫正,统计内存比MTK小或者并没有多出很多,那我们则认为是合理的。否则需要应用或者系统侧分析多占用的原因和合理性,若有内存泄漏则需要解决。在澄清的过程中我们也确实发现有些应用行为和MTK平台差异的地方以及不合理的地方,此外还有MTK平台在skia平台的一个优化点,修改链接如下。

我们平台和高通一样,都是google原生默认的4m,而MTK平台默认值改成了64kb,这会导致很多小于4mb的vulkan buffer或者image资源申请4mb大小的内存,这里是skia采用了AMD的vma内存池机制。这里的差异我们也知会了产品线framework组件的同学。
但到O81A,我们没有MTK的PAD作为竞品平台,导致又回到N81A高通平台,虽然EGL mtrack能对齐,但GL mtrack高通平台非常小,小到看到数据就认为不合理,也提出了质疑,但没有源码和证据。在后续澄清的过程中发现他们GL mtrack上报的不准,只上报了一部分,实际上要看gpu_unmapped和gpu_mapped两部分。这里的过程比较曲折,对齐和澄清的代价也比较大,对我们的困扰也是极大的。对于这两个平台的实现方式,后面内容会详细介绍。
1.2.1.3 memory profile
此外arm mali平台还有一个工具,叫mem_profile,MTK采用mali gpu同样有这个工具,这个工具是在gpu umd driver中对gpu umd driver用到的所有内存进行统计,但统计不到gpu kernel dirver中独立申请的内存。mem_profile统计的维度包括:gpu host memory,gpu deivce memory(umd通过ioctl到kmd中通过alloc_pages申请的内存,不包括kmd自己通过alloc_pages申请的内存)以及GPU External Memory,包括umm(dma-buf)和user buffer。所以可以看到memory profile也无法完全对齐到GL mtrack和EGL mtrack。当前我们平台上通过如下方式dump mem_profile。
adb shell stop
adb shell setprop vendor.mali.xfeature_mem_usage_report 1
adb shell start
adb shell cat /sys/kernel/debug/mali0/ctx/pid_ctxid/mem_profile
note:如果出现/sys/kernel/debug下无内容得情况,则需要先执行:
adb shell mount -t debugfs none /sys/kernel/debug 对O81A平台儿童空间应用首页,我们执行上述命令可以得到如下gpu gl memory信息。
com.xiaomi.kidspace:
Channel: Unnamed (Total memory: 0)
(empty)
Channel: Base Internal (Total memory: 1994752)
Bin id Count / Memory
13: 1 / 4096
17: 10 / 655360
20: 2 / 1335296
Channel: Framepool (Total memory: 0)
(empty)
Channel: Frame Internal (Total memory: 0)
(empty)
Channel: GPU Program (Total memory: 315392)
Bin id Count / Memory
13: 30 / 122880
14: 18 / 172032
15: 1 / 20480
Channel: EGL Color Plane (Total memory: 0)
(empty)
Channel: GLES Vertex Array Object (Total memory: 0)
(empty)
Channel: Image Descriptor (Total memory: 0)
(empty)
Channel: Texture (Total memory: 0)
(empty)
Channel: Buffer (Total memory: 0)
(empty)
Channel: CRC Buffer (Total memory: 0)
(empty)
Channel: CPOM Host Memory (Total memory: 0)
(empty)
Channel: CPOM Render State (Total memory: 0)
(empty)
Channel: CPOM Compute Shader (Total memory: 0)
(empty)
Channel: CPOM Static Data (Total memory: 0)
(empty)
Channel: CFRAME Host Memory (Total memory: 0)
(empty)
Channel: CFRAME Sample Position (Total memory: 0)
(empty)
Channel: CFRAME Discardable FBD (Total memory: 0)
(empty)
Channel: CFRAME Tessellation/Geometry (Total memory: 0)
(empty)
Channel: COBJ Host Memory (Total memory: 0)
(empty)
Channel: CMAR Host Memory (Total memory: 0)
(empty)
Channel: CMAR Profiling/Dumping (Total memory: 0)
(empty)
Channel: CBLEND Host Memory (Total memory: 0)
(empty)
Channel: GLES Host Memory (Total memory: 0)
(empty)
Channel: GLES Query/XFB/Unroll (Total memory: 0)
(empty)
Channel: GLES Multiview (Total memory: 0)
(empty)
Channel: CDEPS Host Memory (Total memory: 0)
(empty)
Channel: CMEM Sub-allocators (Total memory: 815104)
Bin id Count / Memory
13: 81 / 331776
14: 59 / 483328
Channel: CMEM Hoard (Total memory: 74944)
Bin id Count / Memory
7: 252 / 16256
8: 328 / 49856
9: 23 / 8832
Channel: CL Command Payloads (Total memory: 0)
(empty)
Channel: CL Workgroup/Thread (Total memory: 0)
(empty)
Channel: CL Host Memory (Total memory: 0)
(empty)
Channel: CL Shared Virtual Memory (Total memory: 0)
(empty)
Channel: CINSTR Memory (Total memory: 0)
(empty)
Channel: GFX Device Memory CPU Uncached (Total memory: 110604288)
Bin id Count / Memory
18: 5 / 1003520
20: 2 / 1056768
21: 1 / 1052672
22: 6 / 16916480
23: 6 / 29052928
24: 3 / 44740608
25: 1 / 16781312
Channel: GFX Device Memory CPU Cached (Total memory: 0)
(empty)
Channel: GFX Device Memory Transient (Total memory: 0)
(empty)
Channel: GFX Device Memory Protected (Total memory: 0)
(empty)
Channel: GFX Device External Memory (Total memory: 126156800)
Bin id Count / Memory
25: 5 / 126156800
Channel: GFX Device External Swapchain Memory (Total memory: 0)
(empty)
Channel: GFX Device Internal Memory (Total memory: 397312)
Bin id Count / Memory
13: 28 / 114688
17: 2 / 147456
18: 1 / 135168
Channel: GFX Device Internal TLS Memory (Total memory: 0)
(empty)
Channel: GFX Device Internal Host Memory (Total memory: 0)
(empty)
Channel: GFX Device Internal Protected Memory (Total memory: 0)
(empty)
Channel: GFX Device Internal Transient Memory (Total memory: 0)
(empty)
Channel: GFX Descriptor Pool Memory (Total memory: 8192)
Bin id Count / Memory
13: 2 / 8192
Channel: GFX Command Allocator Memory (Total memory: 1843200)
Bin id Count / Memory
13: 21 / 86016
14: 33 / 315392
18: 11 / 1441792
Channel: GFX Command Allocator Host Memory (Total memory: 720896)
Bin id Count / Memory
17: 11 / 720896
Channel: GFX Command Allocator Protected Memory (Total memory: 0)
(empty)
Channel: GFX Graph internal host (Total memory: 0)
(empty)
Channel: GFX Graph internal device (Total memory: 0)
(empty)
Channel: GFX Graph internal device transient (Total memory: 0)
(empty)
Channel: Vulkan Bound Buffer Memory (Total memory: 27541556)
Bin id Count / Memory
8: 1 / 180
11: 1 / 1920
15: 1 / 30720
16: 7 / 245760
17: 2 / 131072
19: 5 / 1966080
21: 4 / 5242880
22: 1 / 3145728
25: 1 / 16777216
Channel: Vulkan Bound Image Memory (Total memory: 201942208)
Bin id Count / Memory
14: 5 / 51200
16: 5 / 204800
18: 3 / 408768
20: 10 / 7192576
21: 3 / 5591040
22: 3 / 9551872
23: 1 / 8056832
24: 3 / 44728320
25: 5 / 126156800
Channel: CMAR Signal Memory (Total memory: 0)
(empty)
Channel: CMAR Flush Chain Memory (Total memory: 0)
(empty)
Channel: CMAR Metadata List Memory (Total memory: 0)
(empty)
Total allocated GPU memory: 241319936这里有个误区需要和大家说明,通常大家看到Graphics部分内存,首先想到的就是GPU占用大,但是GPU的内存模型非常复杂,应用自己申请的,给到GPU使用,但管理权在应用自己手上的内存也会被统计到Graphics中,如GraphicsBuffer、AHardwareBuffer、Texture、Vertex Buffer、Index Buffer等等。有些是应用通过标准绘图API(OpenGL ES/Vulkan/OpenCL)申请的内存,若要释放需要应用显示的调用相应的destroy或者delete API,如申请通过vkAllocateMemory、释放通过vkFreeMemroy等等,GPU管理这部分内存通常通过引用计数,若应用不调用destroy类型的接口,那么GPU的引用计数永远不会放掉,那么GPU内存统计就会统计到。
- vkAllocateMemory

- vkFreeMemory

此外还有GPU自己申请的内存,如crc buffer、tiler heap buffer、suspend buffer,这类内存通常伴随相应功能resource或者进程释放,如crc buffer,若相应的fbo被释放了,那么crc也会销毁。如tiler heap buffer,只有在应用进程相应的Queue Group被销毁了,这部分内存才会释放,通常应用进程销毁,这些内存都会被释放。希望这里能避免大家对这些内存统计的刻板印象,认为Graphics内存都是GPU自己申请,自己使用的,占用大或者泄漏就是GPU的泄漏。通常来说,内存泄漏或者内存占用大多在纹理等resource资源,这类控制权都在应用(可能是android framework)手上,gpu驱动类引用计数使用异常造成的内存无法释放问题,目前我们没有遇到过。
针对这里我们用Vulkan Tutorial来说明这里的内存申请和释放情况。Introduction - Vulkan Tutorial
1.Vertex Buffer Object(VBO)/Index Buffer Object(IBO)
图形渲染使用的顶点数据和索引数据,由应用上传,控制渲染区域和渲染顺序
- 创建VBO/IBO,申请memory
vkCreateBuffer // 创建VBO或者IBO
vkGetBufferMemoryRequirements // 获取buffer memory相关信息
vkAllocateMemory // 申请memory
vkBindBufferMemory // 绑定memory到VBO或者IBO
vkMapMemory // map memory,获取CPU VA,上传顶点数据或者索引数据
vkUnmapMemory // unmap memory
- VBO/IBO
vkCmdBindVertexBuffers // 绑定VBO到当前command recording
vkCmdBindIndexBuffer // 绑定IBO到当前command recording
- 销毁VBO/IBO,释放memory
vkDestroyBuffer // 销毁VBO或者IBO
vkFreeMemory // 释放memory
- Texture
图形渲染使用的纹理数据,由应用上传,或者GPU渲染产生,作为后续渲染的source,GPU通过采样器采样贴图,丰富渲染内容,这里应用上传可以使用应用自己申请的malloc内存或者dma-buf(GraphicBuffer/AHardwareBuffer)。GPU渲染产生比较特殊,图形学常用的render to texture,texture绑定到FrameBuffer Object(FBO)中,GPU渲染到该FBO后,纹理继续作为source在后续渲染中使用。
🐵
1.创建Texture,申请memory
vkCreateImage // 创建VkImage
vkGetImageMemoryRequirements // 获取image memory相关信息
vkAllocateMemory // 申请memory
vkBindImageMemory // 绑定memory到Texture
vkMapMemory // map memory,获取CPU VA,上传纹理数据
vkUnmapMemory // unmap memory
2.Draw command中使用使用Texture
vkCreateImageView // 创建VkImageView
vkCreateSampler // 创建VkSampler
vkUpdateDescriptorSets // 更新VkDescriptorSet
vkCmdBindDescriptorSets // 绑定纹理到当前command recording
3.销毁纹理,释放memory
vkDestroySampler // 销毁VkSampler
vkDestroyImageView // 销毁VkImageView
vkDestroyImage // 销毁VkImage
vkFreeMemory // 释放memory
- FrameBuffer Object(FBO)
fbo实际上就是gpu的render target,gpu的所有渲染行为都是围绕fbo结果展开的。fbo有两个大类,一类是default fbo,即fbo0,使用VkSwapchainKHR中的VkImageView,对应的memory是surface/layer(ANativeWindow)中的GraphicBuffer,这里涉及到BufferQueue轮转,有多块GraphicBuffer,简单来说,就是应用的一个图层有多块buffer轮转,通常至少3块。感兴趣的同学可以参考如下文档学习。BufferQueue 学习总结(内附动态图)-CSDN博客。还有一类是临时fbo,上面说到的render to texture就是典型的用法,texture也不局限于通过vkAllocateMemory使用gpu申请,vkAllocateMemory同样可以import dma-buf(GraphicBuffer/AHardwareBuffer)。
- 创建fbo vkCreateAndroidSurfaceKHR // 创建VkSurfaceKHR,传入ANativeWindow vkCreateSwapchainKHR // 创建VkSwapchainKHR,从ANativeWindow中BufferQueue dequeue所有buffer,创建对应的VkImage vkGetSwapchainImagesKHR // 获取当前swapchain中所有VkImage,创建VkImageView vkCreateImageView // 创建VkImageView vkCreateFramebuffer // 创建VkFrameBuffer vkCreateRenderPass // 创建VkRenderPass 2.使用fbo vkCmdBeginRenderPass // 函数入参VkRenderPassBeginInfo包含当前renderpass需要渲染 的fbo vkCmdEndRenderPass // 结束当前renderpass,也即当前renderpass对fbo的渲染 command recording结束 3.销毁fbo,释放memory vkDestroyRenderPass // 销毁renderpass vkDestroyFramebuffer // 销毁fbo vkDestroyImageView // 销毁VkImageView vkDestroySwapchainKHR // 销毁VkSwapchainKHR vkDestroySurfaceKHR // 销毁VkSurfaceKHR,但这里BufferQueue中的buffer内存并没有 被释放,到这里gpu驱动占用的引用计数为0,真正要释放内存 需要BufferQueue所在的Surface/Layer销毁,不受gpu驱动控制
1.2.1.4 GPU内存总结

这里有一类内存比较特殊,如tiler heap buffer,是由umd侧在queue_internal::init时通过base_tiler_heap_init创建的tiler heap,但其内存是在内核直接调用接口kbase_mem_alloc,进一步调用alloc_pages申请的2m大小内存,gpu的va也会返回给umd侧,在command stream中将该地址记录下来,gpu csf可以通过command stream解析到,此外当2m大小用完时,会通过中断触发kernel handle_oom_event函数,分配新的2m大小的heap tiler内存,但这部分内存不会被memory profile统计到,这部分内存的定界就比较模糊,但GL mtrack会统计到这部分内存,我们姑且当做是kmd自己调用alloc_pages申请的内存。
1.2.2 Xring mtrack
我们平台采用arm mali平台新一代csf架构GPU krake,发布型号Mali-G925-Immortalis。GL mtrack部分完全采用arm mali内核驱动统计的通过alloc_pages申请的内存,包括GPU Internal Memory部分以及kmd自己调用alloc_pages申请,给kmd或者gpu hw使用的内存。如texutre、fbo、vbo、ibo(ebo)、ring buffer(command stream)、tiler heap buffer、sync page(fence、cqs)、suspend buffer等等。
Memtrack服务中通过接口getGLMemory获取GL mtrack,该函数会进一步调用函数getGPUMemory从文件节点/proc/xgpu/gpu_memory中解析出整机或者单应用的GL内存,节点内容形式如下。
mali0 132593 79404
1461 95 0 0
17322 35869 32557 73339
17374 3743 0 0
17447 22643 6121 6121
17871 50772 30584 30584
17922 134 0 0
18122 5947 878 878
19412 6686 0 0
20050 3487 0 0
24887 3109 0 0
30132 100 0 0第一行是整机的GPU内存使用情况。mali0表示设备名,132593是page页数,表示当前整机GL mtrack占用是1325934kb = 530372kb,79404同样也是page页数,表示当前整机的EGL mtrack占用是794044kb = 317616kb
第二行开始是各应用进程占用的GL mtrack和EGL mtrack以及当前应用进程使用到的所有EGL内存(包括应用自己申请的以及其它应用申请的share到当前应用使用的)。
在介绍前,我们先看下arm原生的GL内存统计格式。
mali0 128829
kctx-0x0000000016426fe9 16397
kctx-0x0000000096bb2fd1 100
kctx-0x0000000052fe8686 4271
kctx-0x00000000be7326e2 3090
kctx-0x00000000b146d699 2197
kctx-0x00000000198ddc58 1344
kctx-0x00000000d15ce100 1207
kctx-0x0000000078949b5c 2509
kctx-0x00000000d62ab77b 4120
kctx-0x0000000013b44c09 3529
kctx-0x00000000a5028bad 11833
kctx-0x0000000079b448f0 2415
kctx-0x00000000168ff658 3109
kctx-0x00000000b617e6e0 6463
kctx-0x000000002757675a 134
kctx-0x000000004664c545 24318
kctx-0x000000003a088df2 4866
kctx-0x00000000e7a12809 36824
kctx-0x000000000e967d2c 95原生的格式与我们的类似,差异在于,是按照不同kctx来统计,此外这里只有GL内存。这里对于OpenGL ES的应用来说,一个kctx一般对应一个应用进程,但到Vulkan之后,这里变了,可能一个应用进程会有两个kctx的情况,arm内核中又引入了另外一个变量,kprcs,而kctx里面会保存kprcs指针,故我们在自己的节点中使用kernel rbtree对这里的内存按tgid(进程号)做了重新排布,以适应我们GL mtrack和EGL mtrack的需求。rbtree节点信息结构如下。
struct kbase_tgid_mem_info {
int tgid;
int gl_pages;
int pss_pages;
int rss_pages;
struct rb_node tgid_mem_node;
};节点信息主要通过当前kbdev下的kctx_list,将相应的信息填到对应的变量。
1.2.2.1 GL内存统计
GL部分内存我们关注gl_pages,其值主要通过如下方式进行赋值。
// 使用的+=的原因是一个kprcs可能有多个kctx
tgid_info->gl_pages += atomic_read(&(kctx->used_pages));这里我们介绍下used_pages值的由来。在我们gpu kmd代码中,函数kbase_alloc_phy_pages_helper/kbase_alloc_phy_pages_helper_locked有如下代码段:
/* Increase mm counters before we allocate pages so that this
* allocation is visible to the OOM killer. The actual count
* of pages will be amended later, if necessary, but for the
* moment it is safe to account for the amount initially
* requested.
*/
new_page_count = mem_account_inc(kctx, nr_pages_requested);这里表示在分配前我们会先把要分配的pages添加到内存统计中来,接着会有如下代码段进行矫正。
/* Amend the page count with the number of pages actually used. */
if (nr_pages_to_account > nr_pages_requested)
new_page_count = mem_account_inc(kctx, nr_pages_to_account - nr_pages_requested);
else if (nr_pages_to_account < nr_pages_requested)
new_page_count = mem_account_dec(kctx, nr_pages_requested - nr_pages_to_account);如果分配成功,因为2m page优化带来的新分配部分会继续添加到内存统计,如果分配失败,则减去前面未分配前增加到内存统计中的部分,从而达到矫正内存统计的目的。mem_account_inc实现如下:
static int mem_account_inc(struct kbase_context *kctx, int nr_pages_inc)
{
int new_page_count = atomic_add_return(nr_pages_inc, &kctx->used_pages);
atomic_add(nr_pages_inc, &kctx->kbdev->memdev.used_pages);
kbase_process_page_usage_inc(kctx, nr_pages_inc);
kbase_trace_gpu_mem_usage_inc(kctx->kbdev, kctx, nr_pages_inc);
return new_page_count;
}这里我们看到会将增肌的page页数添加到kctx→used_pages以及kctx→kbdev→memdev.used_pages,前者是单kctx的内存页数,后者是整机的GL内存页数。
此外,当这部分内存不再使用时,函数kbase_free_phy_pages_helper/kbase_free_phy_pages_helper_locked有如下代码段:
new_page_count = mem_account_dec(kctx, nr_pages_to_account);mem_account_dec实现如下:
static int mem_account_dec(struct kbase_context *kctx, int nr_pages_dec)
{
int new_page_count = atomic_sub_return(nr_pages_dec, &kctx->used_pages);
atomic_sub(nr_pages_dec, &kctx->kbdev->memdev.used_pages);
kbase_process_page_usage_dec(kctx, nr_pages_dec);
kbase_trace_gpu_mem_usage_dec(kctx->kbdev, kctx, nr_pages_dec);
return new_page_count;
}原理同mem_account_inc。
函数kbase_alloc_phy_pages_helper/kbase_alloc_phy_pages_helper_locked会进一步调用到alloc_pages,分配order为9(2m page,512个4k pages)或者order为0的pages,至于哪些内存会调用到这两个函数非常繁杂,可以参见前面GPU内存总结部分,对细节感兴趣的可以阅读gpu kmd源码。函数kbase_free_phy_pages_helper/kbase_free_phy_pages_helper_locked会调用__free_pages释放order为9(2m page,512个4k pages)或者order为0的pages。
1.2.2.2 EGL内存统计
EGL部分内存统计我们关注函数kbasep_gpu_memory_procs_egl。其值通过函数从根节点为kctx→kprcs→dma_buf_root的红黑树中获取。
tgid_info->pss_pages = kbasep_gpu_memory_procs_egl(kbdev, &kctx->kprcs->dma_buf_root, true);这里需要注意,除了进程级别的红黑树,我们还有一颗设备级别的红黑树,用来统计整机的EGL部分内存,根节点是kbdev→dma_buf_root。
seq_printf(sfile, "%-16s %10d %10d\n",
kbdev->devname,
atomic_read(&(kbdev->memdev.used_pages)),
kbasep_gpu_memory_procs_egl(kbdev, &kbdev->dma_buf_root, false));函数kbasep_gpu_memory_procs_egl定义如下。
int kbasep_gpu_memory_procs_egl(struct kbase_device *kbdev, struct rb_root *root, bool is_pss)
{
struct rb_node *node = NULL;
struct kbase_dma_buf *buf_node;
int total_size = 0;
lockdep_assert_held(&kbdev->dma_buf_lock);
if (!root) {
dev_err(kbdev->dev, "Invalid rb root\n");
return 0;
}
// traverse all the dmabuf node in rb tree
for (node = rb_first(root); node; node = rb_next(node)) {
buf_node = rb_entry(node, struct kbase_dma_buf, dma_buf_node);
if (!buf_node || !buf_node->dma_buf) {
dev_err(kbdev->dev,"Invalid buffer node or dma_buf\n");
continue;
}
if (is_pss) {
// When the used_count is 1, means it's this process firstly use this dmabuf
if (buf_node->used_count == 1) {
total_size += buf_node->dma_buf->size;
}
} else {
// Calculate rss by sum up all dmabuf node
total_size += buf_node->dma_buf->size;
}
}
return PAGE_ALIGN(total_size) >> PAGE_SHIFT;
}这里我们可以看到,该函数通过遍历红黑树,累加所有节点的dma_buf→size,最终得到进程级或者设备级的EGL内存用度,此外这里需要注意的是变量is_pss,这里不是纯粹意义上的pss,只是为了标识当前要统计的内存是不是由当前应用进程申请的。对于如何识别rbnode中保存的dma-buf是不是当前进程自己申请的,有一个小技巧,接下来介绍dma-buf红黑树的流程中说明。
上面讲到gpu kmd中有两种类型的dma_buf统计的数,一种是设备级,一个dev只有一颗,根节点kbdev→dma_buf_root。一种是进程级,每个进程都有独立的一颗,根节点kctx→kprcs→dma_buf_root。gpu kmd代码通过函数kbase_add_dma_buf_usage和kbase_remove_dma_buf_usage达到增删rbtree节点,从而达到统计内存的目的。
kbase_add_dma_buf_usage
// import
|-- kbase_mem_import
|-- kbase_mem_from_umm
|-- kbase_mem_umm_map_attachment
|-- kbase_add_dma_buf_usage
// ioctl KBASE_IOCTL_STICKY_RESOURCE_MAP
|-- kbase_api_sticky_resource_map
|-- kbase_sticky_resource_acquire
|-- kbase_map_external_resource
|-- kbase_mem_umm_map
|-- kbase_mem_umm_map_attachmentkbase_remove_dma_buf_usage
// import
|-- kbase_mem_phy_alloc_put
|-- kbase_mem_kref_free
|-- kbase_remove_dma_buf_usage
// ioctl KBASE_IOCTL_STICKY_RESOURCE_UNMAP
|-- kbase_api_sticky_resource_unmap
|-- kbase_sticky_resource_release_force
|-- release_sticky_resource_meta
|-- kbase_unmap_external_resource
|-- kbase_mem_umm_unmap
|-- kbase_mem_umm_unmap_attachment
|-- kbase_remove_dma_buf_usagekbase_add_dma_buf_usage通过调用函数kbase_capture_dma_buf_mapping,传入不同的rbtree根节点以及需要插入的dma_buf结构体。
/* add dma_buf to device and process. */
unique_dev_dmabuf = kbase_capture_dma_buf_mapping(kctx, alloc->imported.umm.dma_buf,
&kbdev->dma_buf_root, &kbdev->dev_buf_node);
unique_prcs_dmabuf = kbase_capture_dma_buf_mapping(kctx, alloc->imported.umm.dma_buf,
&kctx->kprcs->dma_buf_root, &kctx->kprcs->proc_buf_node);函数kbase_capture_dma_buf_mapping通过将dma_buf指针作为红黑树的key,进行插入节点。若该dma_buf节点未添加到相应的rbtree中,则进行插入操作,否则将node中的变量import_count++,kbase_dma_buf node定义如下,其中used_count是我们添加的,为了前面提到的只统计当前应用进程自己申请的dma_buf。
/**
* struct kbase_dma_buf - Object instantiated when a dma-buf imported allocation
* is mapped to GPU for the first time within a process.
* Another instantiation is done for the case when that
* allocation is mapped for the first time to GPU.
*
* @dma_buf: Reference to dma_buf been imported.
* @dma_buf_node: Link node to maintain a rb_tree of kbase_dma_buf.
* @import_count: The number of times the dma_buf was imported.
* @used_count: The number of times the dmabuf used in this device.
*/
struct kbase_dma_buf {
struct dma_buf *dma_buf;
struct rb_node dma_buf_node;
u32 import_count;
u32 used_count;
};
前面讲到,若dma_buf在rbtree中已存在,则只对import_count++,但在gpu kmd中无法知道当前应用进程和其它应用进程share的情况,以及当前内存是否是自己申请的,故我们依靠设备树节点的import_count,将该值赋值给进程节点的used_count,这样我们就可以得知,若used_count为1,则表示是设备树种第一次添加这个节点,那么我们认为就是当前应用进程申请的内存。
if (unique_prcs_dmabuf) {
if (kctx->kprcs->proc_buf_node && kbdev->dev_buf_node) {
kctx->kprcs->proc_buf_node->used_count = kbdev->dev_buf_node->import_count;
}
}故我们在函数kbasep_gpu_memory_procs_egl看到有如下代码段,从而达到只统计当前应用进程申请的dma_buf的目的。
if (is_pss) {
// When the used_count is 1, means it's this process firstly use this dmabuf
if (buf_node->used_count == 1) {
total_size += buf_node->dma_buf->size;
}
} else {
// Calculate rss by sum up all dmabuf node
total_size += buf_node->dma_buf->size;
}我们平台O81A的儿童空间(com.xiaomi.kidspace)统计数据如下:
- 5 buffer数据

抓取数据时因发现我们平台Surface/Layer中BufferQueue是5 buffer轮转,和性能以及产品线显示同学对齐为解决性能问题引入,回退后可以切回4 buffer轮转。
https://gerrit.pt.mioffice.cn/c/platform/vendor/xiaomi/proprietary/display/custom/+/5293514
- 4 buffer数据

1.2.3 Qualcomm mtrack
1.2.3.1 GL内存统计
高通平台目前GL部分只上报了gl_unmapped部分内存,导致高通平台和我们相比会小非常多,通过分析高通平台的数据我们发现了这个问题,并获取了gl_mapped部分内存,将gl_unmmaped+gl_mapped部分内存加在一起后,基本上可以和我们平台对齐。
Applications Memory Usage (in Kilobytes):
Uptime: 186747929 Realtime: 188423451
** MEMINFO in pid 18684 [com.xiaomi.kidspace] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 76113 76040 24 42 77012 97856 81322 11999
Dalvik Heap 10216 5016 4996 13 11308 30817 6241 24576
Dalvik Other 2082 1752 56 1 2800
Stack 1920 1920 0 0 1924
Ashmem 444 0 0 0 1140
Gfx dev 3008 3008 0 0 3012
Other dev 142 0 140 0 652
.so mmap 6124 652 496 0 55032
.jar mmap 4498 0 1668 0 45292
.apk mmap 2332 56 1748 0 4956
.ttf mmap 467 0 0 0 4712
.dex mmap 32394 52 31964 0 34492
.oat mmap 340 0 4 0 13400
.art mmap 7652 4636 1996 85 23108
Other mmap 146 4 92 0 1160
EGL mtrack 97920 97920 0 0 97920
GL mtrack 1540 1540 0 0 1540
Unknown 1335 1016 268 1 1860
TOTAL 248815 193612 43452 142 381320 128673 87563 36575
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 11648 34416
Native Heap: 76040 77012
Code: 36640 158404
Stack: 1920 1924
Graphics: 102468 102472
Private Other: 8348
System: 11751
Unknown: 7092
TOTAL PSS: 248815 TOTAL RSS: 381320 TOTAL SWAP PSS: 142
Objects
Views: 363 ViewRootImpl: 1
AppContexts: 7 Activities: 1
Assets: 36 AssetManagers: 0
Local Binders: 49 Proxy Binders: 81
Parcel memory: 20 Parcel count: 83
Death Recipients: 3 WebViews: 0
SQL
MEMORY_USED: 500
PAGECACHE_OVERFLOW: 125 MALLOC_SIZE: 46
DATABASES
pgsz dbsz Lookaside(b) cache hits cache misses cache size Dbname
PER CONNECTION STATS
4 20 17 0 34 1 /data/user/0/com.xiaomi.kidspace/databases/stat_database
4 24 69 17 77 6 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics
4 8 0 0 0 (attached) temp
4 24 43 5 32 2 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics (2)
4 20 55 1 35 2 /data/user/0/com.xiaomi.kidspace/databases/filetransfer_database
4 24 23 0 49 2 /data/user/0/com.xiaomi.kidspace/databases/mitu-media
4 8 0 0 0 (attached) temp
4 24 33 19 32 2 /data/user/0/com.xiaomi.kidspace/databases/mitu-media (2)
POOL STATS
cache hits cache misses cache size Dbname
0 35 35 /data/user/0/com.xiaomi.kidspace/databases/stat_database
24 129 153 /data/user/0/com.xiaomi.kidspace/databases/usage_statistics
1 36 37 /data/user/0/com.xiaomi.kidspace/databases/filetransfer_database
21 101 122 /data/user/0/com.xiaomi.kidspace/databases/mitu-media这里可以看到高通平台儿童空间GL mtrack是1540kb,EGL mtrack是97920kb,Gfx dev是3008kb,Graphics是这三者的和,共102468kb。
而通过如下两条命令dump gpu_unmapped和gpu_mapped内存得到1576960 byte(1540kb)和74862592 byte(73108kb)。其中gpu_unmapped正好是1540kb。
adb shell cat /sys/class/kgsl/kgsl/proc/18684/gpumem_unmapped
adb shell cat /sys/class/kgsl/kgsl/proc/18684/gpumem_mapped此外,我们可以通过如下命令校验高通平台GPU的内存用度。
adb shell cat /d/kgsl/proc/18684/mem我们dump了一份儿童空间的数据,然后将gpumem部分拆出来进行累加得到76439552 byte,正好等于gpu_unmapped和gpu_mapped的累加。
0000000000000000 0000000000000000 196608 1 --w---N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 16384 2 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 4096 3 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 4 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 5 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 6 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 20480 7 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 65536 8 --w-b-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 4096 9 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 196608 10 --w---N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 16384 11 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 4096 12 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 13 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 14 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 15 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 32768 16 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 65536 17 --w-b-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 4096 18 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 7274496 19 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 131072 20 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 65536 22 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 65536 23 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 24 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 25 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 131072 26 --wlb-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 27 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 65536 28 --w-b-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 262144 29 --wlb-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 8192 30 --w-b-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 4096 31 --w-b-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 4096 32 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 33 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 34 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 35 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 786432 36 --wlb-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 262144 37 --wlb-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 16384 38 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 39 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 40 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 131072 41 --wlb-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 65536 42 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 43 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 44 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 131072 45 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 46 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 131072 47 --wlb-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 4096 48 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 8192 49 --w-b-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 50 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 14483456 51 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 52 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 8192 53 --w-b-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 54 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 3145728 55 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 56 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 8192 57 --w-b-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 58 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 8192 59 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 4096 60 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 32768 61 --w-b-Y--- gpumem arraybuffer 0 1 0 0 0
0000000000000000 0000000000000000 131072 62 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 63 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 4096 64 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 65 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 66 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 67 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 68 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 69 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 70 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 71 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 16384 72 --w-b-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 16384 73 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 74 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 4096 75 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 65536 76 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 958464 77 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 14483456 78 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 65536 80 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 4096 81 --w-b-Y--- gpumem arraybuffer 0 1 0 0 0
0000000000000000 0000000000000000 4096 82 --w-b-Y--- gpumem arraybuffer 0 1 0 0 0
0000000000000000 0000000000000000 65536 83 --w-b-Y--- gpumem arraybuffer 0 1 0 0 0
0000000000000000 0000000000000000 2162688 84 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 86 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 87 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 88 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 89 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 90 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 91 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 92 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 93 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 94 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 95 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 96 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 97 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 98 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 99 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 100 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 101 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 8192 102 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 103 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 104 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 4096 105 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 106 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 107 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 108 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 131072 109 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 16384 110 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 112 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 113 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 16384 114 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 116 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 117 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 118 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 65536 119 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 65536 120 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 65536 121 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 122 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 65536 123 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 65536 124 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 4096 125 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 65536 126 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 917504 127 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 14483456 128 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 65536 129 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 131072 130 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 65536 131 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 4096 132 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 133 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 134 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 135 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 8192 136 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 131072 137 --wlb-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 138 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 8192 139 --w-b-Y--- gpumem command 0 1 0 0 0
0000000000000000 0000000000000000 65536 140 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 786432 142 --wlb-Y--- gpumem gl 0 1 0 0 0
0000000000000000 0000000000000000 3145728 143 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 144 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 145 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 146 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 147 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 901120 148 --wlb-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 8192 149 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 1769472 150 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 909312 151 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 65536 152 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 1769472 153 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 8192 154 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 397312 155 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 156 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 1769472 157 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 158 --w-b-N--- gpumem any(0) 0 0 0 0 0
0000000000000000 0000000000000000 4096 159 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 160 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 161 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 4096 162 ----b-N--- gpumem gl 0 0 0 0 0
0000000000000000 0000000000000000 405504 163 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 413696 165 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 166 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 421888 169 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 170 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 430080 173 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 174 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 372736 177 --wlb-Y--- gpumem texture 0 1 0 0 0
0000000000000000 0000000000000000 4096 178 --w-b-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 4096 180 --w-b-Y--- gpumem any(0) 0 1 0 0 0
0000000000000000 0000000000000000 65536 181 --wlb-N--- gpumem texture 0 0 0 0 0
0000000000000000 0000000000000000 65536 183 --wlb-N--- gpumem texture 0 0 0 0 0
1.2.3.2 EGL内存统计
高通mem节点拆出ion节点,有如下ion内存,累加起来正好是100270080 byte(97920kb),和EGL mtrack可以匹配上。
0000000000000000 0000000000000000 25067520 21 --wLb-N--- ion egl_surface 2835 0 1 1 41870
0000000000000000 0000000000000000 25067520 85 --wLb-N--- ion egl_surface 6120 0 1 1 41872
0000000000000000 0000000000000000 25067520 111 --wLb-N--- ion egl_surface 6120 0 1 1 41876
0000000000000000 0000000000000000 25067520 115 --wLb-N--- ion egl_surface 6120 0 1 1 41874高通平台N81A的儿童空间(com.xiaomi.kidspace)统计数据如下:

surfaceflinger统计数据如下:

从surfaceflinger的数据我们可以看到,高通平台ION有两部分数据,其它应用进程申请的,给到surfaceflinger使用的,以及surfaceflinger自己申请的,也即上面表格的shared和allocated部分,而EGL mtrack中只统计了allocated部分,我们从dma_buf dump的数据可以验证这个结论。那么高通(N81A)上EGL mtrack的做法和我们平台的做法是一致的。
O81A和N81A在儿童空间首页内存对比情况如下。

抓取数据文件如下:
此外针对产品线的问题单BUGOS2-452679,也请相应的同学帮忙抓取了对比数据,如下图。

这里可以看到Xring和Qualcomm两个平台,Graphics内存基本上在一个量级,不存在内存泄漏或者平台占用过大等相关问题。
1.2.4 MTK mtrack
MTK平台同样采用了mali gpu,GL mtrack部分和我们一样,都是读取的kmd统计的kctx的used_pages。MTK同样新增了一个proc节点/proc/mtk_mali/gpu_memory。
mali0 8894
kctx-0xe7b9a000 4663 1689
kctx-0xe7b49000 3793 1684但EGL mtrack部分,这里和我们的做法存在差异。EGL mtrack采用了和dma_buf dump类似的方式,新增了一个新的节点/proc/dma_heap/rss_pid
cat /proc/dma_heap/rss_pid113 PID PSS(KB) RSS(KB)
744 103996 103996
-----EGL memtrack data end
--sum: userspace_pss:103996 KB rss:103996 KB
--sum: kernel rss: 74180 KB
pid table:
pid:744 name:surfaceflingerMTK Memtrack具体实现参照如下代码链接。
节点/proc/dma_heap/rss_pid实现原理和dma_buf dump类似,可以参照如下文档。
MTK实现rss_pid节点的源码如下。
1.2.5 平台实现方式总结
1.2.5.1 EGL mtrack
举例说明,系统有进程A、进程B、进程C,假设这里共8块DMA_BUF,大小均为12mb。

进程Buffer申请情况:
进程A:DMA_BUF0、DMA_BUF1、DMA_BUF2
进程B:DMA_BUF3、DMA_BUF4
进程C:DMA_BUF5、DMA_BUF6、DMA_BUF7
进程Buffer使用情况:
进程A:DMA_BUF0、DMA_BUF1、DMA_BUF2
进程B:DMA_BUF1、DMA_BUF3、DMA_BUF4、DMA_BUF5
进程C:DMA_BUF2、DMA_BUF4、DMA_BUF5、DMA_BUF6、DMA_BUF7
那么各平台统计的EGL mtrack内存使用情况通过如下方式计算。
Xring(统计当前应用进程自己申请并使用的):
进程A:DMA_BUF0+DMA_BUF1+DMA_BUF2 = 36mb
进程B:DMA_BUF3+DMA_BUF4 = 24mb
进程C:DMA_BUF5+DMA_BUF6+DMA_BUF7 = 36mb
整机:DMA_BUF0+DMA_BUF1+DMA_BUF2+DMA_BUF3+DMA_BUF4+DMA_BUF5+DMA_BUF7 = 96mb
Qualcomm(统计当前应用进程自己申请并使用的): 和Xring平台结果一样。
1.2.5.2 GL mtrack
对于GL mtrack部分,我们前面已经做了很多说明,相信大家已经有了一个全貌的了解。总结下来就是:
🌟 我们和MTK平台都采用mali的gpu,那么我们的GL mtrack统计方式是一致的,但高通平台只统计了gpu_umapped部分内存,导致GL mtrack非常小,矫正后和我们的量级也是一样的。
更多内存分析细节参见:O2S/O80 GPU内存分析工具、GL MTRACK澄清建议总结、O81A临时版本内存拆解数据报告
串讲录屏
https://xiaomi.f.mioffice.cn/minutes/obk43sy13si7y95xjt6b2ook