summaryrefslogtreecommitdiff
path: root/Makefile
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-26 13:49:38 +0300
committerPaul Buetow <paul@buetow.org>2025-06-26 13:49:38 +0300
commit6664996ced62c77e0c62bc1619662cbed7fccff6 (patch)
treeb995d8aa34aa68ec8f97c4be417ac96e6c6abf48 /Makefile
parent72828b8c5f575cfc7c7c27c5a5d3b7fd9225b625 (diff)
feat: add profiling framework with command echoing
Created a comprehensive profiling framework for dtail commands (dcat, dgrep, dmap) to analyze CPU usage and memory allocations. The framework now prints all executed commands to stdout for full transparency. Key features: - Integrated Go profiling (CPU, memory, allocations) into all three commands - Created profile.sh bash script for analyzing pprof profiles - Added multiple Makefile targets for different profiling scenarios - Automated profiling scripts with command echoing - Support for different data sizes (quick, normal, full) - Special handling for dmap MapReduce format All profiling commands are now echoed to stdout before execution, making it easy to understand what the framework is doing and reproduce commands manually. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'Makefile')
-rw-r--r--Makefile186
1 files changed, 186 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 495abb3..1ef5f02 100644
--- a/Makefile
+++ b/Makefile
@@ -53,3 +53,189 @@ benchmark-quick: build
${GO} test -bench=BenchmarkQuick ./benchmarks
benchmark-full: build
${GO} test -bench=. -benchtime=3x ./benchmarks
+benchmark-baseline: build
+ @echo "Creating benchmark baseline..."
+ @read -p "Enter a descriptive name for this baseline (e.g. 'before-optimization', 'v1.0-release'): " tag; \
+ if [ -z "$$tag" ]; then \
+ echo "Error: Baseline name cannot be empty"; \
+ exit 1; \
+ fi; \
+ mkdir -p benchmarks/baselines; \
+ filename="benchmarks/baselines/baseline_$$(date +%Y%m%d_%H%M%S)_$$(echo $$tag | tr ' ' '_' | tr -cd '[:alnum:]._-').txt"; \
+ echo "Creating baseline: $$filename"; \
+ echo "Git commit: $$(git rev-parse --short HEAD)" > "$$filename"; \
+ echo "Date: $$(date)" >> "$$filename"; \
+ echo "Tag: $$tag" >> "$$filename"; \
+ echo "----------------------------------------" >> "$$filename"; \
+ ${GO} test -bench=. -benchmem ./benchmarks | tee -a "$$filename"; \
+ echo "\nBaseline saved to: $$filename"
+benchmark-baseline-quick: build
+ @echo "Creating quick benchmark baseline..."
+ @read -p "Enter a descriptive name for this baseline (e.g. 'before-optimization', 'v1.0-release'): " tag; \
+ if [ -z "$$tag" ]; then \
+ echo "Error: Baseline name cannot be empty"; \
+ exit 1; \
+ fi; \
+ mkdir -p benchmarks/baselines; \
+ filename="benchmarks/baselines/baseline_$$(date +%Y%m%d_%H%M%S)_$$(echo $$tag | tr ' ' '_' | tr -cd '[:alnum:]._-')_quick.txt"; \
+ echo "Creating quick baseline: $$filename"; \
+ echo "Git commit: $$(git rev-parse --short HEAD)" > "$$filename"; \
+ echo "Date: $$(date)" >> "$$filename"; \
+ echo "Tag: $$tag (quick)" >> "$$filename"; \
+ echo "----------------------------------------" >> "$$filename"; \
+ ${GO} test -bench=BenchmarkQuick -benchmem ./benchmarks | tee -a "$$filename"; \
+ echo "\nQuick baseline saved to: $$filename"
+benchmark-compare: build
+ @if [ -z "${BASELINE}" ]; then \
+ echo "Usage: make benchmark-compare BASELINE=benchmarks/baselines/baseline_TIMESTAMP.txt"; \
+ echo "Available baselines:"; \
+ ls -1 benchmarks/baselines/*.txt 2>/dev/null || echo " No baselines found"; \
+ exit 1; \
+ fi
+ @echo "Running current benchmarks and comparing with ${BASELINE}..."
+ ${GO} test -bench=. -benchmem ./benchmarks | tee benchmarks/baselines/current.txt
+ @echo "\n=== Comparison Report ==="
+ @if command -v benchstat >/dev/null 2>&1; then \
+ benchstat ${BASELINE} benchmarks/baselines/current.txt; \
+ else \
+ echo "benchstat not found. Install with: go install golang.org/x/perf/cmd/benchstat@latest"; \
+ echo "\nShowing simple diff instead:"; \
+ diff -u ${BASELINE} benchmarks/baselines/current.txt || true; \
+ fi
+
+# Profiling targets
+PROFILE_DIR ?= profiles
+PROFILE_SIZE ?= 1000000 # Default 1M lines for profiling
+
+# Generate test data for profiling
+profile-testdata:
+ @echo "Generating test data for profiling..."
+ @mkdir -p testdata
+ @echo "Creating testdata/profile_test.log (${PROFILE_SIZE} lines)..."
+ @seq 1 ${PROFILE_SIZE} | while read i; do \
+ echo "[2024-01-01 00:00:$$i] INFO - Processing request $$i from user$$(($$i % 100)) with status $$(($$i % 2))"; \
+ done > testdata/profile_test.log
+ @echo "Creating testdata/profile_test.csv..."
+ @echo "timestamp,user,action,duration,status" > testdata/profile_test.csv
+ @seq 1 $$(( ${PROFILE_SIZE} / 10 )) | while read i; do \
+ echo "2024-01-01 00:00:$$i,user$$(($$i % 100)),$$([ $$(($$i % 3)) -eq 0 ] && echo login || [ $$(($$i % 3)) -eq 1 ] && echo query || echo logout),$$((100 + $$i % 900)),$$([ $$(($$i % 2)) -eq 0 ] && echo success || echo failure)"; \
+ done >> testdata/profile_test.csv
+ @echo "Test data generated in testdata/"
+
+# Profile dcat
+profile-dcat: dcat profile-testdata
+ @echo "Profiling dcat..."
+ @mkdir -p ${PROFILE_DIR}
+ @echo "Command: ./dcat -profile -profiledir ${PROFILE_DIR} -plain -cfg none testdata/profile_test.log"
+ ./dcat -profile -profiledir ${PROFILE_DIR} -plain -cfg none testdata/profile_test.log > /dev/null
+ @echo "\nAnalyzing dcat profiles..."
+ @echo "CPU Profile:"
+ @echo "Command: ./profiling/profile.sh -top 5 ${PROFILE_DIR}/dcat_cpu_*.prof"
+ @./profiling/profile.sh -top 5 ${PROFILE_DIR}/dcat_cpu_*.prof | tail -n +3
+ @echo "\nMemory Profile:"
+ @echo "Command: ./profiling/profile.sh -top 5 ${PROFILE_DIR}/dcat_mem_*.prof"
+ @./profiling/profile.sh -top 5 ${PROFILE_DIR}/dcat_mem_*.prof | tail -n +3
+
+# Profile dgrep
+profile-dgrep: dgrep profile-testdata
+ @echo "Profiling dgrep..."
+ @mkdir -p ${PROFILE_DIR}
+ @echo "Command: ./dgrep -profile -profiledir ${PROFILE_DIR} -plain -cfg none -regex \"ERROR|user[0-9]+\" testdata/profile_test.log"
+ ./dgrep -profile -profiledir ${PROFILE_DIR} -plain -cfg none -regex "ERROR|user[0-9]+" testdata/profile_test.log > /dev/null
+ @echo "\nAnalyzing dgrep profiles..."
+ @echo "CPU Profile:"
+ @echo "Command: ./profiling/profile.sh -top 5 ${PROFILE_DIR}/dgrep_cpu_*.prof"
+ @./profiling/profile.sh -top 5 ${PROFILE_DIR}/dgrep_cpu_*.prof | tail -n +3
+ @echo "\nMemory Profile:"
+ @echo "Command: ./profiling/profile.sh -top 5 ${PROFILE_DIR}/dgrep_mem_*.prof"
+ @./profiling/profile.sh -top 5 ${PROFILE_DIR}/dgrep_mem_*.prof | tail -n +3
+
+# Profile dmap (with MapReduce format data)
+profile-dmap: dmap
+ @echo "Profiling dmap with MapReduce format..."
+ @cd benchmarks && ./profile_dmap.sh
+
+# Profile all commands
+profile-all: profile-dcat profile-dgrep profile-dmap
+ @echo "\nAll profiling complete. Profiles saved in ${PROFILE_DIR}/"
+
+# Interactive profile analysis
+profile-analyze:
+ @if [ -z "${PROFILE}" ]; then \
+ echo "Available profiles:"; \
+ ls -1t ${PROFILE_DIR}/*.prof 2>/dev/null | head -20 || echo " No profiles found in ${PROFILE_DIR}/"; \
+ echo ""; \
+ echo "Usage: make profile-analyze PROFILE=profiles/dcat_cpu_*.prof"; \
+ else \
+ echo "Opening interactive pprof for ${PROFILE}..."; \
+ go tool pprof ${PROFILE}; \
+ fi
+
+# Generate flame graph
+profile-flamegraph:
+ @if [ -z "${PROFILE}" ]; then \
+ echo "Usage: make profile-flamegraph PROFILE=profiles/dcat_cpu_*.prof"; \
+ echo ""; \
+ echo "Available CPU profiles:"; \
+ ls -1t ${PROFILE_DIR}/*_cpu_*.prof 2>/dev/null | head -10 || echo " No CPU profiles found"; \
+ else \
+ echo "Starting pprof web server for ${PROFILE}..."; \
+ echo "Open http://localhost:8080 in your browser"; \
+ echo "Press Ctrl+C to stop"; \
+ go tool pprof -http=:8080 ${PROFILE}; \
+ fi
+
+# Clean profiles
+profile-clean:
+ @echo "Cleaning profile directory..."
+ rm -rf ${PROFILE_DIR}
+ @echo "Profile directory cleaned"
+
+# Run profiling benchmarks
+profile-benchmark: dcat dgrep dmap
+ @echo "Running profiling benchmarks..."
+ cd benchmarks && ${GO} test -bench="WithProfiling" -benchtime=1x -v
+
+# Run automated profiling script
+profile-auto: dcat dgrep dmap
+ @echo "Running automated profiling script..."
+ cd benchmarks && ./profile_benchmarks.sh
+
+# Run quick profiling (smaller datasets)
+profile-quick: dcat dgrep dmap
+ @echo "Running quick profiling..."
+ cd benchmarks && ./profile_quick.sh
+
+# Show profiling help
+profile-help:
+ @echo "DTail Profiling Targets:"
+ @echo ""
+ @echo " make profile-all - Profile all commands (dcat, dgrep, dmap)"
+ @echo " make profile-dcat - Profile dcat command"
+ @echo " make profile-dgrep - Profile dgrep command"
+ @echo " make profile-dmap - Profile dmap command"
+ @echo ""
+ @echo " make profile-quick - Quick profiling with small datasets"
+ @echo " make profile-auto - Full automated profiling (includes large files)"
+ @echo ""
+ @echo " make profile-analyze - Interactive profile analysis"
+ @echo " Example: make profile-analyze PROFILE=profiles/dcat_cpu_*.prof"
+ @echo ""
+ @echo " make profile-flamegraph - Generate flame graph visualization"
+ @echo " Example: make profile-flamegraph PROFILE=profiles/dcat_cpu_*.prof"
+ @echo ""
+ @echo " make profile-benchmark - Run profiling benchmarks"
+ @echo " make profile-clean - Clean all profiles"
+ @echo ""
+ @echo "Options:"
+ @echo " PROFILE_DIR=<dir> - Profile output directory (default: profiles)"
+ @echo " PROFILE_SIZE=<lines> - Test data size in lines (default: 1000000)"
+ @echo ""
+ @echo "Examples:"
+ @echo " make profile-all PROFILE_SIZE=10000000 # Profile with 10M lines"
+ @echo " make profile-dcat PROFILE_DIR=myprofiles # Custom profile directory"
+ @echo ""
+ @echo "Quick start:"
+ @echo " make profile-quick # Fast profiling with immediate results"
+
+.PHONY: profile-testdata profile-dcat profile-dgrep profile-dmap profile-all profile-analyze profile-flamegraph profile-clean profile-benchmark profile-auto profile-quick profile-help