写了一个 Linux 命令行工具,用来执行一个命令并追踪后代进程的创建

2023-08-02 10:25:00 +08:00
 ppxppx

github: cmdcat

原理就是通过LD_PRELOAD环境变量 hook 掉 exec 和 fork 调用,然后可以将这些调用的参数和环境变量通过进程通信的方式传回给 cmdcat 进程。

写这个工具的原因是有的 C/C++项目的构建比较复杂,用这个工具可以知道某个对象是怎么构建出来的。并且 cmdcat 也可以用来生成compile_commands.json,借鉴了bear

cmdcat 的输出是 json 格式,描述创建后代进程的命令行参数和环境变量。下面是执行cmdcat make的一部分输出样例:

...
                {
                    "args": {
                        "0": "-s",
                        "1": "-f",
                        "2": "CMakeFiles/execfork.dir/build.make",
                        "3": "CMakeFiles/execfork.dir/build"
                    },
                    "children": [],
                    "cmd": "/usr/bin/make",
                    "cwd": "/home/prack/cmdcat/build",
                    "envs": {
                        "AUTOFEATURE": "true autotest",
                        "BASH_IT": "/home/prack/conf/otherRepo/bash-it",
                        "BASH_IT_THEME": "bobby",
                        "CMDCAT_SERVER_DOMAIN": "AF_UNIX",
                        "CMDCAT_SERVER_PATH": "/tmp/socket-yyjhgqrwdkxwzaxxirgcp",
                        "CMDCAT_SERVER_PORT": "0",
                        "CMDCAT_SERVER_TYPE": "SOCK_DGRAM",
                        "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
                        "GIT_HOSTING": "git@git.domain.com",
                        "GREP_COLOR": "1;33",
                        "HISTCONTROL": "ignorespace:erasedups",
                        "HISTSIZE": "5000",
                        "HOME": "/home/prack",
                        "IRC_CLIENT": "irssi",
                        "LANG": "en_US.UTF-8",
                        "LD_PRELOAD": "/tmp/libccat_yyjhgqrwdkxwzaxxirgc.so",
                        "LOGNAME": "prack",
                        "LSCOLORS": "Gxfxcxdxdxegedabagacad",
                        "MAKEFLAGS": "s",
                        "MAKELEVEL": "2",
                        "MAKE_TERMERR": "/dev/pts/13",
                        "MFLAGS": "-s",
                        "MOTD_SHOWN": "pam",
                        "OLDPWD": "/home/prack/cmdcat",
                        "PATH": "/home/prack/.local/bin:/home/prack/local/bin:/home/prack/.node/bin:/home/prack/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
                        "PWD": "/home/prack/cmdcat/build",
                        "SCM_CHECK": "true",
                        "SHELL": "/bin/bash",
                        "SHLVL": "1",
                        "SSH_CLIENT": "192.168.44.1 54209 22",
                        "SSH_CONNECTION": "192.168.44.1 59849 192.168.44.43 22",
                        "SSH_TTY": "/dev/pts/0",
                        "TERM": "screen-256color",
                        "TERM_PROGRAM": "tmux",
                        "TERM_PROGRAM_VERSION": "3.2a",
                        "TMUX": "/tmp/tmux-1000/default,2768,1",
                        "TMUX_PANE": "%20",
                        "TMUX_PLUGIN_MANAGER_PATH": "/home/prack/.tmux/plugins/",
                        "TODO": "t",
                        "USER": "prack",
                        "XDG_DATA_DIRS": "/usr/local/share:/usr/share:/var/lib/snapd/desktop",
                        "XDG_RUNTIME_DIR": "/run/user/1000",
                        "XDG_SESSION_CLASS": "user",
                        "XDG_SESSION_ID": "4",
                        "XDG_SESSION_TYPE": "tty",
                        "_": "/home/prack/local/bin/cmdcat"
                    },
                    "fullcmd": "/usr/bin/make -s -f CMakeFiles/execfork.dir/build.make CMakeFiles/execfork.dir/build",
                    "history": {
                        "0": {
                            "argv": {
                                "0": "-s",
                                "1": "-f",
                                "2": "CMakeFiles/Makefile2",
                                "3": "all"
                            },
                            "cmd": "/usr/bin/make"
                        }
                    },
                    "nchild": 0,
                    "pid": 330249,
                    "ppid": 330215,
                    "uid": 39
                },
                {
                    "args": {
                        "0": "-c",
                        "1": "/usr/bin/cmake -E cmake_echo_color --switch= --progress-dir=/home/prack/cmdcat/build/CMakeFiles --progress-num=8,9 \"Built target execfork\""
                    },
                    "children": [
                        {
                            "args": {
                                "0": "-E",
                                "1": "cmake_echo_color",
                                "2": "--switch=",
                                "3": "--progress-dir=/home/prack/cmdcat/build/CMakeFiles",
                                "4": "--progress-num=8,9",
                                "5": "Built target execfork"
                            },
                            "children": [],
                            "cmd": "/usr/bin/cmake",
                            "cwd": "/home/prack/cmdcat/build",
                            "envs": {
                                "AUTOFEATURE": "true autotest",
                                "BASH_IT": "/home/prack/conf/otherRepo/bash-it",
                                "BASH_IT_THEME": "bobby",
                                "CMDCAT_SERVER_DOMAIN": "AF_UNIX",
                                "CMDCAT_SERVER_PATH": "/tmp/socket-yyjhgqrwdkxwzaxxirgcp",
                                "CMDCAT_SERVER_PORT": "0",
                                "CMDCAT_SERVER_TYPE": "SOCK_DGRAM",
                                "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
                                "GIT_HOSTING": "git@git.domain.com",
                                "GREP_COLOR": "1;33",
                                "HISTCONTROL": "ignorespace:erasedups",
                                "HISTSIZE": "5000",
                                "HOME": "/home/prack",
                                "IRC_CLIENT": "irssi",
                                "LANG": "en_US.UTF-8",
                                "LD_PRELOAD": "/tmp/libccat_yyjhgqrwdkxwzaxxirgc.so",
                                "LOGNAME": "prack",
                                "LSCOLORS": "Gxfxcxdxdxegedabagacad",
                                "MAKEFLAGS": "s",
                                "MAKELEVEL": "2",
                                "MAKE_TERMERR": "/dev/pts/13",
                                "MFLAGS": "-s",
                                "MOTD_SHOWN": "pam",
                                "OLDPWD": "/home/prack/cmdcat",
                                "PATH": "/home/prack/.local/bin:/home/prack/local/bin:/home/prack/.node/bin:/home/prack/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
                                "PWD": "/home/prack/cmdcat/build",
                                "SCM_CHECK": "true",
                                "SHELL": "/bin/bash",
                                "SHLVL": "1",
                                "SSH_CLIENT": "192.168.44.1 54209 22",
                                "SSH_CONNECTION": "192.168.44.1 59849 192.168.44.43 22",
                                "SSH_TTY": "/dev/pts/0",
                                "TERM": "screen-256color",
                                "TERM_PROGRAM": "tmux",
                                "TERM_PROGRAM_VERSION": "3.2a",
                                "TMUX": "/tmp/tmux-1000/default,2768,1",
                                "TMUX_PANE": "%20",
                                "TMUX_PLUGIN_MANAGER_PATH": "/home/prack/.tmux/plugins/",
                                "TODO": "t",
                                "USER": "prack",
                                "XDG_DATA_DIRS": "/usr/local/share:/usr/share:/var/lib/snapd/desktop",
                                "XDG_RUNTIME_DIR": "/run/user/1000",
                                "XDG_SESSION_CLASS": "user",
                                "XDG_SESSION_ID": "4",
                                "XDG_SESSION_TYPE": "tty",
                                "_": "/home/prack/local/bin/cmdcat"
                            },
                            "fullcmd": "/usr/bin/cmake -E cmake_echo_color --switch= --progress-dir=/home/prack/cmdcat/build/CMakeFiles --progress-num=8,9 Built target execfork",
                            "history": {
                                "0": {
                                    "argv": {
                                        "0": "-c",
                                        "1": "/usr/bin/cmake -E cmake_echo_color --switch= --progress-dir=/home/prack/cmdcat/build/CMakeFiles --progress-num=8,9 \"Built target execfork\""
                                    },
                                    "cmd": "/bin/sh"
                                }
                            },
                            "nchild": 0,
                            "pid": 330251,
                            "ppid": 330250,
                            "uid": 41
                        }
                    ],

ubuntu20.04 的用户可以直接下载构建好的二进制文件release,其他 linux 系统的没有验证过。

1516 次点击
所在节点    分享创造
6 条回复
sakeven
2023-08-02 11:38:29 +08:00
用 ebpf 跟踪更好。LD_PRELOAD 对静态链接无效。
julyclyde
2023-08-02 13:09:14 +08:00
重新发明了世界

用 cgroups 不就得了
err1y
2023-08-02 17:53:02 +08:00
类似 strace
weiwenhao
2023-08-02 18:02:19 +08:00
cmdcat A -> B, B 启动 C 之后,B 自己退出, 然后 C 再次启动新的程序能够追踪到么。
ppxppx
2023-08-02 18:15:26 +08:00
@weiwenhao 能的
fuis
2023-08-03 14:00:03 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/961721

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX