admin管理员组

文章数量:1037775

探秘VPP:快速定位libc内存泄漏的有效方法

在文章《VPP 内存泄漏

这里就使用到了动态库预加载LD_PRELOAD机制。LD_PRELOAD 是 Linux 系统中一个非常强大的特性,它允许用户通过环境变量指定在加载其他共享库之前优先加载的共享库。这一机制主要用于动态链接阶段,可以在不修改程序源代码的情况下改变或增强程序的行为。

在Linux系统中,当一个程序启动时,操作系统会调用动态链接器(通常是ld.so或其变种)来加载并链接程序依赖的所有共享库。如果环境变量LD_PRELOAD被设置,动态链接器会在处理其他任何共享库之前优先加载由LD_PRELOAD指定的共享库。动态链接器遵循一种称为“先到先得”的符号解析策略。实现细节是通过修改动态链接器内部的某些函数(如_dl_load_object())来调整加载顺序,确保LD_PRELOAD中的库最先被加载。这样做的结果是,这些预加载的库有机会“先入为主”,即它们可以定义与后续加载的库中同名的符号(如函数),从而实现对这些符号的覆盖。

在 vpp(Vector Packet Processing)里,内存的分配与管理依赖于 vpp 的主堆(main - heap)。然而,当引入外部库时,尤其是在插件场景下,问题就会凸显出来。以 IKEv2 插件使用的 OpenSSL 库为例,这些外部库一般会通过标准的 libc 函数,如 malloc()free() 等来进行内存管理,这实际上是在使用默认的 libc 堆。但 vpp 本身并不知晓这个默认 libc 堆的存在。这一情况直接导致 vpp 自带的内存跟踪工具(memory - trace)失去作用,无法对外部库使用的内存进行有效跟踪和管理。

为解决相关问题,vpp 代码中提供了 libvppmem_preload.so 库,其相关代码位于 src/vpp/mem/mem.c 文件。该库的作用是替换标准的 libc 内存管理函数,例如 malloc()free() 等,转而使用vpp自身的内存管理函数,如 clib_mem_allocclib_mem_free 等。

这种替换方式具有显著优势。它能确保所有内存分配与释放操作都通过 vpp 的内存管理接口完成,进而使 vpp 自带的调试和监控工具能够全面监测整个应用程序的内存使用情况,即使是由外部库发起的内存操作也能被覆盖。以下是具体的操作方法如下:

代码语言:javascript代码运行次数:0运行复制
执行make build编译之后,库一般存放在下面的位置:root@learningvpp:~/workspace/vpp# ls build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -l
lrwxrwxrwx 1 root root 26 Mar 10 13:09 build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -> libvppmem_preload.so.25.06

接下来我们使用LD_PRELOAD机制加载libvppmem_preload.so库,来运行vpp,如下:

  1. root@learningvpp:~/workspace/vpp# LD_PRELOAD=build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so build-root/install-vpp_debug-native/vpp/bin/vpp -c /etc/vpp/startup.conf
  2. vpp mem: libc allocation requested but no vpp heap ready, defaulting to libc.
  3. unix_config:461: couldn't open log '/var/log/vpp/vpp.log'
  4. vpp[1428284]: perfmon: skipping source 'intel-uncore' - intel_uncore_init: no uncore units found
  5. vpp[1428284]: perfmon: skipping source 'intel-core' - intel_core_init: not a IA-32 CPU
  6. vpp[1428284]: vlib_pci_bind_to_uio: Skipping PCI device 0000:00:05.0: missing kernel VFIO or UIO driver
  7. vpp[1428284]: vat-plug/load: vat_plugin_register: idpf plugin not loaded...
  8. vpp[1428284]: vat-plug/load: vat_plugin_register: oddbuf plugin not loaded...

在上图第2行的提示中,反映了在VPP初始化的初期,由于VPP自身的堆管理机制尚未准备就绪,暂时通过libc的malloc()函数进行内存分配。此状况仅会在VPP启动的极短时间内出现。一旦VPP的内存管理系统完成初始化,便会切换至使用VPP自己的内存管理策略。这表明了系统在启动过程中短暂依赖标准C库的内存分配功能,以确保顺利过渡到VPP定制的内存管理流程。

接下来,我们将利用VPP自带的插件单元测试用例中的test crypto来验证是否能够成功记录到与OpenSSL相关的库调用。

代码语言:txt复制
#开启内存跟踪命令 
DBGvpp# memory-trace on main-heap 
#调用加解密测试套件  
DBGvpp# test crypto   
128-GCM Spec. TC1 (encrypt-aes-128-gcm)     OK  
128-GCM Spec. TC1 (decrypt-aes-128-gcm)     OK  
128-GCM Spec. TC2 (encrypt-aes-128-gcm)     OK  
  .....  
#查询跟踪情况: 
DBGvpp# show memory main-heap verbose  
Thread 0 vpp_main  
  base 0x74ce3fe01000,size 512m,locked,unmap-on-destroy, traced, name 'main heap' 
page stats: page-size 4K, total 131072, mapped 25159, not-mapped 105913 
  numa 0: 25159 pages, 98.28m bytes  
  total: 512.00M, used: 101.54M, free: 410.46M, trimmable: 398.23M 
      free chunks 1008 free fastbin blks 0 
      max total allocated 512.00M  
 
  Bytes    Count     Sample   Traceback  
192 2 0x72a4168a32b0 0x72a435ad3225 
                  CRYPTO_zalloc + 0x15 
 0x72a411fe4128 
                  HMAC_Init_ex + 0x167 
 0x72a40e8041f8 
 0x72a40e8034cd 
 0x72a40e800aa0 
                  vnet_crypto_key_add + 0x1db 
                  generate_digest + 0x116 
                  test_crypto + 0x48e 
                  test_crypto_command_fn + 0x224 
                  vlib_cli_dispatch_sub_commands + 0xe24 
                  vlib_cli_dispatch_sub_commands + 0xe24

我们注意到,在VPP的内存跟踪中,OpenSSL的CRYPTO_zalloc()调用显示为malloc()。这种情况仅在使用了预加载技术时才会出现;如果没有使用预加载,这一调用将不会在此处显示。这表明预加载使我们能够捕捉到通过libc库进行的内存分配操作,即使这些操作实际上是由OpenSSL函数发起的。

至此,我们可以监测到openssl相关函数调用栈情况,至于是否存在泄漏情况,还需要阅读代码判断。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-13,如有侵权请联系 cloudcommunity@tencent 删除内存泄漏libc函数内存内存管理

探秘VPP:快速定位libc内存泄漏的有效方法

在文章《VPP 内存泄漏

这里就使用到了动态库预加载LD_PRELOAD机制。LD_PRELOAD 是 Linux 系统中一个非常强大的特性,它允许用户通过环境变量指定在加载其他共享库之前优先加载的共享库。这一机制主要用于动态链接阶段,可以在不修改程序源代码的情况下改变或增强程序的行为。

在Linux系统中,当一个程序启动时,操作系统会调用动态链接器(通常是ld.so或其变种)来加载并链接程序依赖的所有共享库。如果环境变量LD_PRELOAD被设置,动态链接器会在处理其他任何共享库之前优先加载由LD_PRELOAD指定的共享库。动态链接器遵循一种称为“先到先得”的符号解析策略。实现细节是通过修改动态链接器内部的某些函数(如_dl_load_object())来调整加载顺序,确保LD_PRELOAD中的库最先被加载。这样做的结果是,这些预加载的库有机会“先入为主”,即它们可以定义与后续加载的库中同名的符号(如函数),从而实现对这些符号的覆盖。

在 vpp(Vector Packet Processing)里,内存的分配与管理依赖于 vpp 的主堆(main - heap)。然而,当引入外部库时,尤其是在插件场景下,问题就会凸显出来。以 IKEv2 插件使用的 OpenSSL 库为例,这些外部库一般会通过标准的 libc 函数,如 malloc()free() 等来进行内存管理,这实际上是在使用默认的 libc 堆。但 vpp 本身并不知晓这个默认 libc 堆的存在。这一情况直接导致 vpp 自带的内存跟踪工具(memory - trace)失去作用,无法对外部库使用的内存进行有效跟踪和管理。

为解决相关问题,vpp 代码中提供了 libvppmem_preload.so 库,其相关代码位于 src/vpp/mem/mem.c 文件。该库的作用是替换标准的 libc 内存管理函数,例如 malloc()free() 等,转而使用vpp自身的内存管理函数,如 clib_mem_allocclib_mem_free 等。

这种替换方式具有显著优势。它能确保所有内存分配与释放操作都通过 vpp 的内存管理接口完成,进而使 vpp 自带的调试和监控工具能够全面监测整个应用程序的内存使用情况,即使是由外部库发起的内存操作也能被覆盖。以下是具体的操作方法如下:

代码语言:javascript代码运行次数:0运行复制
执行make build编译之后,库一般存放在下面的位置:root@learningvpp:~/workspace/vpp# ls build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -l
lrwxrwxrwx 1 root root 26 Mar 10 13:09 build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so -> libvppmem_preload.so.25.06

接下来我们使用LD_PRELOAD机制加载libvppmem_preload.so库,来运行vpp,如下:

  1. root@learningvpp:~/workspace/vpp# LD_PRELOAD=build-root/install-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvppmem_preload.so build-root/install-vpp_debug-native/vpp/bin/vpp -c /etc/vpp/startup.conf
  2. vpp mem: libc allocation requested but no vpp heap ready, defaulting to libc.
  3. unix_config:461: couldn't open log '/var/log/vpp/vpp.log'
  4. vpp[1428284]: perfmon: skipping source 'intel-uncore' - intel_uncore_init: no uncore units found
  5. vpp[1428284]: perfmon: skipping source 'intel-core' - intel_core_init: not a IA-32 CPU
  6. vpp[1428284]: vlib_pci_bind_to_uio: Skipping PCI device 0000:00:05.0: missing kernel VFIO or UIO driver
  7. vpp[1428284]: vat-plug/load: vat_plugin_register: idpf plugin not loaded...
  8. vpp[1428284]: vat-plug/load: vat_plugin_register: oddbuf plugin not loaded...

在上图第2行的提示中,反映了在VPP初始化的初期,由于VPP自身的堆管理机制尚未准备就绪,暂时通过libc的malloc()函数进行内存分配。此状况仅会在VPP启动的极短时间内出现。一旦VPP的内存管理系统完成初始化,便会切换至使用VPP自己的内存管理策略。这表明了系统在启动过程中短暂依赖标准C库的内存分配功能,以确保顺利过渡到VPP定制的内存管理流程。

接下来,我们将利用VPP自带的插件单元测试用例中的test crypto来验证是否能够成功记录到与OpenSSL相关的库调用。

代码语言:txt复制
#开启内存跟踪命令 
DBGvpp# memory-trace on main-heap 
#调用加解密测试套件  
DBGvpp# test crypto   
128-GCM Spec. TC1 (encrypt-aes-128-gcm)     OK  
128-GCM Spec. TC1 (decrypt-aes-128-gcm)     OK  
128-GCM Spec. TC2 (encrypt-aes-128-gcm)     OK  
  .....  
#查询跟踪情况: 
DBGvpp# show memory main-heap verbose  
Thread 0 vpp_main  
  base 0x74ce3fe01000,size 512m,locked,unmap-on-destroy, traced, name 'main heap' 
page stats: page-size 4K, total 131072, mapped 25159, not-mapped 105913 
  numa 0: 25159 pages, 98.28m bytes  
  total: 512.00M, used: 101.54M, free: 410.46M, trimmable: 398.23M 
      free chunks 1008 free fastbin blks 0 
      max total allocated 512.00M  
 
  Bytes    Count     Sample   Traceback  
192 2 0x72a4168a32b0 0x72a435ad3225 
                  CRYPTO_zalloc + 0x15 
 0x72a411fe4128 
                  HMAC_Init_ex + 0x167 
 0x72a40e8041f8 
 0x72a40e8034cd 
 0x72a40e800aa0 
                  vnet_crypto_key_add + 0x1db 
                  generate_digest + 0x116 
                  test_crypto + 0x48e 
                  test_crypto_command_fn + 0x224 
                  vlib_cli_dispatch_sub_commands + 0xe24 
                  vlib_cli_dispatch_sub_commands + 0xe24

我们注意到,在VPP的内存跟踪中,OpenSSL的CRYPTO_zalloc()调用显示为malloc()。这种情况仅在使用了预加载技术时才会出现;如果没有使用预加载,这一调用将不会在此处显示。这表明预加载使我们能够捕捉到通过libc库进行的内存分配操作,即使这些操作实际上是由OpenSSL函数发起的。

至此,我们可以监测到openssl相关函数调用栈情况,至于是否存在泄漏情况,还需要阅读代码判断。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-13,如有侵权请联系 cloudcommunity@tencent 删除内存泄漏libc函数内存内存管理

本文标签: 探秘VPP快速定位libc内存泄漏的有效方法