cmakefilevalgrindaddress-sanitizer

Enable/disable flags when doing a valgrind build


I have two targets in my Makefile, namely:

all: $(TARGET)

valgrind: $(TARGET)
    ...
    ...
    ...

The valgrind target simply runs the executable with the valgrind program. The CFLAGS for this Makefile includes:

CFLAGS += -fsanitize=address
CFLAGS += -fsanitize=undefined
CFLAGS += -fsanitize=bounds-strict
CFLAGS += -fsanitize=leak
CFLAGS += -fsanitize=null
CFLAGS += -fsanitize=signed-integer-overflow
CFLAGS += -fsanitize=bool
CFLAGS += -fsanitize=pointer-overflow
CFLAGS += -fsanitize-address-use-after-scope

CFLAGS += -fanalyzer

When doing make valgrind (assuming the target was already built), I have to first run make clean (this simply removes *.obj/ and $(TARGET), and then comment out these flags because Valgrind and sanitizers do not work together. And then uncomment them back for the next build, and so on.

How can I make the valgrind target unconditionally rebuild the target each time but with the sanitizers disabled?

The whole Makefile (with some fluff removed):

CC = gcc-13

CFLAGS += -DBENCHMARKING

CFLAGS += -O3
CFLAGS += -std=c2x
CFLAGS += -s
CFLAGS += -no-pie

CFLAGS += -fno-builtin
CFLAGS += -fno-common
CFLAGS += -fno-omit-frame-pointer

CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Warray-bounds
CFLAGS += -Wconversion
CFLAGS += -Wformat-signedness
CFLAGS += -Wno-parentheses
CFLAGS += -Wpedantic
CFLAGS += -pendatic-errors
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wwrite-strings
CFLAGS += -Wno-missing-braces
CFLAGS += -Wno-missing-field-initializers

CFLAGS += -fsanitize=address
CFLAGS += -fsanitize=undefined
CFLAGS += -fsanitize=bounds-strict
CFLAGS += -fsanitize=leak
CFLAGS += -fsanitize=null
CFLAGS += -fsanitize=signed-integer-overflow
CFLAGS += -fsanitize=bool
CFLAGS += -fsanitize=pointer-overflow
CFLAGS += -fsanitize-address-use-after-scope

CFLAGS += -fanalyzer

SRC_DIR = src
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJ_DIR = obj
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
DEPS = $(OBJS:.o=.d)

TARGET = read_file

all: $(TARGET)
    
$(TARGET): $(OBJ_DIR) $(OBJS)
    $(CC) $(CFLAGS) -o $@ $(OBJS)

$(OBJ_DIR):
    $(MKDIR) $(OBJ_DIR)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
    $(CC) $(CFLAGS) -MMD -c -o $@ $<

-include $(DEPS)

valgrind: $(TARGET)
    valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --mmap_memchr $(TARGET)
    valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --getline $(TARGET)
    valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --mmap_getline $(TARGET)
    valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --fread $(TARGET)

Solution

  • One way is to use MAKECMDGOALS and look for valgrind and only add the -fsanitize* options if the goals do not have valgrind in them.

    To test this:

    1. I had to cosmetically clean up your Makefile to run on my system (e.g. no gcc-13) and some mispelled options.
    2. I added a clean target and made it a prereq for all and valgrind
    3. For good measure, I added some: .PHONY: whatever lines
    4. I used three dummy sources: src/main.c, src/foo.c, and src/bar.c

    So, build with either:

    1. make
    2. make valgrind

    Here is the modified Makefile:

    ###CC = gcc-13
    CC = gcc
    MKDIR = mkdir -p
    
    CFLAGS += -DBENCHMARKING
    
    CFLAGS += -O3
    ###CFLAGS += -std=c2x
    CFLAGS += -std=c1x
    CFLAGS += -s
    CFLAGS += -no-pie
    
    CFLAGS += -fno-builtin
    CFLAGS += -fno-common
    CFLAGS += -fno-omit-frame-pointer
    
    CFLAGS += -Wall
    CFLAGS += -Wextra
    CFLAGS += -Warray-bounds
    CFLAGS += -Wconversion
    CFLAGS += -Wformat-signedness
    CFLAGS += -Wno-parentheses
    CFLAGS += -Wpedantic
    CFLAGS += -pedantic-errors
    CFLAGS += -Wstrict-prototypes
    CFLAGS += -Wwrite-strings
    CFLAGS += -Wno-missing-braces
    CFLAGS += -Wno-missing-field-initializers
    
    # add sanitize options if _not_ building for valgrind
    ifeq ($(findstring valgrind,$(MAKECMDGOALS)),)
      CFLAGS += -fsanitize=address
      CFLAGS += -fsanitize=undefined
      CFLAGS += -fsanitize=bounds-strict
      CFLAGS += -fsanitize=leak
      CFLAGS += -fsanitize=null
      CFLAGS += -fsanitize=signed-integer-overflow
      CFLAGS += -fsanitize=bool
      CFLAGS += -fsanitize=pointer-overflow
      CFLAGS += -fsanitize-address-use-after-scope
      ###CFLAGS += -fanalyzer
    endif
    
    SRC_DIR = src
    SRCS = $(wildcard $(SRC_DIR)/*.c)
    OBJ_DIR = obj
    OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
    DEPS = $(OBJS:.o=.d)
    
    TARGET = read_file
    
    .PHONY: all
    all: clean $(TARGET)
    
    $(TARGET): $(OBJ_DIR) $(OBJS)
        $(CC) $(CFLAGS) -o $@ $(OBJS)
    
    $(OBJ_DIR):
        $(MKDIR) $(OBJ_DIR)
    
    $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
        $(CC) $(CFLAGS) -MMD -c -o $@ $<
    
    -include $(DEPS)
    
    .PHONY: valgrind
    valgrind: clean $(TARGET)
        valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --mmap_memchr $(TARGET)
        valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --getline $(TARGET)
        valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --mmap_getline $(TARGET)
        valgrind --tool=memcheck --leak-check=yes ./$(TARGET) --fread $(TARGET)
    
    .PHONY: clean
    clean:
        rm -fr $(TARGET) $(OBJ_DIR)
    

    Here is the output of make:

    rm -fr read_file obj
    mkdir -p obj
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=leak -fsanitize=null -fsanitize=signed-integer-overflow -fsanitize=bool -fsanitize=pointer-overflow -fsanitize-address-use-after-scope -MMD -c -o obj/foo.o src/foo.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=leak -fsanitize=null -fsanitize=signed-integer-overflow -fsanitize=bool -fsanitize=pointer-overflow -fsanitize-address-use-after-scope -MMD -c -o obj/main.o src/main.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=leak -fsanitize=null -fsanitize=signed-integer-overflow -fsanitize=bool -fsanitize=pointer-overflow -fsanitize-address-use-after-scope -MMD -c -o obj/bar.o src/bar.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=leak -fsanitize=null -fsanitize=signed-integer-overflow -fsanitize=bool -fsanitize=pointer-overflow -fsanitize-address-use-after-scope -o read_file obj/foo.o obj/main.o obj/bar.o
    

    Here is the output of make valgrind:

    rm -fr read_file obj
    mkdir -p obj
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -MMD -c -o obj/foo.o src/foo.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -MMD -c -o obj/main.o src/main.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -MMD -c -o obj/bar.o src/bar.c
    gcc -DBENCHMARKING -O3 -std=c1x -s -no-pie -fno-builtin -fno-common -fno-omit-frame-pointer -Wall -Wextra -Warray-bounds -Wconversion -Wformat-signedness -Wno-parentheses -Wpedantic -pedantic-errors -Wstrict-prototypes -Wwrite-strings -Wno-missing-braces -Wno-missing-field-initializers -o read_file obj/foo.o obj/main.o obj/bar.o
    valgrind --tool=memcheck --leak-check=yes ./read_file --mmap_memchr read_file
    ==1519900== Memcheck, a memory error detector
    ==1519900== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==1519900== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==1519900== Command: ./read_file --mmap_memchr read_file
    ==1519900==
    foo: hello
    bar: hello
    ==1519900==
    ==1519900== HEAP SUMMARY:
    ==1519900==     in use at exit: 0 bytes in 0 blocks
    ==1519900==   total heap usage: 1 allocs, 1 frees, 4,096 bytes allocated
    ==1519900==
    ==1519900== All heap blocks were freed -- no leaks are possible
    ==1519900==
    ==1519900== For lists of detected and suppressed errors, rerun with: -s
    ==1519900== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    valgrind --tool=memcheck --leak-check=yes ./read_file --getline read_file
    ==1519902== Memcheck, a memory error detector
    ==1519902== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==1519902== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==1519902== Command: ./read_file --getline read_file
    ==1519902==
    foo: hello
    bar: hello
    ==1519902==
    ==1519902== HEAP SUMMARY:
    ==1519902==     in use at exit: 0 bytes in 0 blocks
    ==1519902==   total heap usage: 1 allocs, 1 frees, 4,096 bytes allocated
    ==1519902==
    ==1519902== All heap blocks were freed -- no leaks are possible
    ==1519902==
    ==1519902== For lists of detected and suppressed errors, rerun with: -s
    ==1519902== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    valgrind --tool=memcheck --leak-check=yes ./read_file --mmap_getline read_file
    ==1519903== Memcheck, a memory error detector
    ==1519903== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==1519903== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==1519903== Command: ./read_file --mmap_getline read_file
    ==1519903==
    foo: hello
    bar: hello
    ==1519903==
    ==1519903== HEAP SUMMARY:
    ==1519903==     in use at exit: 0 bytes in 0 blocks
    ==1519903==   total heap usage: 1 allocs, 1 frees, 4,096 bytes allocated
    ==1519903==
    ==1519903== All heap blocks were freed -- no leaks are possible
    ==1519903==
    ==1519903== For lists of detected and suppressed errors, rerun with: -s
    ==1519903== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    valgrind --tool=memcheck --leak-check=yes ./read_file --fread read_file
    ==1519904== Memcheck, a memory error detector
    ==1519904== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==1519904== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==1519904== Command: ./read_file --fread read_file
    ==1519904==
    foo: hello
    bar: hello
    ==1519904==
    ==1519904== HEAP SUMMARY:
    ==1519904==     in use at exit: 0 bytes in 0 blocks
    ==1519904==   total heap usage: 1 allocs, 1 frees, 4,096 bytes allocated
    ==1519904==
    ==1519904== All heap blocks were freed -- no leaks are possible
    ==1519904==
    ==1519904== For lists of detected and suppressed errors, rerun with: -s
    ==1519904== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    Merely for completeness, here are the dummy source files I created:

    ==> src/bar.c <==
    #include <stdio.h>
    
    void
    bar(void)
    {
        printf("bar: hello\n");
    }
    
    ==> src/foo.c <==
    #include <stdio.h>
    
    void
    foo(void)
    {
        printf("foo: hello\n");
    }
    
    ==> src/main.c <==
    #include <stdio.h>
    
    void foo(void);
    void bar(void);
    
    int
    main(void)
    {
        foo();
        bar();
        return 0;
    }