|
| 1 | +#!/bin/bash -eu |
| 2 | +# Merge all .profraw files into a single .profdata file |
| 3 | +# Usage: ./collect-coverage.sh [COVERAGE_DIR] [OUTPUT_FILE] |
| 4 | + |
| 5 | +COVERAGE_DIR="${1:-${CLN_COVERAGE_DIR:-/tmp/cln-coverage}}" |
| 6 | +OUTPUT="${2:-coverage/merged.profdata}" |
| 7 | + |
| 8 | +echo "Collecting coverage from: $COVERAGE_DIR" |
| 9 | + |
| 10 | +# Find all profraw files |
| 11 | +mapfile -t PROFRAW_FILES < <(find "$COVERAGE_DIR" -name "*.profraw" 2>/dev/null || true) |
| 12 | + |
| 13 | +if [ ${#PROFRAW_FILES[@]} -eq 0 ]; then |
| 14 | + echo "ERROR: No .profraw files found in $COVERAGE_DIR" |
| 15 | + exit 1 |
| 16 | +fi |
| 17 | + |
| 18 | +echo "Found ${#PROFRAW_FILES[@]} profile files" |
| 19 | + |
| 20 | +# Validate each profraw file and filter out corrupt/incomplete ones |
| 21 | +# Define validation function for parallel execution |
| 22 | +validate_file() { |
| 23 | + local profraw="$1" |
| 24 | + |
| 25 | + # Check if file is empty |
| 26 | + if [ ! -s "$profraw" ]; then |
| 27 | + return 1 # Empty |
| 28 | + fi |
| 29 | + |
| 30 | + # Check if file is suspiciously small (likely incomplete write) |
| 31 | + # Valid profraw files are typically > 1KB |
| 32 | + filesize=$(stat -c%s "$profraw" 2>/dev/null || stat -f%z "$profraw" 2>/dev/null) |
| 33 | + if [ "$filesize" -lt 1024 ]; then |
| 34 | + return 2 # Too small |
| 35 | + fi |
| 36 | + |
| 37 | + # Try to validate the file by checking if llvm-profdata can read it |
| 38 | + if llvm-profdata show "$profraw" >/dev/null 2>&1; then |
| 39 | + echo "$profraw" # Valid - output to stdout |
| 40 | + return 0 |
| 41 | + else |
| 42 | + return 3 # Corrupt |
| 43 | + fi |
| 44 | +} |
| 45 | + |
| 46 | +# Export function for parallel execution |
| 47 | +export -f validate_file |
| 48 | + |
| 49 | +TOTAL=${#PROFRAW_FILES[@]} |
| 50 | +NPROC=$(nproc 2>/dev/null || echo 4) |
| 51 | +echo "Validating ${TOTAL} files in parallel (using ${NPROC} cores)..." |
| 52 | + |
| 53 | +# Run validation in parallel and collect valid files |
| 54 | +mapfile -t VALID_FILES < <( |
| 55 | + printf '%s\n' "${PROFRAW_FILES[@]}" | \ |
| 56 | + xargs -P "$NPROC" -I {} bash -c 'validate_file "$@"' _ {} |
| 57 | +) |
| 58 | + |
| 59 | +# Calculate error counts |
| 60 | +CORRUPT_COUNT=$((TOTAL - ${#VALID_FILES[@]})) |
| 61 | + |
| 62 | +if [ ${#VALID_FILES[@]} -eq 0 ]; then |
| 63 | + echo "ERROR: No valid .profraw files found (all $CORRUPT_COUNT files were corrupt/incomplete)" |
| 64 | + exit 1 |
| 65 | +fi |
| 66 | + |
| 67 | +echo "Valid files: ${#VALID_FILES[@]}" |
| 68 | +if [ $CORRUPT_COUNT -gt 0 ]; then |
| 69 | + echo "Filtered out: $CORRUPT_COUNT files (empty/small/corrupt)" |
| 70 | +fi |
| 71 | +mkdir -p "$(dirname "$OUTPUT")" |
| 72 | + |
| 73 | +# Merge with -sparse flag for efficiency |
| 74 | +# Use batched merging to avoid "Argument list too long" errors |
| 75 | +BATCH_SIZE=500 |
| 76 | +TOTAL_FILES=${#VALID_FILES[@]} |
| 77 | + |
| 78 | +if [ "$TOTAL_FILES" -le "$BATCH_SIZE" ]; then |
| 79 | + # Small enough to merge in one go |
| 80 | + echo "Merging ${TOTAL_FILES} files..." |
| 81 | + llvm-profdata merge -sparse "${VALID_FILES[@]}" -o "$OUTPUT" |
| 82 | +else |
| 83 | + # Need to merge in batches |
| 84 | + echo "Merging ${TOTAL_FILES} files in batches of ${BATCH_SIZE}..." |
| 85 | + |
| 86 | + # Create temp directory for intermediate files |
| 87 | + TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/profdata-merge.XXXXXX") |
| 88 | + trap 'rm -rf "$TEMP_DIR"' EXIT |
| 89 | + |
| 90 | + BATCH_NUM=0 |
| 91 | + INTERMEDIATE_FILES=() |
| 92 | + |
| 93 | + # Merge files in batches |
| 94 | + for ((i=0; i<TOTAL_FILES; i+=BATCH_SIZE)); do |
| 95 | + BATCH_NUM=$((BATCH_NUM + 1)) |
| 96 | + END=$((i + BATCH_SIZE)) |
| 97 | + if [ "$END" -gt "$TOTAL_FILES" ]; then |
| 98 | + END=$TOTAL_FILES |
| 99 | + fi |
| 100 | + |
| 101 | + BATCH_FILES=("${VALID_FILES[@]:$i:$BATCH_SIZE}") |
| 102 | + INTERMEDIATE="$TEMP_DIR/batch-$BATCH_NUM.profdata" |
| 103 | + |
| 104 | + echo " Batch $BATCH_NUM: merging files $((i+1))-$END..." |
| 105 | + llvm-profdata merge -sparse "${BATCH_FILES[@]}" -o "$INTERMEDIATE" |
| 106 | + INTERMEDIATE_FILES+=("$INTERMEDIATE") |
| 107 | + done |
| 108 | + |
| 109 | + # Merge all intermediate files into final output |
| 110 | + echo "Merging ${#INTERMEDIATE_FILES[@]} intermediate files into final output..." |
| 111 | + llvm-profdata merge -sparse "${INTERMEDIATE_FILES[@]}" -o "$OUTPUT" |
| 112 | + |
| 113 | + # Cleanup handled by trap |
| 114 | +fi |
| 115 | + |
| 116 | +echo "✓ Merged profile: $OUTPUT" |
0 commit comments