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 = 4

MemtrackType::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平台的一个优化点,修改链接如下。

https://source-v.dun.mi.com/opengrok-v/xref/mivendor_v_mt6899/external/skia/src/gpu/vk/VulkanAMDMemoryAllocator.cpp

我们平台和高通一样,都是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)

图形渲染使用的顶点数据和索引数据,由应用上传,控制渲染区域和渲染顺序

  1. 创建VBO/IBO,申请memory

vkCreateBuffer // 创建VBO或者IBO

vkGetBufferMemoryRequirements // 获取buffer memory相关信息

vkAllocateMemory // 申请memory

vkBindBufferMemory // 绑定memory到VBO或者IBO

vkMapMemory // map memory,获取CPU VA,上传顶点数据或者索引数据

vkUnmapMemory // unmap memory

  1. VBO/IBO

vkCmdBindVertexBuffers // 绑定VBO到当前command recording

vkCmdBindIndexBuffer // 绑定IBO到当前command recording

  1. 销毁VBO/IBO,释放memory

vkDestroyBuffer // 销毁VBO或者IBO

vkFreeMemory // 释放memory

  1. 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

  1. 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)。

  1. 创建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页数添加到kctxused_pages以及kctxkbdevmemdev.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。其值通过函数从根节点为kctxkprcsdma_buf_root的红黑树中获取。

tgid_info->pss_pages = kbasep_gpu_memory_procs_egl(kbdev, &kctx->kprcs->dma_buf_root, true);​

这里需要注意,除了进程级别的红黑树,我们还有一颗设备级别的红黑树,用来统计整机的EGL部分内存,根节点是kbdevdma_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_bufsize,最终得到进程级或者设备级的EGL内存用度,此外这里需要注意的是变量is_pss,这里不是纯粹意义上的pss,只是为了标识当前要统计的内存是不是由当前应用进程申请的。对于如何识别rbnode中保存的dma-buf是不是当前进程自己申请的,有一个小技巧,接下来介绍dma-buf红黑树的流程中说明。

上面讲到gpu kmd中有两种类型的dma_buf统计的数,一种是设备级,一个dev只有一颗,根节点kbdevdma_buf_root。一种是进程级,每个进程都有独立的一颗,根节点kctxkprcsdma_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_attachment​
kbase_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_usage

kbase_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:surfaceflinger​

MTK Memtrack具体实现参照如下代码链接。

节点/proc/dma_heap/rss_pid实现原理和dma_buf dump类似,可以参照如下文档。

[基础知识]安卓中的libdmabufinfo

MTK实现rss_pid节点的源码如下。

https://source-v.dun.mi.com/opengrok-v/xref/mivendor_v_mt6899/kernel/kernel_device_modules-6.6/drivers/dma-buf/heaps/mtk_heap_debug.c

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