Valgrind构建时自动禁用Sanitizer并强制重编译目标的方法
Valgrind与Sanitizer兼容问题的Makefile优化方案
问题背景
当前Makefile中valgrind目标依赖默认编译的$(TARGET),但默认编译启用了AddressSanitizer等一系列Sanitizer选项,而Valgrind与这些Sanitizer无法兼容。每次运行make valgrind都需要手动执行make clean、注释Sanitizer选项,完成后还要恢复注释,操作繁琐。需要实现:
valgrind目标触发时,无条件重编译目标程序- 编译时自动禁用所有Sanitizer选项
解决方案
核心思路是为Valgrind单独构建一个不带Sanitizer的目标程序,避免与默认编译产物冲突,同时强制每次构建。以下是修改后的完整Makefile:
CC = gcc-13 # 基础编译参数(不含Sanitizer) BASE_CFLAGS += -DBENCHMARKING BASE_CFLAGS += -O3 BASE_CFLAGS += -std=c2x BASE_CFLAGS += -s BASE_CFLAGS += -no-pie BASE_CFLAGS += -fno-builtin BASE_CFLAGS += -fno-common BASE_CFLAGS += -fno-omit-frame-pointer BASE_CFLAGS += -Wall BASE_CFLAGS += -Wextra BASE_CFLAGS += -Warray-bounds BASE_CFLAGS += -Wconversion BASE_CFLAGS += -Wformat-signedness BASE_CFLAGS += -Wno-parentheses BASE_CFLAGS += -Wpedantic BASE_CFLAGS += -pedantic-errors BASE_CFLAGS += -Wstrict-prototypes BASE_CFLAGS += -Wwrite-strings BASE_CFLAGS += -Wno-missing-braces BASE_CFLAGS += -Wno-missing-field-initializers # Sanitizer专属参数 SANITIZER_FLAGS += -fsanitize=address SANITIZER_FLAGS += -fsanitize=undefined SANITIZER_FLAGS += -fsanitize=bounds-strict SANITIZER_FLAGS += -fsanitize=leak SANITIZER_FLAGS += -fsanitize=null SANITIZER_FLAGS += -fsanitize=signed-integer-overflow SANITIZER_FLAGS += -fsanitize=bool SANITIZER_FLAGS += -fsanitize=pointer-overflow SANITIZER_FLAGS += -fsanitize-address-use-after-scope SANITIZER_FLAGS += -fanalyzer # 默认编译包含Sanitizer CFLAGS = $(BASE_CFLAGS) $(SANITIZER_FLAGS) SRC_DIR = src SRCS = $(wildcard $(SRC_DIR)/*.c) OBJ_DIR = obj VALGRIND_OBJ_DIR = obj-valgrind OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS)) VALGRIND_OBJS = $(patsubst $(SRC_DIR)/%.c,$(VALGRIND_OBJ_DIR)/%.o,$(SRCS)) DEPS = $(OBJS:.o=.d) VALGRIND_DEPS = $(VALGRIND_OBJS:.o=.d) TARGET = read_file VALGRIND_TARGET = $(TARGET)-valgrind all: $(TARGET) # 默认带Sanitizer的目标 $(TARGET): $(OBJ_DIR) $(OBJS) $(CC) $(CFLAGS) -o $@ $(OBJS) # Valgrind专用目标(不带Sanitizer) $(VALGRIND_TARGET): $(VALGRIND_OBJ_DIR) $(VALGRIND_OBJS) $(CC) $(BASE_CFLAGS) -o $@ $(VALGRIND_OBJS) $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(VALGRIND_OBJ_DIR): mkdir -p $(VALGRIND_OBJ_DIR) # 默认编译规则 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(CC) $(CFLAGS) -MMD -c -o $@ $< # Valgrind专属编译规则 $(VALGRIND_OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(CC) $(BASE_CFLAGS) -MMD -c -o $@ $< -include $(DEPS) -include $(VALGRIND_DEPS) # Valgrind目标:依赖专用编译产物,强制每次构建 valgrind: FORCE $(VALGRIND_TARGET) valgrind --tool=memcheck --leak-check=yes ./$(VALGRIND_TARGET) --mmap_memchr $(TARGET) valgrind --tool=memcheck --leak-check=yes ./$(VALGRIND_TARGET) --getline $(TARGET) valgrind --tool=memcheck --leak-check=yes ./$(VALGRIND_TARGET) --mmap_getline $(TARGET) valgrind --tool=memcheck --leak-check=yes ./$(VALGRIND_TARGET) --fread $(TARGET) # 强制目标:确保valgrind每次都触发重新编译 FORCE: # 扩展clean目标,清理所有产物 clean: rm -rf $(OBJ_DIR) $(VALGRIND_OBJ_DIR) $(TARGET) $(VALGRIND_TARGET)
关键修改说明
- 拆分编译参数:将基础编译选项和Sanitizer选项分离,默认
CFLAGS包含两者,Valgrind编译时仅使用基础参数。 - 独立产物目录:为Valgrind版本的目标文件创建单独的
obj-valgrind目录,避免与默认编译的目标文件冲突,无需手动clean默认产物。 - 专用目标程序:生成
$(TARGET)-valgrind作为Valgrind专用的可执行文件,与默认的$(TARGET)共存,互不影响。 - 强制重编译:通过
FORCE伪目标,确保每次执行make valgrind都会重新编译Valgrind版本的程序,无需手动触发clean。 - 扩展clean:统一清理所有编译产物,包括默认和Valgrind版本的目标文件与可执行程序。
现在执行make valgrind即可直接编译不带Sanitizer的版本并运行Valgrind,无需任何手动修改;执行make all则保持默认带Sanitizer的编译流程。
内容的提问来源于stack exchange,提问作者Madagascar
相关产品推荐
相关产品推荐

