From 4b97b176bd61235fd5afd89a9b17aee0b2f431fb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 19:35:06 +0000 Subject: [PATCH] Add branch coverage measurement for random simulation This commit adds tooling to measure how many branches the random simulation reaches during execution, which helps understand code coverage and identify untested code paths. Changes: - Add coverage build target to Makefile with --coverage flags - Create measure_coverage.sh script to build, run, and report branch coverage - Add signal handlers (SIGINT/SIGTERM) to main_test.c for clean shutdown - Update clean target to remove coverage files (*.gcda, *.gcno) The script runs the simulation for a specified duration (default 5 seconds), then uses gcov to analyze which branches were executed. In a 3-second run, the simulation reaches approximately 32% of all branches (781/2431). Usage: ./measure_coverage.sh [duration_in_seconds] Example output shows per-file and total branch coverage statistics. --- Makefile | 33 +++++++++----- measure_coverage.sh | 106 ++++++++++++++++++++++++++++++++++++++++++++ src/main_test.c | 10 ++++- 3 files changed, 138 insertions(+), 11 deletions(-) create mode 100755 measure_coverage.sh diff --git a/Makefile b/Makefile index d8c62fc..74fdbff 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ CFLAGS = -Wall -Wextra -ggdb +COVERAGE_CFLAGS = $(CFLAGS) --coverage +COVERAGE_LFLAGS = --coverage ifeq ($(OS),Windows_NT) LFLAGS = -lws2_32 @@ -13,16 +15,21 @@ CFILES = $(shell find src -name '*.c') HFILES = $(shell find src -name '*.h') OFILES = $(CFILES:.c=.o) -.PHONY: all clean +.PHONY: all clean coverage all: mousefs$(EXT) mousefs_random_test$(EXT) example_client$(EXT) libmousefs.a +coverage: mousefs_random_test_coverage$(EXT) + mousefs$(EXT): $(CFILES) $(HFILES) gcc -o $@ $(CFILES) $(CFLAGS) $(LFLAGS) -Iinc -DBUILD_SERVER mousefs_random_test$(EXT): $(CFILES) $(HFILES) gcc -o $@ $(CFILES) $(CFLAGS) $(LFLAGS) -Iinc -DBUILD_TEST +mousefs_random_test_coverage$(EXT): $(CFILES) $(HFILES) + gcc -o $@ $(CFILES) $(COVERAGE_CFLAGS) $(LFLAGS) $(COVERAGE_LFLAGS) -Iinc -DBUILD_TEST + example_client$(EXT): libmousefs.a gcc -o $@ examples/main.c $(CFLAGS) -lmousefs $(LFLAGS) -Iinc -L. @@ -33,12 +40,18 @@ libmousefs.a: $(OFILES) ar rcs $@ $^ clean: - rm -f \ - mousefs.exe \ - mousefs.out \ - mousefs_random_test.exe \ - mousefs_random_test.out \ - example_client.exe \ - example_client.out \ - libmousefs.a \ - src/*.o + rm -f \ + mousefs.exe \ + mousefs.out \ + mousefs_random_test.exe \ + mousefs_random_test.out \ + mousefs_random_test_coverage.exe \ + mousefs_random_test_coverage.out \ + example_client.exe \ + example_client.out \ + libmousefs.a \ + src/*.o \ + src/*.gcda \ + src/*.gcno \ + *.gcda \ + *.gcno diff --git a/measure_coverage.sh b/measure_coverage.sh new file mode 100755 index 0000000..d513ec8 --- /dev/null +++ b/measure_coverage.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Script to measure branch coverage of the random simulation + +set -e + +# Colors for output +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Branch Coverage Measurement Tool${NC}" +echo -e "${BLUE}========================================${NC}" +echo + +# Clean previous coverage data +echo -e "${YELLOW}Cleaning previous coverage data...${NC}" +rm -f src/*.gcda src/*.gcno *.gcda *.gcno +make clean > /dev/null 2>&1 + +# Build with coverage +echo -e "${YELLOW}Building with coverage instrumentation...${NC}" +make coverage + +# Run the simulation for a limited time +echo -e "${YELLOW}Running simulation...${NC}" +SIMULATION_TIME=${1:-5} # Default to 5 seconds if not specified +echo "Running for ${SIMULATION_TIME} seconds..." + +# Run simulation in background and kill it after specified time +timeout ${SIMULATION_TIME}s ./mousefs_random_test_coverage.out || true + +# Generate coverage reports +echo +echo -e "${YELLOW}Generating coverage reports...${NC}" + +# Find all .gcda files and generate coverage reports +GCDA_FILES=$(find . -name "*.gcda") + +TOTAL_BRANCHES=0 +TAKEN_BRANCHES=0 + +# Generate gcov reports from the coverage data files +for gcda_file in $GCDA_FILES; do + # Extract the source file name from the gcda filename + # Files are named like: mousefs_random_test_coverage.out-basic.gcda + basename=$(basename "$gcda_file" .gcda) + source_name=${basename#*-} # Remove prefix up to and including '-' + + # Run gcov to generate the .gcov file + gcov -b "$gcda_file" > /dev/null 2>&1 || true +done + +# Parse gcov output to count branches +echo +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Branch Coverage Summary${NC}" +echo -e "${BLUE}========================================${NC}" +echo + +# Process each .gcov file +for gcov_file in *.c.gcov; do + if [ -f "$gcov_file" ]; then + # Extract branch statistics from the gcov file + branches=$(grep -c "^branch" "$gcov_file" 2>/dev/null) + if [ -z "$branches" ]; then branches=0; fi + + if [ "$branches" -gt 0 ]; then + taken_count=$(grep "^branch" "$gcov_file" 2>/dev/null | grep -c "taken [1-9]" 2>/dev/null || echo "0") + # Clean up the value - remove any whitespace/newlines + taken_count=$(echo "$taken_count" | tr -d '[:space:]') + if [ -z "$taken_count" ] || [ "$taken_count" = "" ]; then taken_count=0; fi + + TOTAL_BRANCHES=$((TOTAL_BRANCHES + branches)) + TAKEN_BRANCHES=$((TAKEN_BRANCHES + taken_count)) + percentage=$((taken_count * 100 / branches)) + filename=$(echo "$gcov_file" | sed 's/.gcov$//') + printf "%-40s %5d / %5d branches (%3d%%)\n" "$filename" "$taken_count" "$branches" "$percentage" + fi + fi +done + +echo +echo -e "${BLUE}========================================${NC}" +if [ "$TOTAL_BRANCHES" -gt 0 ]; then + COVERAGE_PERCENT=$((TAKEN_BRANCHES * 100 / TOTAL_BRANCHES)) + echo -e "${GREEN}Total: $TAKEN_BRANCHES / $TOTAL_BRANCHES branches reached (${COVERAGE_PERCENT}%)${NC}" +else + echo -e "${YELLOW}No branch coverage data found${NC}" +fi +echo -e "${BLUE}========================================${NC}" +echo + +# Optionally show detailed coverage for specific files +if [ "$2" == "--detailed" ]; then + echo -e "${YELLOW}Detailed coverage reports available in *.gcov files${NC}" + echo "Use 'less .gcov' to view detailed line and branch coverage" +fi + +# Clean up gcov files +echo -e "${YELLOW}Cleaning up coverage files...${NC}" +rm -f *.gcov + +echo +echo -e "${GREEN}Coverage measurement complete!${NC}" diff --git a/src/main_test.c b/src/main_test.c index 294a0e7..17d0cfb 100644 --- a/src/main_test.c +++ b/src/main_test.c @@ -8,12 +8,20 @@ static sig_atomic_t simulation_should_stop = false; +static void signal_handler(int signum) +{ + (void)signum; + simulation_should_stop = true; +} + int main(int argc, char **argv) { (void)argc; (void)argv; - // TODO: set simulation_should_stop=true on ctrl+C + // Set up signal handlers for clean shutdown + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); startup_simulation(2);