背景
在某服务的测试环境中,/root
目录挂载的是一块20G
的磁盘。每个节点可能会运行多个实例,我们会在环境上进行性能测试、性能分析等操作。在某次测试的过程中,我们发现系统盘的20G
已被占满。
由于一般的环境都是这样处理的,所以这里我们不考虑重新划盘这样的解决方案。
问题发现与分析
是什么占据了磁盘?
我们查看每个文件夹的大小:
shell
du -h --max-depth=1
发现在20G
的空间中,除了一些必要的文件外,有大约8G
被.debug
文件夹占据,此外还有2G
由perf
采集到的数据。perf
采集到的数据直接删除即可,我们重点关注.debug
文件夹。.debug
文件夹是用来保存可执行文件对应的debug
信息的。
进入.debug
文件夹下,我们发现了和可执行文件的路径几乎一致的路径。例如可执行文件testcpp
路径为/root/workplace/test/testcpp
,对应在.debug
下的路径就是/root/.debug/root/workplace/test/testcpp/1ec9a7a87620face349c8f562de67d00e7a1cfa2
,这里的1ec9a7a87620face349c8f562de67d00e7a1cfa2
是这个可执行文件的buildid
。这里buldid
是这个可执行文件在编译的时候生成的。我们可以用file
来进行查看:
由于测试环境上会运行多个实例,所以在.debug
文件夹下会出现多个实例的elf
文件,这就导致整体的存储占用比较高。
是什么产生了这些文件?
从过去使用perf
的经验中,笔者了解到perf script
可能会使用到.debug
文件夹下的内容,同时perf
也有perf buildid-list
这样明显和.debug
文件相关联的部分,因此笔者首先将目光放到了perf
上。
显然这是一个已经执行过perf
的环境,所以我们不妨找一个没有.debug
文件夹的环境,执行perf
试试:
可以看到在这里生成了.debug
目录。
我们通过strace
抓取perf record
执行的情况:
shell
strace -o strace perf record -ag -F 999 -- sleep 2
在输出的strace
结果中直接搜索.debug
:
可以看到perf
直接去创建了.debug
目录并且会去检查其子目录的情况,并进行子目录的创建,也即perf record会生成.debug文件夹及对应的文件。
在perf config
注释中,我们也找到了相关说明:
The recording tools also stores a hard link or copy in a per-user directory, $HOME/.debug/, of binaries, shared libraries, /proc/kallsyms and /proc/kcore files to be used at analysis time.
问题解决
在前面我们提到,我们不考虑增加存储这样的解法,因此我们考虑如下的两个思路:
- 不生成
.debug
- 将
.debug
生成到其他目录下
不生成.debug
如何不生成.debug目录
我们首先去查阅了perf record
的文档,在文档中我们发现可以用-B/-N
选项关闭:
-B, --no-buildid Do not save the build ids of binaries in the perf.data files. This skips post processing after recording, which sometimes makes the final step in the recording process to take a long time, as it needs to process all events looking for mmap records. The downside is that it can misresolve symbols if the workload binaries used when recording get locally rebuilt or upgraded, because the only key available in this case is the pathname. You can also set the "record.build-id" config variable to 'skip to have this behaviour permanently.
-N, --no-buildid-cache Do not update the buildid cache. This saves some overhead in situations where the information in the perf.data file (which includes buildids) is sufficient. You can also set the "record.build-id" config variable to no-cache to have the same effect.
我们实际验证一下:
不生成的影响
根据注释来说,我们会发现-B
与-N
的作用并不是完全一致的,前者是不保存相关的二进制信息,而后者是不做更新。这会有什么后果呢?
在perf
的使用流程中,我们往往会使用perf script
去解析perf record
生成的数据,在解析的过程中,会涉及到地址与函数的转换,这里就需要我们的debug
信息的参与。在实际的转换过程中,perf script
会通过以下的渠道去找对应的二进制文件:
- 可执行文件目录,例如前文提到的
/root/workplace/test/testcpp
;如果是容器环境,会首先进行setns
操作; $HOME/.debug
目录下的文件(默认情况下);/usr/lib/debug
目录下的文件;
而perf
会进行如下的操作:
Each executable and shared library in modern distributions comes with a content based identifier that, if available, will be inserted in a perf.data file header to, at analysis time find what is needed to do symbol resolution, code annotation, etc.
因此,正如注释中说到的,如果可执行文件目录的文件发生了变更,就会导致错误的匹配。这也就是.debug
保存的原因,让perf
能够在即使可执行文件消失的情况下也可以正确的解析。所以如果环境上的程序不会发生太多变化的话,不保存相关信息也是可以的。
将.debug生成到其他目录下
在前面提到的注释中,我们发现也可以通过进行配置的方式来调整perf record
执行的情况,我们查看perf config
的选项,发现如下的选项:
[buildid] # Default, disable using /dev/null dir = ~/.debug
因此我们可以将该目录生成到其他路径中,不使用该系统盘路径即可。下面是使用perf config
切换路径的例子:
小结
本文基于在实际情况中遇到的磁盘满载问题,进行了一系列的探索,结论如下:
- 为了更好的做解析,
perf record
默认会将诸多debug
文件放到~/.debug
文件夹下; - 可以通过
-B/-N
选项禁止该行为 - 可以通过
perf config
配置存放文件路径