基础介绍

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

我们平台和高通一样,都是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
  2. 销毁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,申请memoryvkCreateImage // 创建VkImagevkGetImageMemoryRequirements // 获取image memory相关信息vkAllocateMemory // 申请memoryvkBindImageMemory // 绑定memory到TexturevkMapMemory // map memory,获取CPU VA,上传纹理数据vkUnmapMemory // unmap memory 2. Draw command中使用使用TexturevkCreateImageView // 创建VkImageViewvkCreateSampler // 创建VkSamplervkUpdateDescriptorSets // 更新VkDescriptorSetvkCmdBindDescriptorSets // 绑定纹理到当前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. 创建fbovkCreateAndroidSurfaceKHR // 创建VkSurfaceKHR,传入ANativeWindowvkCreateSwapchainKHR // 创建VkSwapchainKHR,从ANativeWindow中BufferQueue dequeue所有buffer,创建对应的VkImagevkGetSwapchainImagesKHR // 获取当前swapchain中所有VkImage,创建VkImageViewvkCreateImageView // 创建VkImageViewvkCreateFramebuffer // 创建VkFrameBuffervkCreateRenderPass // 创建VkRenderPass 2. 使用fbovkCmdBeginRenderPass // 函数入参VkRenderPassBeginInfo包含当前renderpass需要渲染 的fbovkCmdEndRenderPass // 结束当前renderpass,也即当前renderpass对fbo的渲染 command recording结束 3. 销毁fbo,释放memoryvkDestroyRenderPass // 销毁renderpassvkDestroyFramebuffer // 销毁fbovkDestroyImageView // 销毁VkImageViewvkDestroySwapchainKHR // 销毁VkSwapchainKHRvkDestroySurfaceKHR // 销毁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占用是132593*4kb = 530372kb,79404同样也是page页数,表示当前整机的EGL mtrack占用是79404*4kb = 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,只是为了标识当前要统计的内存是不是由当前应用进程首次import到gpu的。对于如何识别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轮转。

  • 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在儿童空间首页内存对比情况如下。

[嵌入表格]

抓取数据文件如下:

[附件: R5Bsb5biBoMTXoxKW6UcKZKgnPc]

[附件: DGZ8bECq6o3etMxGqrecI4cmntb]

[附件: WeCpbxaA0oDPYtxLSGUcTUoYnlc]

此外针对产品线的问题单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类似,可以参照如下文档。

dmabuf_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平台结果一样。

🏆 MTK: DMA_BUF使用进程个数: DMA_BUF0:1,DMA_BUF1:2,DMA_BUF2:2,DMA_BUF3:1,DMA_BUF4:2,DMA_BUF5:2,DMA_BUF6:1,DMA_BUF7:1, 进程A:DMA_BUF0+DMA_BUF1/2+DMA_BUF2/2 = 24mb 进程B:DMA_BUF1/2+DMA_BUF3+DMA_BUF4/2+DMA_BUF5/2 = 30mb 进程C:DMA_BUF2/2+DMA_BUF4/2+DMA_BUF5/2+DMA_BUF6+DMA_BUF7 = 42mb 整机:进程A+进程B+进程C = 96m

1.2.5.2 GL mtrack

对于GL mtrack部分,我们前面已经做了很多说明,相信大家已经有了一个全貌的了解。总结下来就是:

🌟 我们和MTK平台都采用mali的gpu,那么我们的GL mtrack统计方式是一致的,但高通平台只统计了gpu_umapped部分内存,导致GL mtrack非常小,矫正后和我们的量级也是一样的。

更多内存分析细节参见:O80 GPU内存分析工具GL MTRACK澄清建议总结O81A临时版本内存拆解数据报告

串讲录屏

[附件: Diiibsb5KomlAFxLCu2cPxhCnUe]

https://xiaomi.f.mioffice.cn/minutes/obk43sy13si7y95xjt6b2ook