如何查看递归Makefile的完整父进程调用树(含参数)?
给递归Makefile插桩获取完整调用链的方案
嘿,我之前也折腾过递归Make的调用链追踪,刚好能给你一个实用的解决办法——直接在Makefile里加一段插桩逻辑,就能输出从顶层Make到当前进程的完整调用树,包含所有命令参数,完全符合你要的类似pstree -ha的效果,还能避开pstree的问题。
核心思路
我们利用shell的进程遍历能力,结合Make的内置执行环境,从当前Make进程往上追溯所有父级Make进程,把整个调用链收集起来再格式化输出。这样不管递归多少层,都能清晰看到每一步的调用参数。
具体实现步骤
1. 在Makefile中定义调用链打印函数
把这段代码加到你的根Makefile(或者所有需要插桩的Makefile)里:
# 定义打印Make调用链的函数,会输出从顶层到当前的完整调用路径(含参数) define PRINT_MAKE_CALL_CHAIN @( \ # 获取当前Make进程的PID(因为shell是Make的子进程,所以PPID就是Make的PID) current_make_pid=$$PPID; \ # 用数组存储调用链,从顶层到当前的顺序 call_chain=(); \ # 向上遍历父进程,收集所有Make进程 while true; do \ # 获取当前进程的命令行(含完整参数) cmd_line=$$(ps -o cmd= -p $$current_make_pid); \ # 检查是否是Make进程(通过命令行开头是否为make来过滤,可根据实际调整) if echo "$$cmd_line" | grep -qE '^make(\s|$)'; then \ # 把当前进程放到数组开头(因为我们从下往上找,最后要倒序) call_chain=("$$cmd_line" "$${call_chain[@]}"); \ # 获取父进程PID parent_pid=$$(ps -o ppid= -p $$current_make_pid | tr -d '[:space:]'); \ # 终止条件:父进程是init(PID=1)或者不是Make进程 if [ "$$parent_pid" -eq 1 ] || ! echo "$$(ps -o cmd= -p $$parent_pid)" | grep -qE '^make(\s|$)'; then break; fi; # 切换到父进程继续遍历 current_make_pid=$$parent_pid; else break; fi; done; \ # 输出格式化的调用链 echo -e "\n=== 当前Make调用链 ==="; indent=""; for entry in "$${call_chain[@]}"; do echo "$${indent}└── $$entry"; indent="$${indent} "; done; echo -e "=====================\n"; ) endef
2. 插入到需要追踪的位置
你可以选择两种方式插桩:
方式一:全局前置(所有目标执行前都打印)
定义一个伪目标,让所有目标依赖它:
.PHONY: track-call-chain track-call-chain: $(PRINT_MAKE_CALL_CHAIN) # 把track-call-chain作为所有目标的前置依赖,比如默认的all目标 all: track-call-chain your-actual-targets # 其他目标也可以加上这个依赖 subdir-build: track-call-chain $(MAKE) -C subdirectory
方式二:特定规则内插桩
如果只需要追踪某个特定的递归调用,直接在对应规则里调用函数即可:
build-module: # 先打印调用链再执行命令 $(PRINT_MAKE_CALL_CHAIN) $(MAKE) -C modules/moduleA CONFIG_DEBUG=1
效果示例
假设你有三层递归调用:
make all → make -C src → make -C src/lib
那么在最底层的Make进程里,输出会是这样:
=== 当前Make调用链 === └── make all └── make -C src └── make -C src/lib CONFIG_DEBUG=1 =====================
优化建议
- 如果你的环境里Make命令可能带路径(比如
/usr/bin/make),可以把过滤规则改成grep -qE '(^|/)make(\s|$)',避免漏判。 - 要是想把调用链保存到日志文件,把echo的内容重定向到日志即可,比如
echo "... >> make_call_chain.log"。 - 可以给每个Make调用加自定义标识,比如递归时传递
$(MAKE) -C subdir CALL_LEVEL=$(call inc,$(CALL_LEVEL)),这样在调用链里能更直观看到层级。
内容的提问来源于stack exchange,提问作者HardcoreHenry
相关产品推荐
相关产品推荐

