专栏名称: 字节跳动技术团队
字节跳动的技术实践分享
目录
相关文章推荐
字节跳动技术团队  ·  远程访问代理+内网穿透:火山引擎边缘网关助力 ... ·  8 小时前  
字节跳动技术团队  ·  稀土掘金 x Trae ... ·  8 小时前  
51好读  ›  专栏  ›  字节跳动技术团队

抖音renderD128系统级疑难OOM分析与解决

字节跳动技术团队  · 公众号  · 架构  · 2025-05-06 12:00

正文

请到「今天看啥」查看全文


从驱动和关键so库中寻找线索
  • libdrm库
DRM

DRM是Linux内核层的显示驱动框架,它把显示功能封装成 open/close/ioctl 等标准接口,用户空间的程序调用这些接口,驱动设备,显示数据。libdrm库封装了DRM driver提供的这些接口。通过libdrm库,程序可以间接调用DRM Driver

但libdrm库中的 drm_mmap 是调用 mmap __mmap2 (都是监控中的接口)
#if defined(ANDROID) && !defined(__LP64__)extern void *__mmap2(void *, size_t, int, int, int, size_t);
static inline void *drm_mmap(void *addr, size_t length, int prot, int flags,                             int fd, loff_t offset){   /* offset must be aligned to 4096 (not necessarily the page size) */   if (offset & 4095) {      errno = EINVAL;      return MAP_FAILED;   }
   return __mmap2(addr, length, prot, flags, fd, (size_t) (offset >> 12));}#else/* assume large file support exists */#  define drm_mmap(addr, length, prot, flags, fd, offset) \              mmap(addr, length, prot, flags, fd, offset)
  • mesa3D
mesa3D
mesa3D中是通过调用l ibdrm库中的接口,间接调用DRM Driver的
https://gitlab .freedesktop.org/mesa/mesa
在mesa的源代码中找到了类似libsrv_um.so中 PRVSRVBridgeCall 的函数 pvr_srv_bridge_call
static int pvr_srv_bridge_call(int fd,                               uint8_t bridge_id,                               uint32_t function_id,                               void *input,                               uint32_t input_buffer_size,                               void *output,                               uint32_t output_buffer_size){   struct drm_srvkm_cmd cmd = {      .bridge_id = bridge_id,      .bridge_func_id = function_id,      .in_data_ptr = (uint64_t)(uintptr_t)input,      .out_data_ptr = (uint64_t)(uintptr_t)output,      .in_data_size = input_buffer_size,      .out_data_size = output_buffer_size,   };
   int ret = drmIoctl(fd, DRM_IOCTL_SRVKM_CMD, &cmd);   if (unlikely(ret))      return ret;
   VG(VALGRIND_MAKE_MEM_DEFINED(output, output_buffer_size));
   return 0U;}
同时发现了BridgeCall的相关id定义
图片
通过提交的 commit 了解到这部分代码是为 powerVR rogue GPU增加的驱动;
commit链接: https://gitlab.freedesktop.org/mesa/mesa/-/commit/8991e646411b 73c1e03278267c80758e921f2352
图片
存在renderD128内存问题的机型使用的GPU也是PowerVR GPU,那么内存申请关键逻辑 应该确实就在libsrv_um.so和gralloc.mt6765.so中
Huawei Y6p - Full phone specifications
图片
  • libsrv_um.so与gralloc.mt6765.so
奇怪的是,libsrv_um.so中只有munma p的符号,却没有mmap的符号(gralloc.mt6765.so同样没有)
图片
这比较不符合常理,一般来说,mmap和munmap都是成对出现的,猜测有几种可能性:
  1. 在其他库中mmap

  2. 用其他方式实现mmap操作

  • 使用dlsym拿到mmap等的符号,再调用

  1. 这种情况,使用inline hook是可以监控到的

  • 调用ioctl实现mmap操作 ❌

  • 直接使用系统调用 ✅

    1. 在libsrv_um.so中发现调用了syscall,系统调用号是0xC0(192),正是mmap的系统调用号! 图片


    2. gralloc.mt6765.so同libsrv_um.so,也是通过系统调用进行mmap的!


结论 hook syscall 应该可以监控到renderD128相关内存的调用!

2.3.3 验证监控方案

  • 监控方式

    1. 使用bytehook代理了libsrv_um.so和gralloc.mt6765.so中对syscall的调用

    2. 记录renderD128内存的变化

  • 测试场景 播放视频
  • 测试结果

    1. 系统调用mmap可以监控到renderD128内存的分配

    2. 在播放视频期间renderD128内存增长大小符合通过系统调用mmap分配的大小

  • 堆栈

图片
  • 内存变化
图片
  • 结论 : 底层驱 动可能考虑到架构适配或者效率问题,直接使用系统调用而 非通用接口调用。在之前的监控中并未考虑到这种情况,所以会导致监控不全。






请到「今天看啥」查看全文