Android dmabuf_dump 命令详解
版本基于:Android 16 (W)
1. 命令格式
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 helpIf 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 对象是CsvOutput 或 RawOutput;-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 输出信息格式
cdsprpcd:2510Name Rss Pss nr_procs Inode Exportersystem 4 kB 4 kB 1 56 systemsystem 4 kB 4 kB 1 57 systemsystem 256 kB 256 kB 1 58 systemPROCESS TOTAL 264 kB 264 kB
----------------------binder:2522_2:2522Name Rss Pss nr_procs Inode Exportersystem 32 kB 16 kB 2 661 systemsystem 32 kB 16 kB 2 662 systemsystem 32 kB 16 kB 2 663 systemsystem 32 kB 16 kB 2 664 systemsystem 32 kB 16 kB 2 665 systemPROCESS 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()
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>
的信息有:
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。
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;
}
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是否可以访问,下面将轮询读取该目录下所有的 fdstd::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 fdinfostd::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 fdif (!is_dmabuf_file) {continue;}//这里是兼容性,当发现idnode为-1 时,可能inode 缺失,通过stat的方式获取sb.st_infoif (inode == static_cast<uint64_t>(-1)) {// Fallback to stat() on the fd path to get inode numberstd::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;
}
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 dmabufif (!FileIsDmaBuf(mapinfo.name)) { //确认 vma 是否为dmabufreturn;}//通过唯一的标识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 namestd::string exporter;bool sysfs_stats = ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path);if (!sysfs_stats) {exporter = "<unknown>";}//尝试读取inode 下的 size,如果没有获得,则使用vma的大小//但此时可能产生误导,有可能vma 超过实际buffer sizeuint64_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 输出信息格式
shift:/proc/1919/fdinfo # dmabuf_dump 2390mediaserver:2390Name Rss Pss nr_procs Inode Exportersystem 32 kB 32 kB 1 501 systemsystem 32 kB 32 kB 1 505 systemsystem 32 kB 32 kB 1 510 systemsystem 32 kB 32 kB 1 512 systemsystem 32 kB 32 kB 1 661 systemsystem 32 kB 32 kB 1 662 systemsystem 32 kB 32 kB 1 663 systemsystem 32 kB 32 kB 1 664 systemsystem 32 kB 32 kB 1 665 systemsystem 16 kB 16 kB 1 666 systemsystem 16 kB 16 kB 1 667 systemsystem 16 kB 16 kB 1 668 systemsystem 32 kB 32 kB 1 670 systemsystem 32 kB 32 kB 1 671 systemsystem 32 kB 32 kB 1 672 systemPROCESS 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()
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 信息,这里做一下cleardmabufs->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 输出信息格式
----------------------- DMA-BUF per-buffer stats -----------------------Dmabuf Inode | Size(bytes) | Exporter Name |619 | 13004800 | system57 | 4096 | system250 | 32768 | system......721 | 13004800 | system665 | 32768 | system9 | 516096 | system27 | 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 格式:
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,systemPROCESS 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 权限;
dr-xr-xr-x 2 system system u:r:tee:s0 0 2025-10-02 11:13 fdinfo
没有进入 /sys/kernel/dmabuf/buffers 权限;
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() 函数。
#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;
}
经过上面统计之后,就可以根据业务需要显示:
for (auto& [pid, memInfo] : gPidMemoryMap) {ALOGD("%6d %llu", pid, memInfo.pss);}