Android dmabuf_dump 命令详解

版本基于:Android 16 (W)

1. 命令格式

bash 复制代码
shift:/ # dmabuf_dump -h
Usage: dmabuf_dump [-abh] [PID] [-o <raw|csv>]
-a       show all dma buffers (ion) in big table, [buffer x process] grid
-b       show DMA-BUF per-buffer, per-exporter and per-device statistics
-o       [raw][csv] print output in the specified format.
-h       show this help
         If PID is supplied, the dmabuf information for that process is shown.
         Per-buffer DMA-BUF stats do not take an argument.
  • -o: 可选,优先了解该选项,这是dmabuf_dump 命令的输出格式,raw 或 csv,默认为 raw 。代码中会根据该选项来决定输出Helper 对象是CsvOutputRawOutput
  • **-a: **可选,以表的形式显示dmabuf 信息,可以与 -o 配合使用;
  • -b: 可选,解析 /sys/kernel/dmabuf/buffers 下每个buffer 的export_name 和 size, 不排序;
  • **[pid]: **可选,指定固定的进程 PID,不能与 -a 或 -b 同时使用;
  • 当没有 -a-b 选项时,将以进程维度,分别输出每个进程占用的 dmabuf 的每个inode 占用的内存信息,包括 PSS、RSS、nr_procs、export name。当然如果此时有 [pid] 参数,则只输出该 PID 的dmabuf 内存信息;

对应代码后框架如下:

2. 源码剖析

依赖节点:

  • /sys/kernel/dmabuf/buffers/<inode>/
  • /proc/<PID>/fdinfo/<fd>
  • /proc/<PID>/maps

2.1 不带任何参数

2.1.1 输出信息格式

bash 复制代码
        cdsprpcd:2510
                  Name              Rss              Pss         nr_procs            Inode               Exporter
                system             4 kB             4 kB                1               56                  system
                system             4 kB             4 kB                1               57                  system
                system           256 kB           256 kB                1               58                  system
         PROCESS TOTAL           264 kB           264 kB
----------------------
   binder:2522_2:2522
                  Name              Rss              Pss         nr_procs            Inode               Exporter
                system            32 kB            16 kB                2              661                  system
                system            32 kB            16 kB                2              662                  system
                system            32 kB            16 kB                2              663                  system
                system            32 kB            16 kB                2              664                  system
                system            32 kB            16 kB                2              665                  system
         PROCESS TOTAL           304 kB           152 kB
----------------------
dmabuf total: 176932 kB kernel_rss: 4240 kB userspace_rss: 331332 kB userspace_pss: 172691 kB

将dmabuf 信息按照 PID 维度分离,记录每个 PID 不同的 inode 信息。

最后汇总总的内存分布:

  • **total:**统计 /sys/kernel/dmabuf/buffers 下所有inode size 之和;
  • **kernel_rss:**total 中除去用户层映射的部分,也就是unmapped size;
  • **userspace_rss:**用户层映射的 mapped RSS;
  • **userspace_pss:**用户层映射的mapped PSS;

2.1.2 ReadProcfsDmaBufs()

cpp 复制代码
main
|-->ReadProcfsDmaBufs         //vector<DmaBuffer>, 每个inode对应一个DmaBuffer
    |-->轮询/proc/下所有PID 目录,将pid传入下面两个函数
    |-->ReadDmaBufFdRefs
        |-->进入/proc/<PID>/fdinfo 目录,轮询每个<fd>
        |-->ReadDmaBufFdInfo       //读取<fd> 信息,解析每一行,存在exp_name信息,则认为是dmabuf
        |-->如果解析的inode为-1,则从/proc/<PID>/fd/<fd> 通过stat命令解析size
        |-->更新到vector<DmaBuffer>中
        |-->增加DmaBuffer 的fd 引用
    |-->ReadDmaBufMapRefs
        |-->读取/proc/<PID>/maps信息
        |-->确认每个vma,是否为 /dmabuf 开头,例如/dmabuf:system
        |-->增加DmaBuffer 的map 引用
        |-->ReadBufferExporter     //读取/sys/kernel/dmabuf/buffers/<inode>/exporter_name
        |-->ReadBufferSize         //读取/sys/kernel/dmabuf/buffers/<inode>/size
        |-->如果ReadBufferExporter 和ReadBufferSize 有任意一个失败,则使用vma的size

每个 /proc/<PID>/fdinfo/<fd> 的信息有:

bash 复制代码
pos:    0                       |    pos:    0
flags:  02000002                |    flags:  0200001
mnt_id: 8                       |    mnt_id: 11
ino:    4                       |    ino:    108582
size:   28672                   |
count:  3                       |
exp_name:       qcom,qseecom    |
name:   qcom,qseeco             |

左边带有 exp_name则表示该 inode 为 dmabuf。

cpp 复制代码
system/memory/libmeminfo/libdmabufinfo/dmabufinfo.cpp

/**
 * 该函数统计所有 dmabuf inode 信息,当dmabuf 已经使用/proc/<PID>/fdinfo/<fd> 已经创建好,
 * 所以通过/proc/<PID>/fdinfo/<fd> 基本能够统计所有活跃的dmabuf内存。然而,可能存在fd 刚被
 * close,而映射关系还没有解除,所以轮询一遍 /proc/<PID>/maps 确认是否有dmabuf 的vma 存在。
 *
 * 所以,这里轮询 /proc获取 PID,并调用两个函数:
 *   1. ReadDmaBufFdRefs() 统计/proc/<PID>/fdinfo/<fd> 信息;
 *   2. ReadDmaBufMapRefs() 统计/proc/<PID>/maps 中的dmabuf vma;
 */
bool ReadProcfsDmaBufs(std::vector<DmaBuffer>* bufs) {
    bufs->clear();

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
    if (!dir) {
        LOG(ERROR) << "Failed to open /proc directory";
        bufs->clear();
        return false;
    }

    struct dirent* dent;
    while ((dent = readdir(dir.get()))) {
        if (dent->d_type != DT_DIR) continue;

        int pid = atoi(dent->d_name);
        if (pid == 0) {
            continue;
        }

        if (!ReadDmaBufFdRefs(pid, bufs)) {
            LOG(ERROR) << "Failed to read dmabuf fd references for pid " << pid;
        }

        if (!ReadDmaBufMapRefs(pid, bufs)) {
            LOG(ERROR) << "Failed to read dmabuf map references for pid " << pid;
        }
    }

    return true;
}
cpp 复制代码
system/memory/libmeminfo/libdmabufinfo/dmabufinfo.cpp

//参数 procfs_path 默认为 /proc
bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
                             const std::string& procfs_path) {
    constexpr char permission_err_msg[] =
            "Failed to read fdinfo - requires either PTRACE_MODE_READ or root depending on "
            "the device kernel";
    static bool logged_permission_err = false;

    //确认 /proc/<PID>/fdinfo是否可以访问,下面将轮询读取该目录下所有的 fd
    std::string fdinfo_dir_path =
            ::android::base::StringPrintf("%s/%d/fdinfo", procfs_path.c_str(), pid);
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(fdinfo_dir_path.c_str()), &closedir);
    if (!dir) {
        // Don't log permission errors to reduce log spam on devices where fdinfo
        // of other processes can only be read by root.
        if (errno != EACCES) {
            PLOG(ERROR) << "Failed to open " << fdinfo_dir_path << " directory";
        } else if (!logged_permission_err) {
            LOG(ERROR) << permission_err_msg;
            logged_permission_err = true;
        }
        return false;
    }
    struct dirent* dent;
    while ((dent = readdir(dir.get()))) {
        int fd;
        if (!::android::base::ParseInt(dent->d_name, &fd)) { //fd都是整数
            continue;
        }

        // Set defaults in case the kernel doesn't give us the information
        // we need in fdinfo
        std::string name = "<unknown>";
        std::string exporter = "<unknown>";
        uint64_t count = 0;
        uint64_t size = 0;
        uint64_t inode = -1;
        bool is_dmabuf_file = false;

        auto fdinfo_result = ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count, &size, &inode,
                                              &is_dmabuf_file, procfs_path);
        if (fdinfo_result != OK) {
            if (fdinfo_result == NOT_FOUND) {
                continue;
            }
            // Don't log permission errors to reduce log spam when the process doesn't
            // have the PTRACE_MODE_READ permission.
            if (errno != EACCES) {
                LOG(ERROR) << "Failed to read fd info for pid: " << pid << ", fd: " << fd;
            } else if (!logged_permission_err) {
                LOG(ERROR) << permission_err_msg;
                logged_permission_err = true;
            }
            return false;
        }

        //确认是否为dmabuf fd
        if (!is_dmabuf_file) {
            continue;
        }

        //这里是兼容性,当发现idnode为-1 时,可能inode 缺失,通过stat的方式获取sb.st_info
        if (inode == static_cast<uint64_t>(-1)) {
            // Fallback to stat() on the fd path to get inode number
            std::string fd_path =
                    ::android::base::StringPrintf("%s/%d/fd/%d", procfs_path.c_str(), pid, fd);

            struct stat sb;
            if (stat(fd_path.c_str(), &sb) < 0) {
                if (errno == ENOENT) {
                  continue;
                }
                PLOG(ERROR) << "Failed to stat: " << fd_path;
                return false;
            }

            inode = sb.st_ino;
            // If root, calculate size from the allocated blocks.
            size = sb.st_blocks * 512;
        }

        //通过dmabuf 唯一标识inode,确认是否创建过DmaBuffer对象,无论是否存在,都增加fd 引用计数
        auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
                                [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
        if (buf != dmabufs->end()) {
            if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
            if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
            if (buf->count() == 0) buf->SetCount(count);
            buf->AddFdRef(pid);
            continue;
        }

        DmaBuffer& db = dmabufs->emplace_back(inode, size, count, exporter, name);
        db.AddFdRef(pid);
    }

    return true;
}
cpp 复制代码
system/memory/libmeminfo/libdmabufinfo/dmabufinfo.cpp

/**
 * 参数 procfs_path:默认为/proc
 * 参数dmabuf_sysfs_path:默认为 /sys/kernel/dmabuf/buffers
 */
bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
                              const std::string& procfs_path,
                              const std::string& dmabuf_sysfs_path) {
    std::string mapspath = ::android::base::StringPrintf("%s/%d/maps", procfs_path.c_str(), pid);
    std::ifstream fp(mapspath);
    if (!fp) {
        LOG(ERROR) << "Failed to open " << mapspath << " for pid: " << pid;
        return false;
    }

    // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
    // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
    auto account_dmabuf = [&](const android::procinfo::MapInfo& mapinfo) {
        // no need to look into this mapping if it is not dmabuf
        if (!FileIsDmaBuf(mapinfo.name)) {     //确认 vma 是否为dmabuf
            return;
        }

        //通过唯一的标识inode,确认是否已经创建好DmaBuffer 对象
        auto buf = std::find_if(
                dmabufs->begin(), dmabufs->end(),
                [&mapinfo](const DmaBuffer& dbuf) { return dbuf.inode() == mapinfo.inode; });

        //如果创建好了 DmaBuffer对象,增加map 引用计数
        if (buf != dmabufs->end()) {
            buf->AddMapRef(pid);
            return;
        }

        
        //如果没有创建DmaBuffer对象,尝试获取inode下的 exporter name
        std::string exporter;
        bool sysfs_stats = ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path);
        if (!sysfs_stats) {
            exporter = "<unknown>";
        }

        //尝试读取inode 下的 size,如果没有获得,则使用vma的大小
        //但此时可能产生误导,有可能vma 超过实际buffer size
        uint64_t size = 0;
        if (!sysfs_stats || !ReadBufferSize(mapinfo.inode, &size, dmabuf_sysfs_path)) {
            size = mapinfo.end - mapinfo.start;
        }

        DmaBuffer& dbuf = dmabufs->emplace_back(mapinfo.inode, size, 0, exporter, "<unknown>");
        dbuf.AddMapRef(pid);
    };

    for (std::string line; getline(fp, line);) {
        if (!::android::procinfo::ReadMapFileContent(line.data(), account_dmabuf)) {
            LOG(ERROR) << "Failed to parse " << mapspath << " for pid: " << pid;
            return false;
        }
    }

    return true;
}

2.2 带有PID 参数

通常带有 PID 参数是想要获取某个特定进程的 dmabuf 信息,所以不会跟 -a 或 -b 同时出现。

当不带任何参数时,会轮询 /proc/ 下所有 PID 进行解析。而带有 PID 选项,则无需轮询,直接通过函数 ReadDmaBufInfo() 进行解析。

2.2.1 输出信息格式

bash 复制代码
shift:/proc/1919/fdinfo # dmabuf_dump 2390
     mediaserver:2390
                  Name              Rss              Pss         nr_procs            Inode               Exporter
                system            32 kB            32 kB                1              501                  system
                system            32 kB            32 kB                1              505                  system
                system            32 kB            32 kB                1              510                  system
                system            32 kB            32 kB                1              512                  system
                system            32 kB            32 kB                1              661                  system
                system            32 kB            32 kB                1              662                  system
                system            32 kB            32 kB                1              663                  system
                system            32 kB            32 kB                1              664                  system
                system            32 kB            32 kB                1              665                  system
                system            16 kB            16 kB                1              666                  system
                system            16 kB            16 kB                1              667                  system
                system            16 kB            16 kB                1              668                  system
                system            32 kB            32 kB                1              670                  system
                system            32 kB            32 kB                1              671                  system
                system            32 kB            32 kB                1              672                  system
         PROCESS TOTAL           432 kB           432 kB
----------------------
dmabuf total: 176932 kB kernel_rss: 176500 kB userspace_rss: 432 kB userspace_pss: 432 kB

这里不过多解释,详细看上文第 2.1.1 节

2.2.1 ReadDmaBufInfo()

cpp 复制代码
system/memory/libmeminfo/libdmabufinfo/dmabufinfo.cpp

/**
 * 该函数用以解析某特定 PID 的dmabuf 信息,其实就是ReadProcfsDmaBufs()的一个PID分支
 *
 * 参数 read_fdrefs:默认为 true
 * 参数 procfs_path:默认为 /proc
 * 参数 dmabuf_sysfs_path:默认为/sys/kernel/dmabuf/buffers
 */
bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs,
                    const std::string& procfs_path, const std::string& dmabuf_sysfs_path) {
    //只解析一个 PID 信息,这里做一下clear
    dmabufs->clear();

    if (read_fdrefs) {
        if (!ReadDmaBufFdRefs(pid, dmabufs, procfs_path)) {
            LOG(ERROR) << "Failed to read dmabuf fd references";
            return false;
        }
    }

    if (!ReadDmaBufMapRefs(pid, dmabufs, procfs_path, dmabuf_sysfs_path)) {
        LOG(ERROR) << "Failed to read dmabuf map references";
        return false;
    }
    return true;
}

2.3 选项 -b

用以解析 /sys/kernel/dmabuf/buffers 下每个buffer 的export_name 和 size, 不排序。

2.3.1 输出信息格式

bash 复制代码
----------------------- DMA-BUF per-buffer stats -----------------------
    Dmabuf Inode |     Size(bytes) |    Exporter Name                    |
             619 |        13004800 |           system
              57 |            4096 |           system
             250 |           32768 |           system
             ...
			 ...
             721 |        13004800 |           system
             665 |           32768 |           system
               9 |          516096 |           system
              27 |            4096 |           system


----------------------- DMA-BUF exporter stats -----------------------
      Exporter Name              | Total Count |     Total Size(bytes)   |
                          system |           47| 176926720
                          ...


----------------------- DMA-BUF total stats -----------------------
Total DMA-BUF count: 91, Total DMA-BUF size(bytes): 181178368

分三块:

  • **第一块:**按照 inode 维度输出,包括size 和 exporter name;
  • **第二块:**按照exporter name维度输出,这里省略了system 之外的其他 exporter;
  • **第三块:**总的信息;

更多信息可以查看 DumpDmabufSysfsStats() 函数。

2.4 选项 -a

以表的形式显示dmabuf 信息

2.4.1 输出信息格式

这是文本形式的信息,后面还有很多进程信息。

  • **第一列:**dmabuf 的inode;
  • **第二列:**该dmabuf 的size;
  • **第三列:**该inode 被fd 引用的次数;
  • **第四列:**该inode 被mapped 的引用次数;
  • **第五列开始:**各个进程的引用统计;

统计的信息来自函数 ReadProcfsDmaBufs() 函数,打印函数为 **PrintDmaBufTable(),**可以查看上文框架图。

2.5 选项 -o

用以指定print 时的输出格式,raw 或 csv,默认为 raw,上面输出的格式都是文本信息。

也可以指定为 csv 格式,例如带有 PID 参数的输出csv 格式:

bash 复制代码
        mediaserver:2390
"Name","Rss(kB)","Pss(kB)","nr_procs","Inode","Exporter"
"system",32,32,1,501,system
"system",32,32,1,505,system
"system",32,32,1,510,system
"system",32,32,1,512,system
"system",32,32,1,661,system
"system",32,32,1,662,system
"system",32,32,1,663,system
"system",32,32,1,664,system
"system",32,32,1,665,system
"system",16,16,1,666,system
"system",16,16,1,667,system
"system",16,16,1,668,system
"system",32,32,1,670,system
"system",32,32,1,671,system
"system",32,32,1,672,system

PROCESS TOTAL
"Rss total(kB)","Pss total(kB)"
432,432
----------------------
        TOTALS
"dmabuf total (kB)","kernel_rss (kB)","userspace_rss (kB)","userspace_pss (kB)"

3. 实用

dmabuf_dump 命令在user 版本中可能存在缺陷:

  • dmabuf_dump 命令会失败;

  • 没有进入/proc/<PID>/fdinfo 权限;

bash 复制代码
dr-xr-xr-x 2 system system u:r:tee:s0 0 2025-10-02 11:13 fdinfo
  • 没有进入 /sys/kernel/dmabuf/buffers 权限;
bash 复制代码
drwxr-xr-x 125 root root u:object_r:sysfs_dmabuf_stats:s0 0 2025-10-01 20:39 buffers

可以使用 libdmabufinfo.so 这个静态库,模拟dmabuf_dump 中的函数调用,调用 ReadProcfsDmaBuf()ReadDmaBufInfo() 函数。

bash 复制代码
#include <dmabufinfo/dmabuf_sysfs_stats.h>
#include <dmabufinfo/dmabufinfo.h>

struct PidMemoryInfo {
    uint64_t rss;
    uint64_t pss;
};
std::unordered_map<pid_t, PidMemoryInfo> gPidMemoryMap;
uint64_t gDmabufTotal;

int parseDmabuf()
{
    ALOGV("parseDmabuf start...");

    std::vector<DmaBuffer> bufs;
    if (!ReadProcfsDmaBufs(&bufs)) {
        ALOGE("Failed to ReadProcfsDmaBufs, check logcat for info");
        return -1;
    }

    if (bufs.empty()) {
        ALOGE("parsed dmabuf is empty....");
        gPidMemoryMap.clear();
        return 0;
    }

    std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
    std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
    for (auto& buf : bufs) {
        inode_to_dmabuf[buf.inode()] = buf;

        for (auto pid : buf.pids()) {
            pid_to_inodes[pid].insert(buf.inode());
        }
    }

    gPidMemoryMap.clear();

    for (auto& [pid, inodes] : pid_to_inodes) {
        uint64_t pss = 0;
        uint64_t rss = 0;

        for (auto& inode : inodes) {
            DmaBuffer& buf = inode_to_dmabuf[inode];
            rss += buf.size();
            pss += buf.Pss();
        }

        PidMemoryInfo memInfo = {rss, pss};
        gPidMemoryMap[pid] = memInfo;
    }

    if (!GetDmabufTotalExportedKb(&gDmabufTotal)) {
        ALOGE("Warning: Could not get total exported dmabufs. Kernel size will be 0.");
        return -1;
    }

    return 0;
}

经过上面统计之后,就可以根据业务需要显示:

cpp 复制代码
    for (auto& [pid, memInfo] : gPidMemoryMap) {
        ALOGD("%6d    %llu", pid, memInfo.pss);
    }
相关推荐
LING19 分钟前
RN容器启动优化实践
android·react native
恋猫de小郭3 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker8 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴8 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭18 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab19 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin