使用 ProcDump 调试 Linux

了解微软的开源工具,用于获取进程信息。
92 位读者喜欢这篇文章。

微软对 Linux 和开源日益增长的赞赏已不是什么秘密。在过去的几年里,该公司稳步增加了对开源的贡献,包括将其部分软件和工具移植到 Linux。2018 年末,微软宣布,它正在将其部分 Sysinternals 工具作为开源移植到 Linux,而 ProcDump for Linux 是第一个这样的版本。

如果您曾在 Windows 上进行调试或故障排除工作,您可能听说过 Sysinternals。它是一个“瑞士军刀” 工具集,可帮助系统管理员、开发人员和 IT 安全专业人员监视和排除 Windows 环境中的故障。

Sysinternals 最流行的工具之一是 ProcDump。顾名思义,它用于将正在运行的进程的内存转储到磁盘上的核心文件中。然后可以使用调试器分析此核心文件,以了解进行转储时进程的状态。 之前使用过 Sysinternals,我很好奇尝试一下 ProcDump 的 Linux 移植版本。

开始使用 ProcDump for Linux

要尝试 ProcDump for Linux,您需要下载 该工具并进行编译 。(我正在使用 Red Hat Enterprise Linux,尽管这些说明在其他 Linux 发行版上应该同样适用)

$ cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.2 (Ootpa)
$ 
$ uname -r
4.18.0-193.el8.x86_64
$ 

首先,克隆 ProcDump for Linux 存储库

$ git clone https://github.com/microsoft/ProcDump-for-Linux.git
Cloning into 'ProcDump-for-Linux'...
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 414 (delta 14), reused 14 (delta 6), pack-reused 374
Receiving objects: 100% (414/414), 335.28 KiB | 265.00 KiB/s, done.
Resolving deltas: 100% (232/232), done.
$ 
$ cd ProcDump-for-Linux/
$ 
$ ls 
azure-pipelines.yml  CONTRIBUTING.md  docs     INSTALL.md  Makefile    procdump.gif  src
CODE_OF_CONDUCT.md   dist             include  LICENSE     procdump.1  README.md     tests
$

接下来,使用 make 构建程序。它会打印出编译源文件所需的精确 GCC 命令行界面

$ make
rm -rf obj
rm -rf bin
rm -rf /root/ProcDump-for-Linux/pkgbuild
gcc -c -g -o obj/Logging.o src/Logging.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/Events.o src/Events.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/ProcDumpConfiguration.o src/ProcDumpConfiguration.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/Handle.o src/Handle.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/Process.o src/Process.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/Procdump.o src/Procdump.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/TriggerThreadProcs.o src/TriggerThreadProcs.c -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/CoreDumpWriter.o src/CoreDumpWriter.c -Wall -I ./include -pthread -std=gnu99
gcc -o bin/procdump obj/Logging.o obj/Events.o obj/ProcDumpConfiguration.o obj/Handle.o obj/Process.o obj/Procdump.o obj/TriggerThreadProcs.o obj/CoreDumpWriter.o -Wall -I ./include -pthread -std=gnu99
gcc -c -g -o obj/ProcDumpTestApplication.o tests/integration/ProcDumpTestApplication.c -Wall -I ./include -pthread -std=gnu99
gcc -o bin/ProcDumpTestApplication obj/ProcDumpTestApplication.o -Wall -I ./include -pthread -std=gnu99
$

编译会创建两个新目录。第一个是 obj/ 目录,其中保存编译期间创建的目标文件。第二个(也是更重要的)目录是 bin/,其中存储已编译的 procdump 程序。它还会编译另一个名为 ProcDumpTestApplication 的测试二进制文件

$ ls obj/
CoreDumpWriter.o  Handle.o   ProcDumpConfiguration.o  ProcDumpTestApplication.o  TriggerThreadProcs.o
Events.o          Logging.o  Procdump.o               Process.o
$ 
$ 
$ ls bin/
procdump  ProcDumpTestApplication
$ 
$ file bin/procdump 
bin/procdump: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6e8827db64835ea0d1f0941ac3ecff9ee8c06e6b, with debug_info, not stripped
$ 
$ file bin/ProcDumpTestApplication 
bin/ProcDumpTestApplication: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=c8fd86f53c07df142e52518815b2573d1c690e4e, with debug_info, not stripped
$

通过此设置,每次运行 procdump 实用程序时,都必须移动到 bin/ 文件夹中。要使其在系统内的任何位置都可用,请运行 make install。这会将二进制文件复制到通常的 bin/ 目录中,该目录是 shell 的 $PATH 的一部分

$ which procdump
/usr/bin/which: no procdump in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin)
$ 
$ make install
mkdir -p //usr/bin
cp bin/procdump //usr/bin
mkdir -p //usr/share/man/man1
cp procdump.1 //usr/share/man/man1
$ 
$ which procdump
/usr/bin/procdump
$

安装后,ProcDump 提供了一个手册页,您可以使用 man procdump 访问它

$ man procdump
$ 

运行 ProcDump

要转储进程的内存,您需要向 ProcDump 提供其进程 ID (PID)。您可以使用机器上任何正在运行的程序或守护程序。在此示例中,我将使用一个无限循环的微型 C 程序。编译并运行该程序(要退出程序,请按 Ctrl+C,或者如果它在后台运行,请使用带有 PID 的 kill 命令)

$ cat progxyz.c 
#include <stdio.h>

int main() {
	for (;;)
	{
		printf(".");
		sleep(1);
	}
	return 0;
}
$ 
$ gcc progxyz.c -o progxyz
$ 
$ ./progxyz &
[1] 350498
$

通过运行该程序,您可以使用 pgrepps 找到其 PID。记下 PID

$ pgrep progxyz
350498
$ 
$ ps -ef | grep progxyz
root      350498  345445  0 03:29 pts/1    00:00:00 ./progxyz
root      350508  347350  0 03:29 pts/0    00:00:00 grep --color=auto progxyz
$

在测试进程运行时,调用 procdump 并提供 PID。输出说明了进程的名称和 PID,报告已生成 Core dump,并显示其文件名

$ procdump -p 350498

ProcDump v1.1.1 - Sysinternals process dump utility
Copyright (C) 2020 Microsoft Corporation. All rights reserved. Licensed under the MIT license.
Mark Russinovich, Mario Hewardt, John Salem, Javid Habibi
Monitors a process and writes a dump file when the process exceeds the
specified criteria.

Process:		progxyz (350498)
CPU Threshold:		n/a
Commit Threshold:	n/a
Polling interval (ms):	1000
Threshold (s):	10
Number of Dumps:	1

Press Ctrl-C to end monitoring without terminating the process.

[03:30:00 - INFO]: Timed:
[03:30:01 - INFO]: Core dump 0 generated: progxyz_time_2020-06-24_03:30:00.350498
$

列出当前目录的内容,您应该会看到新的核心文件。文件名与 procdump 命令显示的文件名匹配,并且 日期、时间和 PID 都附加到文件名中

$ ls -l progxyz_time_2020-06-24_03\:30\:00.350498 
-rw-r--r--. 1 root root 356848 Jun 24 03:30 progxyz_time_2020-06-24_03:30:00.350498
$ 
$ file progxyz_time_2020-06-24_03\:30\:00.350498 
progxyz_time_2020-06-24_03:30:00.350498: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './progxyz', real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: './progxyz', platform: 'x86_64'
$

使用 GNU 项目调试器分析核心文件

要查看是否可以读取 proc 文件,请调用 GNU 项目调试器 (gdb)。请记住提供测试二进制文件的路径,以便您可以在堆栈上看到所有函数名称。在此,bt(回溯)显示在进行转储时正在执行 sleep() 函数

$ gdb -q ./progxyz ./progxyz_time_2020-06-24_03\:30\:00.350498 
Reading symbols from ./progxyz...(no debugging symbols found)...done.
[New LWP 350498]
Core was generated by `./progxyz'.
#0  0x00007fb6947e9208 in nanosleep () from /lib64/libc.so.6
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-101.el8.x86_64
(gdb) bt
#0  0x00007fb6947e9208 in nanosleep () from /lib64/libc.so.6
#1  0x00007fb6947e913e in sleep () from /lib64/libc.so.6
#2  0x00000000004005f3 in main ()
(gdb)

gcore 怎么样?

Linux 用户会很快指出,Linux 已经有一个名为 gcore 的命令,该命令随大多数 Linux 发行版一起提供,并且执行与 ProcDump 完全相同的功能。这是一个有效的论点。如果您从未使用过它,请尝试以下操作以使用 gcore 转储进程的核心。再次运行测试程序,然后运行 gcore,并提供 PID 作为参数

$ ./progxyz &
[1] 350664
$ 
$ 
$ pgrep progxyz
350664
$ 
$ 
$ gcore 350664
0x00007fefd3be2208 in nanosleep () from /lib64/libc.so.6
Saved corefile core.350664
[Inferior 1 (process 350664) detached]
$

gcore 打印一条消息,说明它已将核心保存到特定文件。检查当前目录以查找此核心文件,并再次使用 gdb 加载它

$ 
$ ls -l  core.350664 
-rw-r--r--. 1 root root 356848 Jun 24 03:34 core.350664
$ 
$ 
$ file core.350664 
core.350664: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './progxyz', real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: './progxyz', platform: 'x86_64'
$ 
$ gdb -q ./progxyz ./core.350664 
Reading symbols from ./progxyz...(no debugging symbols found)...done.
[New LWP 350664]
Core was generated by `./progxyz'.
#0  0x00007fefd3be2208 in nanosleep () from /lib64/libc.so.6
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-101.el8.x86_64
(gdb) bt
#0  0x00007fefd3be2208 in nanosleep () from /lib64/libc.so.6
#1  0x00007fefd3be213e in sleep () from /lib64/libc.so.6
#2  0x00000000004005f3 in main ()
(gdb) q
$

为了使 gcore 工作,您需要确保以下设置到位。首先,确保为核心文件设置了 ulimit;如果将其设置为 0,则不会生成核心文件。其次,确保 /proc/sys/kernel/core_pattern 具有适当的设置以指定核心模式

$ ulimit -c
unlimited
$ 

您应该使用 ProcDump 还是 gcore?

在某些情况下,您可能更喜欢使用 ProcDump 而不是 gcore,并且 ProcDump 具有一些内置功能,这些功能在一般情况下可能很有用。

等待测试二进制文件执行

无论您使用 ProcDump 还是 gcore,测试进程都必须执行并处于运行状态,以便您可以提供 PID 来生成核心文件。但是 ProcDump 具有一项功能,可以等待直到特定的二进制文件运行;一旦它找到与给定名称匹配的测试二进制文件正在运行,它就会为该测试二进制文件生成核心文件。可以使用 -w 参数和程序名称而不是 PID 来启用此功能。此功能在测试程序快速退出的情况下可能很有用。

以下是它的工作原理。在此示例中,没有名为 progxyz 的进程正在运行

$ pgrep progxyz 
$

使用 -w 命令调用 procdump 以使其保持等待状态。从另一个终端,调用测试二进制文件 progxyz

$ procdump -w progxyz

ProcDump v1.1.1 - Sysinternals process dump utility
Copyright (C) 2020 Microsoft Corporation. All rights reserved. Licensed under the MIT license.
Mark Russinovich, Mario Hewardt, John Salem, Javid Habibi
Monitors a process and writes a dump file when the process exceeds the
specified criteria.

Process:		progxyz (pending)
CPU Threshold:		n/a
Commit Threshold:	n/a
Polling interval (ms):	1000
Threshold (s):	10
Number of Dumps:	1

Press Ctrl-C to end monitoring without terminating the process.

[03:39:23 - INFO]: Waiting for process 'progxyz' to launch...

然后,从另一个终端,调用测试二进制文件 progxyz: 

$ ./progxyz &
[1] 350951
$ 

ProcDump 立即检测到 该二进制文件正在运行 ,并为此二进制文件转储核心文件

[03:39:23 - INFO]: Waiting for process 'progxyz' to launch...
[03:43:22 - INFO]: Found process with PID 350951
[03:43:22 - INFO]: Timed:
[03:43:23 - INFO]: Core dump 0 generated: progxyz_time_2020-06-24_03:43:22.350951
$ 


$ ls -l progxyz_time_2020-06-24_03\:43\:22.350951 
-rw-r--r--. 1 root root 356848 Jun 24 03:43 progxyz_time_2020-06-24_03:43:22.350951
$ 
$ file progxyz_time_2020-06-24_03\:43\:22.350951 
progxyz_time_2020-06-24_03:43:22.350951: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './progxyz', real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: './progxyz', platform: 'x86_64'
$ 

多个核心转储

ProcDump 的另一个重要功能是,您可以使用命令行参数 -n <count> 指定要生成的核心文件数量。核心转储之间的默认时间间隔为 10 秒,但您可以使用 -s <sec> 参数修改此时间间隔。此示例使用 ProcDump 来获取测试二进制文件的三个核心转储

$ ./progxyz &
[1] 351014
$ 
$ procdump -n 3 -p 351014

ProcDump v1.1.1 - Sysinternals process dump utility
Copyright (C) 2020 Microsoft Corporation. All rights reserved. Licensed under the MIT license.
Mark Russinovich, Mario Hewardt, John Salem, Javid Habibi
Monitors a process and writes a dump file when the process exceeds the
specified criteria.

Process:		progxyz (351014)
CPU Threshold:		n/a
Commit Threshold:	n/a
Polling interval (ms):	1000
Threshold (s):	10
Number of Dumps:	3

Press Ctrl-C to end monitoring without terminating the process.

[03:45:20 - INFO]: Timed:
[03:45:21 - INFO]: Core dump 0 generated: progxyz_time_2020-06-24_03:45:20.351014
[03:45:31 - INFO]: Timed:
[03:45:32 - INFO]: Core dump 1 generated: progxyz_time_2020-06-24_03:45:31.351014
[03:45:42 - INFO]: Timed:
[03:45:44 - INFO]: Core dump 2 generated: progxyz_time_2020-06-24_03:45:42.351014
$ 
$ ls -l progxyz_time_2020-06-24_03\:45\:*
-rw-r--r--. 1 root root 356848 Jun 24 03:45 progxyz_time_2020-06-24_03:45:20.351014
-rw-r--r--. 1 root root 356848 Jun 24 03:45 progxyz_time_2020-06-24_03:45:31.351014
-rw-r--r--. 1 root root 356848 Jun 24 03:45 progxyz_time_2020-06-24_03:45:42.351014
$ 

基于 CPU 和内存使用率的核心转储

ProcDump 还使您能够在测试二进制文件或进程达到特定 CPU 或内存阈值时触发核心转储。ProcDump 的手册页显示了调用 ProcDump 时要使用的命令行参数

-C          Trigger core dump generation when CPU exceeds or equals specified value (0 to 100 * nCPU)
-c          Trigger core dump generation when CPU is less than specified value (0 to 100 * nCPU)
-M          Trigger core dump generation when memory commit exceeds or equals specified value (MB)
-m          Trigger core dump generation when when memory commit is less than specified value (MB)
-T          Trigger when thread count exceeds or equals specified value.
-F          Trigger when filedescriptor count exceeds or equals specified value.
-I          Polling frequency in milliseconds (default is 1000)

例如,您可以要求 ProcDump 在给定 PID 的 CPU 使用率超过 70% 时转储核心

procdump -C 70 -n 3 -p 351014

结论

ProcDump 是移植到 Linux 的众多 Windows 程序中的一个有趣的补充。它不仅为 Linux 用户提供了额外的工具选项,而且还可以让 Windows 用户在 Linux 上工作时感到更自在。

接下来阅读什么
标签
User profile image.
经验丰富的软件工程专业人士。主要兴趣是安全、Linux、恶意软件。喜欢在命令行上工作。对底层软件和了解事物的工作原理感兴趣。此处表达的观点仅代表我个人,不代表我的雇主

评论已关闭。

Creative Commons License本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
© . All rights reserved.