Android 埋点信息分析——内存篇

news2024/12/24 2:56:19

源码基于:Android U

0. 前言

在前一篇《Android statsd 埋点简析》一文中简单剖析了Android 埋点采集、传输的框架,本文在其基础对埋点信息进行解析,来看下Android 中埋下的内存信息有哪些。

1. 通过代码剖析google 埋点内容

1.1 PROCESS_MEMORY_STATE

frameworks/base/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java

    int pullProcessMemoryStateLocked(int atomTag, List<StatsEvent> pulledData) {
        List<ProcessMemoryState> processMemoryStates =
                LocalServices.getService(ActivityManagerInternal.class)
                        .getMemoryStateForProcesses();
        for (ProcessMemoryState processMemoryState : processMemoryStates) {
            final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
                    processMemoryState.pid);
            if (memoryStat == null) {
                continue;
            }
            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, processMemoryState.uid,
                    processMemoryState.processName, processMemoryState.oomScore, memoryStat.pgfault,
                    memoryStat.pgmajfault, memoryStat.rssInBytes, memoryStat.cacheInBytes,
                    memoryStat.swapInBytes, -1 /*unused*/, -1 /*unused*/, -1 /*unused*/));
        }
        return StatsManager.PULL_SUCCESS;
    }

getMemoryStateForProcesses函数:读取每个应用进程;

readMemoryStatFromFilesystem函数:读取/proc/<pid>/stat节点,解析pgfault(9)、pgmajfault(11)、rssInbytes(23) 数据;

统计数据有:

  • uid
  • processName
  • oomScore
  • pgmajfault
  • rss
  • cache(memcg)
  • swap (memcg)

1.2 PROCESS_MEMORY_HIGH_WATER_MARK

    int pullProcessMemoryHighWaterMarkLocked(int atomTag, List<StatsEvent> pulledData) {
        List<ProcessMemoryState> managedProcessList =
                LocalServices.getService(ActivityManagerInternal.class)
                        .getMemoryStateForProcesses();
        for (ProcessMemoryState managedProcess : managedProcessList) {
            final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
            if (snapshot == null) {
                continue;
            }
            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid,
                    managedProcess.processName,
                    // RSS high-water mark in bytes.
                    snapshot.rssHighWaterMarkInKilobytes * 1024L,
                    snapshot.rssHighWaterMarkInKilobytes));
        }
        // Complement the data with native system processes
        SparseArray<String> processCmdlines = getProcessCmdlines();
        managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
        int size = processCmdlines.size();
        for (int i = 0; i < size; ++i) {
            ...
        }
        // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
        SystemProperties.set("sys.rss_hwm_reset.on", "1");
        return StatsManager.PULL_SUCCESS;
    }

该函数主要查询所有应用进程和native 进程的内存信息。

getMemoryStateForProcesses函数:读取每个应用进程;

readMemorySnapshotFromProcfs函数:读取/proc/<pid>/status节点,解析UidVmHWMVmRssRssAnonRssShmemVmSwap 数据。通过判定/proc/<pid>/status节点中是否有RssAnonRssShmemVmSwap数据排除 kernel 进程;

最后通过设置 prop 唤醒 rss_hwm_reset 程序,将VmHWM 清除。

统计数据有:

  • uid
  • processName / cmdline (native是cmdline)
  • VmHWM

1.3 PROCESS_MEMORY_SNAPSHOT

同上 HWM,统计每一个应用进程和native 进程的内存快照,区别在于这里另外统计了每个进程的 GPU 使用量:sys/fs/bpf/map_fpuMem_gpu_mem_total_map

统计数据有:

  • uid
  • processName / cmdline(native是cmdline)
  • pid
  • oomScore
  • rss
  • rss_anon
  • swap
  • rss_anon + swap
  • gpu memory
  • hasForegroundServices (native 为false)
  • rss_shmem

1.4 SYSTEM_ION_HEAP_SIZE

    int pullSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
        final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, systemIonHeapSizeInBytes));
        return StatsManager.PULL_SUCCESS;
    }

解析 sys/kernel/debug/ion/heaps/system 节点total 部分的数据。

1.5 ION_HEAP_SIZE

    int pullIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
        int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, ionHeapSizeInKilobytes));
        return StatsManager.PULL_SUCCESS;
    }

调用 Debug.getIonHeapsSizeKb,详细可以查看 android_os_Debug.cpp

解析/sys/kernel/ion/total_heaps_kb

1.6 PROCESS_SYSTEM_ION_HEAP_SIZE

    int pullProcessSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
        List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
        for (IonAllocations allocations : result) {
            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, getUidForPid(allocations.pid),
                    readCmdlineFromProcfs(allocations.pid),
                    (int) (allocations.totalSizeInBytes / 1024), allocations.count,
                    (int) (allocations.maxSizeInBytes / 1024)));
        }
        return StatsManager.PULL_SUCCESS;
    }

readProcessSystemIonHeapSizesFromDebugfs解析sys/kernel/debug/ion/heaps/system 节点进程部分数据。

1.7 PROCESS_DMABUF_MEMORY

    int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) {
        KernelAllocationStats.ProcessDmabuf[] procBufs =
                KernelAllocationStats.getDmabufAllocations();

        if (procBufs == null) {
            return StatsManager.PULL_SKIP;
        }
        for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) {
            pulledData.add(FrameworkStatsLog.buildStatsEvent(
                    atomTag,
                    procBuf.uid,
                    procBuf.processName,
                    procBuf.oomScore,
                    procBuf.retainedSizeKb,
                    procBuf.retainedBuffersCount,
                    0, /* mapped_dmabuf_kb - deprecated */
                    0, /* mapped_dmabuf_count - deprecated */
                    procBuf.surfaceFlingerSizeKb,
                    procBuf.surfaceFlingerCount
            ));
        }
        return StatsManager.PULL_SUCCESS;
    }

getDmabufAllocations函数主要是调用 dmabufinfo.cpp 中ReadProcfsDmaBufs函数获取进程dmabuf 信息。

统计数据有:

  • uid
  • cmdline
  • oomScore
  • total (KB)
  • inode count
  • surfaceflinger size (KB)
  • surfaceflinger inode cnt

1.8 SYSTEM_MEMORY

    int pullSystemMemory(int atomTag, List<StatsEvent> pulledData) {
        SystemMemoryUtil.Metrics metrics = SystemMemoryUtil.getMetrics();
        pulledData.add(
                FrameworkStatsLog.buildStatsEvent(
                        atomTag,
                        metrics.unreclaimableSlabKb,           //meminfo.SUnreclaim
                        metrics.vmallocUsedKb,                 //meminfo.VmallocUsed
                        metrics.pageTablesKb,                  //meminfo.PageTables
                        metrics.kernelStackKb,                 //meminfo.KernelStack
                        metrics.totalIonKb,
                        metrics.unaccountedKb,
                        metrics.gpuTotalUsageKb,
                        metrics.gpuPrivateAllocationsKb,
                        metrics.dmaBufTotalExportedKb,
                        metrics.shmemKb,                       //meminfo.Shmem
                        metrics.totalKb,                       //meminfo.MemTotal
                        metrics.freeKb,                        //meminfo.MemFree
                        metrics.availableKb,                   //meminfo.MemAvailable
                        metrics.activeKb,                      //meminfo.Active
                        metrics.inactiveKb,                    //meminfo.Inactive
                        metrics.activeAnonKb,                  //meminfo.Active(anon)
                        metrics.inactiveAnonKb,                //meminfo.Inactive(anon)
                        metrics.activeFileKb,                  //meminfo.Active(file)
                        metrics.inactiveFileKb,                //meminfo.Inactive(file)
                        metrics.swapTotalKb,                   //meminfo.SwapTotal
                        metrics.swapFreeKb,                    //meminfo.SwapFree
                        metrics.cmaTotalKb,                    //meminfo.CmaTotal
                        metrics.cmaFreeKb));                   //meminfo.CmaFree
        return StatsManager.PULL_SUCCESS;
    }

totalIonKb:统计/sys/kernel/dmabuf/buffers下所有定义在 /dev/dma_heap 的 exporter的总大小;如果不支持dmabuf,那就统计/sys/kernel/ion/total_heaps_kb节点;

gpuTotalUsageKb:解析节点 /sys/fs/bpf/map_gpuMem_gpu_mem_total_map

gpuPrivateAllocationsKb:获取GPU private

dmaBufTotalExportedKb:统计/sys/kernel/dmabuf/buffers下dmabuf 总和;

unaccountedKb:meminfo.MemTotal - accountedKb;

accountedKb 包括:

meminfo.MemFree + zram + meminfo.Buffers + meminfo.active + meminfo.inactive + meminfo.Unevictable + meminfo.SUnreclaim + meminfo.KReclaimable + meminfo.VmallocUsed + meminfo.PageTables + meminfo.KernelStack + dmaBufTotalExportedKb + gpuPrivateAllocationsKb

1.9 VMSTAT

    int pullVmStat(int atomTag, List<StatsEvent> pulledData) {
        ProcfsMemoryUtil.VmStat vmStat = ProcfsMemoryUtil.readVmStat();
        if (vmStat != null) {
            pulledData.add(
                    FrameworkStatsLog.buildStatsEvent(
                            atomTag,
                            vmStat.oomKillCount));
        }
        return StatsManager.PULL_SUCCESS;
    }

只统计 oom_kill 的次数。

2. 通过看板剖析 google 埋点内容

2.1 RSS hwm

结合代码第 1.2 节应该是统计每个进程的 hwm,其中包含顺序、倒序显示,显示的数值应该是平均值 ± 体现最大值和最小值。

Metric details 有可能显示更多的分位数信息。

从看板数据来看,三方的应用占用内存较大,例如 com.tencent.ig 和 com.roblox.client,后期内存健康优化可以考虑三方应用给系统带来的压力,也需要确定应用在后台时的内存占用。这里可以优先查看这些进程的anon RSS + swap 的内存占用,确定是否存在内存泄漏。

2.2 P95 anon RSS + swap

结合代码第 1.3 节应该是统计每个进程的 anon RSS + swap 高于P95 的分布。

Metric details 可能有更多分位数的分布。

从看板数据来看,三方的应用占用匿名页内存较大,可能存在内存泄露的可能。可以查看details

anon RSS + swap 中包含leaked、unused 内存,这些都会swap out 到zram,需要限制这个阈值。

2.3 ION heap Size

这里应该统计的是dmabuf,结合代码第 1.8 节。

Distribution details 中可能有每个进程的 dmabuf 的分布。

从看板数据来看,有还有1% 的进程使用DMABUF超过了910M,需要通过details 进行细细确认进程占用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1987269.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全之sql靶场(11-23)

sql靶场&#xff08;11-23&#xff09; 目录 第十一关&#xff08;post注入&#xff09; 第十二关 第十三关 第十四关 第十五关 第十六关 第十七关 第十八关 第十九关 第二十关 第二十一关 第二十二关 第二十三关 第十一关&#xff08;post注入&#xff09; 查看…

echart 制作 Grafana 面板之仪表盘

目录 前言准备工作实现代码代码详解总结相关阅读 前言 Grafana 是一个开源的可视化监控工具&#xff0c;支持多种数据源&#xff0c;并且可以创建丰富的仪表盘。ECharts 是一个强大的开源数据可视化库&#xff0c;通过结合这两者&#xff0c;我们可以创建自定义的仪表盘&…

GPIO输出控制之LED闪烁、LED流水灯以及蜂鸣器应用案例

系列文章目录 STM32之GPIO&#xff08;General Purpose Input/Output&#xff0c;通用型输入输出&#xff09; 文章目录 系列文章目录前言一、LED和蜂鸣器简介1.1 LED1.2 蜂鸣器1.3 面包板 二、LED硬件电路2.1 低电平驱动电路2.2 高电平驱动电路 三、蜂鸣器硬件电路3.1 PNP型三…

使用idea 把一个git分支的部分提交记录合并到另一个git分支上

一、需求 需要将A&#xff08;合并分支&#xff09;分支上的提交记录中的某一次&#xff08;或几次&#xff09;提交合并到B&#xff08;被合并分支&#xff09;分支上 说明&#xff1a;熟练使用idea可以直接看下图即可&#xff0c;若不熟悉可以根据下列步骤进行操作&#xf…

富士乐施5070-V打印机驱动安装

富士乐施5070-V打印机驱动安装 特指打印A3纸张需求&#xff0c;即驱动中能够选择纸张类型&#xff08;安装选择305df驱动只能打印A4类型&#xff09; 富士乐施打印机驱动下载网址&#xff1a; https://m3support-fb.fujifilm-fb.com.cn/driver_downloads/www/ 安装流程&…

C#自定义快捷操作键的实现 - 开源研究系列文章

这次想到应用程序的快捷方式使用的问题。 Windows已经提供了API函数能够对窗体的热键进行注册&#xff0c;然后就能够在窗体中使用这些注册的热键进行操作了。于是笔者就对这个操作进行了整理&#xff0c;将注册热键操作写成了帮助类&#xff0c;并且用此博文来记录这个使用DEM…

【教程】linux-ubuntu安装并配置docker

linux-ubuntu安装并配置docker 一、在线安装1.卸载历史版本情况一&#xff1a;如果之前是手动安装的话&#xff0c;一步一步卸载情况二&#xff1a;通过APT安装 2.使用APT安装&#xff08;推荐&#xff09;(1) 添加https软件包&#xff08;2&#xff09;在apt源中添加docker软件…

kubernets学习笔记——使用kubeadm构建kubernets集群及排错

使用kubeadm构建kubernets集群 一、准备工作1、repo源配置&#xff1a;阿里巴巴开源镜像源2、更新软件包并安装必要的系统工具3、同步时间4、禁用selinux5、禁用交换分区swap6、关闭防火墙 二、安装docker-ce、docker、cri-docker1、安装docker-ce2、开启内核转发&#xff0c;转…

【学习笔记】A2X通信的协议(四)- A2X PC5通信(二)

目录 6.1.2.4 A2X PC5单播链接释放程序 6.1.2.4.1 概述 6.1.2.4.2 发起UE启动A2X PC5单播链接释放程序 6.1.2.4.3 目标UE接受的A2X PC5单播链接释放程序 6.1.2.4.4 发起UE完成的A2X PC5单播链接释放程序 6.1.2.4.5 异常情况 6.1.2.4.5.1 发起UE的异常情况 6.1.2.5 A2X …

Linux——嵌入式学习——C++学习(1)

一、环境配置 由于之前安装过QT&#xff0c;所以直接连接网络之后&#xff0c;运行 运行之后检查安装版本 接着用qt的使用步骤 创建工程即可 三、 1、注释 单行注释&#xff1a;// 多行注释/* */ 2、auto 自动推导类型 2.1声明变量 使…

Linux小组件:makefile

引言&#xff1a; 我们在Windows下编程时使用vs这种集成开发环境&#xff0c;里面什么编译运行调试清理等等服务都被一连串打包好了。在Linux下怎么实现呢&#xff1f;使用我们伟大的makefile&#xff01; makefile是Linux下的一个工具&#xff0c;通过文本编辑器vim对文件内…

Linux内核编程(十一)设备模型

本文目录 一、知识点1. 设备模型2. sysfs 文件系统3. kobject、kset设备模型框架 二、kobject实验1. 创建kobject2. 释放kobject★示例 三、kset实验1. 创建kset2. 注销kset★示例 四、引用计数器1. 概念2. 为什么要引入引用计数器&#xff1f;3. 常用函数&#xff08;1&#x…

【Nuxt】发送请求

概述 以下方式只能在 setup / 生命周期钩子 里面使用。 useFetch 下面的 API / hooks 具体用法查看官网文档。 const BASE_URL http://codercba.com:9060/juanpi/api;// 1. $fetch server and client // $fetch(BASE_URL /homeInfo, { // method: GET // }).then(res &…

python爬虫04 | Reuqests库快速入门,干穿urllib

文章目录 Requests库简介提出请求响应内容二进制响应内容JSON 响应内容原始响应内容自定义标头更复杂的 POST 请求POST 多部分编码的文件响应状态代码响应标头Cookie重定向和历史记录超时错误和异常 Ending Requests库简介 什么是Requests库 Requests是一个简单易用的HTTP库&…

分享一个基于SpringBoot和Vue的闲置物品交易与物品租赁平台(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

人工智能计算机视觉先锋——OpenCv 的颜色检测

红色 在计算机的世界里&#xff0c;只有 0 或者1&#xff0c;如何让计算机认识颜色是计算机视觉工作者首先需要考虑的事情&#xff0c;我们知道整个世界的颜色虽然五彩缤纷&#xff0c;但是都是3种原色彩合成的&#xff08;R G B&#xff09;&#xff0c;有了&#xff08;R G …

C语言 | Leetcode C语言题解之第327题区间和的个数

题目&#xff1a; 题解&#xff1a; #define FATHER_NODE(i) (0 (i) ? -1 : ((i) - 1 >> 1)) #define LEFT_NODE(i) (((i) << 1) 1) #define RIGHT_NODE(i) (((i) << 1) 2)/* 优先队列&#xff08;小根堆&#xff09;。 */ typedef s…

数字人解决方案——音频驱动机器人

音频集成 机器人 标志着 人工智能&#xff08;AI&#xff09;。 想象一下&#xff0c;机器人可以通过视觉和听觉导航并与周围环境互动。音频驱动的机器人使这成为可能&#xff0c;提高了它们更高效、更直观地执行任务的能力。这一发展可能会影响到各个领域&#xff0c;包括家庭…

解决PermissionError: [Errno 13] Permission denied: “xx“报错

这个报错我是使用 shutil.copy(src_file, dst_file) 语句产生的&#xff0c;因此有些问题我会围绕此句代码来进行解决&#xff0c;如果有更好的建议&#xff0c;欢迎积极留言。 目录 1.路径拼写错误&#xff0c;建议使用绝对路径 2.此文件正在使用&#xff0c;关闭当前打开的…

vba 保存word里面的图片_1分钟批量处理100张图片,有Word在

天下苦Word久矣&#xff01;Word不仅是个码字工具&#xff0c;还是个排版工具&#xff0c;而Word在排版方面经常遇到的问题&#xff0c;恐怕说个三天三夜都说不完&#xff01; 好不容易做完了100页的活动方案&#xff0c;交到处女座上司那里&#xff0c;他告诉我&#xff1a;“…