Initial release: Go Jdenticon library v0.1.0
- Core library with SVG and PNG generation - CLI tool with generate and batch commands - Cross-platform path handling for Windows compatibility - Comprehensive test suite with integration tests
This commit is contained in:
156
scripts/benchmark-hotspots.sh
Executable file
156
scripts/benchmark-hotspots.sh
Executable file
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
# Quick Memory Hotspot Validation Script
|
||||
# Usage: ./scripts/benchmark-hotspots.sh [baseline_dir]
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}🎯 Go Jdenticon Hotspot Validation${NC}"
|
||||
echo -e "${BLUE}=================================${NC}"
|
||||
|
||||
BASELINE_DIR="${1:-./profiles/baseline}"
|
||||
TEMP_DIR="./profiles/temp_$(date +%s)"
|
||||
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
# Function to run quick benchmark
|
||||
quick_benchmark() {
|
||||
local package=$1
|
||||
local name=$2
|
||||
|
||||
echo -e "${BLUE}⚡ Quick benchmark: ${package}${NC}"
|
||||
go test -bench=BenchmarkGenerate -benchmem -count=3 "./${package}" > "${TEMP_DIR}/${name}_current.txt" 2>&1
|
||||
}
|
||||
|
||||
# Function to extract key metrics
|
||||
extract_metrics() {
|
||||
local file=$1
|
||||
|
||||
# Extract allocation metrics from benchmark output
|
||||
grep "BenchmarkGenerate" "$file" | \
|
||||
awk '{
|
||||
if (NF >= 5) {
|
||||
ns_per_op = $3
|
||||
bytes_per_op = $4
|
||||
allocs_per_op = $5
|
||||
gsub(/ns\/op/, "", ns_per_op)
|
||||
gsub(/B\/op/, "", bytes_per_op)
|
||||
gsub(/allocs\/op/, "", allocs_per_op)
|
||||
print bytes_per_op "," allocs_per_op "," ns_per_op
|
||||
}
|
||||
}' | tail -1
|
||||
}
|
||||
|
||||
# Function to compare metrics
|
||||
compare_metrics() {
|
||||
local baseline_file=$1
|
||||
local current_file=$2
|
||||
local component=$3
|
||||
|
||||
if [ ! -f "$baseline_file" ]; then
|
||||
echo -e "${YELLOW}⚠️ No baseline found for ${component}${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
baseline_metrics=$(extract_metrics "$baseline_file")
|
||||
current_metrics=$(extract_metrics "$current_file")
|
||||
|
||||
if [ -z "$baseline_metrics" ] || [ -z "$current_metrics" ]; then
|
||||
echo -e "${RED}❌ Could not extract metrics for ${component}${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
IFS=',' read -r baseline_bytes baseline_allocs baseline_ns <<< "$baseline_metrics"
|
||||
IFS=',' read -r current_bytes current_allocs current_ns <<< "$current_metrics"
|
||||
|
||||
# Calculate percentage changes
|
||||
bytes_change=$(echo "scale=1; ($current_bytes - $baseline_bytes) * 100 / $baseline_bytes" | bc 2>/dev/null || echo "0")
|
||||
allocs_change=$(echo "scale=1; ($current_allocs - $baseline_allocs) * 100 / $baseline_allocs" | bc 2>/dev/null || echo "0")
|
||||
time_change=$(echo "scale=1; ($current_ns - $baseline_ns) * 100 / $baseline_ns" | bc 2>/dev/null || echo "0")
|
||||
|
||||
echo -e "\n${BLUE}📊 ${component} Comparison:${NC}"
|
||||
echo -e " Memory: ${baseline_bytes} → ${current_bytes} B/op (${bytes_change}%)"
|
||||
echo -e " Allocations: ${baseline_allocs} → ${current_allocs} allocs/op (${allocs_change}%)"
|
||||
echo -e " Time: ${baseline_ns} → ${current_ns} ns/op (${time_change}%)"
|
||||
|
||||
# Color code the results
|
||||
if (( $(echo "$bytes_change < -5" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " Status: ${GREEN}🚀 Memory improvement!${NC}"
|
||||
elif (( $(echo "$bytes_change > 10" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " Status: ${RED}⚠️ Memory regression${NC}"
|
||||
else
|
||||
echo -e " Status: ${YELLOW}📈 Within normal range${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
echo -e "${YELLOW}🔥 Running hotspot validation benchmarks...${NC}\n"
|
||||
|
||||
# Run benchmarks for key components
|
||||
quick_benchmark "internal/engine" "engine"
|
||||
quick_benchmark "jdenticon" "full"
|
||||
|
||||
echo -e "\n${BLUE}📈 Comparing with baseline...${NC}"
|
||||
|
||||
# Compare with baseline if available
|
||||
if [ -d "$BASELINE_DIR" ]; then
|
||||
compare_metrics "${BASELINE_DIR}/engine_memory_results.txt" "${TEMP_DIR}/engine_current.txt" "Engine"
|
||||
compare_metrics "${BASELINE_DIR}/full_memory_results.txt" "${TEMP_DIR}/full_current.txt" "Full Library"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ No baseline directory found at: ${BASELINE_DIR}${NC}"
|
||||
echo -e "${BLUE}💡 Run memory-analysis.sh first to establish baseline${NC}"
|
||||
fi
|
||||
|
||||
# Show current raw results
|
||||
echo -e "\n${BLUE}📋 Current Raw Results:${NC}"
|
||||
echo -e "${YELLOW}Engine Package:${NC}"
|
||||
grep "BenchmarkGenerate" "${TEMP_DIR}/engine_current.txt" | head -5 || echo "No results"
|
||||
|
||||
echo -e "\n${YELLOW}Full Library:${NC}"
|
||||
grep "BenchmarkGenerate" "${TEMP_DIR}/full_current.txt" | head -5 || echo "No results"
|
||||
|
||||
# Quick hotspot check
|
||||
echo -e "\n${BLUE}🔍 Quick Hotspot Status Check:${NC}"
|
||||
|
||||
# Check if we're within expected performance bounds
|
||||
engine_metrics=$(extract_metrics "${TEMP_DIR}/engine_current.txt")
|
||||
if [ -n "$engine_metrics" ]; then
|
||||
IFS=',' read -r bytes allocs ns <<< "$engine_metrics"
|
||||
|
||||
echo -e "Current Performance:"
|
||||
echo -e " Memory: ${bytes} B/op"
|
||||
echo -e " Allocations: ${allocs} allocs/op"
|
||||
echo -e " Time: ${ns} ns/op"
|
||||
|
||||
# Check against known hotspot targets
|
||||
if (( $(echo "$bytes > 6000" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " ${RED}🔥 High memory usage - optimization needed${NC}"
|
||||
elif (( $(echo "$bytes < 3000" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " ${GREEN}✅ Excellent memory efficiency${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}📊 Moderate memory usage${NC}"
|
||||
fi
|
||||
|
||||
if (( $(echo "$allocs > 60" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " ${RED}🔥 High allocation count - pooling opportunities${NC}"
|
||||
elif (( $(echo "$allocs < 30" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo -e " ${GREEN}✅ Efficient allocation pattern${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}📊 Moderate allocation count${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\n${BLUE}💡 Quick Commands:${NC}"
|
||||
echo -e " Set as new baseline: ${YELLOW}cp ${TEMP_DIR}/*_current.txt ${BASELINE_DIR}/${NC}"
|
||||
echo -e " Full analysis: ${YELLOW}./scripts/memory-analysis.sh${NC}"
|
||||
echo -e " Profile specific hotspot: ${YELLOW}go test -bench=BenchmarkGenerate -memprofile=temp.prof ./internal/engine && go tool pprof temp.prof${NC}"
|
||||
|
||||
# Cleanup
|
||||
echo -e "\n${GREEN}✅ Hotspot validation completed${NC}"
|
||||
echo -e "${BLUE}📁 Temp results in: ${TEMP_DIR}${NC}"
|
||||
186
scripts/memory-analysis.sh
Executable file
186
scripts/memory-analysis.sh
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive Memory Profiling Script for go-jdenticon
|
||||
# Usage: ./scripts/memory-analysis.sh [output_dir]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
OUTPUT_DIR="${1:-./profiles}"
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
REPORT_DIR="${OUTPUT_DIR}/report_${TIMESTAMP}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}🔍 Go Jdenticon Memory Analysis${NC}"
|
||||
echo -e "${BLUE}================================${NC}"
|
||||
|
||||
# Create output directories
|
||||
mkdir -p "${REPORT_DIR}"
|
||||
|
||||
echo -e "${YELLOW}📁 Output directory: ${REPORT_DIR}${NC}"
|
||||
|
||||
# Function to run memory profiling benchmark
|
||||
run_memory_benchmark() {
|
||||
local package=$1
|
||||
local profile_name=$2
|
||||
local benchmark_pattern=${3:-"."}
|
||||
|
||||
echo -e "${BLUE}🧪 Running memory benchmark: ${package}${NC}"
|
||||
|
||||
go test -bench="${benchmark_pattern}" \
|
||||
-memprofile="${REPORT_DIR}/${profile_name}.prof" \
|
||||
-benchmem \
|
||||
-benchtime=3s \
|
||||
"./${package}" > "${REPORT_DIR}/${profile_name}_results.txt" 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ ${package} profiling completed${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ ${package} profiling failed${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to analyze memory profile
|
||||
analyze_profile() {
|
||||
local profile_path=$1
|
||||
local analysis_name=$2
|
||||
|
||||
echo -e "${BLUE}📊 Analyzing ${analysis_name}${NC}"
|
||||
|
||||
# Generate top allocations
|
||||
go tool pprof -top "${profile_path}" > "${REPORT_DIR}/${analysis_name}_top.txt" 2>/dev/null
|
||||
|
||||
# Generate allocation list for hotspot functions
|
||||
go tool pprof -list="renderShape|AddPolygon|GenerateColorTheme|svgValue" "${profile_path}" > "${REPORT_DIR}/${analysis_name}_hotspots.txt" 2>/dev/null
|
||||
|
||||
# Generate memory usage by package
|
||||
go tool pprof -top -cum "${profile_path}" > "${REPORT_DIR}/${analysis_name}_cumulative.txt" 2>/dev/null
|
||||
|
||||
echo -e "${GREEN}✅ ${analysis_name} analysis completed${NC}"
|
||||
}
|
||||
|
||||
# Function to generate comparison report
|
||||
generate_comparison() {
|
||||
local baseline_prof=$1
|
||||
local current_prof=$2
|
||||
local comparison_name=$3
|
||||
|
||||
if [ -f "${baseline_prof}" ] && [ -f "${current_prof}" ]; then
|
||||
echo -e "${BLUE}🔄 Generating comparison: ${comparison_name}${NC}"
|
||||
go tool pprof -base="${baseline_prof}" -top "${current_prof}" > "${REPORT_DIR}/${comparison_name}_diff.txt" 2>/dev/null
|
||||
echo -e "${GREEN}✅ Comparison ${comparison_name} completed${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
echo -e "${YELLOW}🚀 Starting memory profiling...${NC}"
|
||||
|
||||
# 1. Profile engine package
|
||||
echo -e "\n${BLUE}1. Engine Package Profiling${NC}"
|
||||
run_memory_benchmark "internal/engine" "engine_memory"
|
||||
|
||||
# 2. Profile renderer package
|
||||
echo -e "\n${BLUE}2. Renderer Package Profiling${NC}"
|
||||
run_memory_benchmark "internal/renderer" "renderer_memory"
|
||||
|
||||
# 3. Profile full library
|
||||
echo -e "\n${BLUE}3. Full Library Profiling${NC}"
|
||||
run_memory_benchmark "jdenticon" "full_memory"
|
||||
|
||||
# 4. Analyze profiles
|
||||
echo -e "\n${BLUE}4. Analyzing Memory Profiles${NC}"
|
||||
for profile in "${REPORT_DIR}"/*.prof; do
|
||||
if [ -f "$profile" ]; then
|
||||
basename=$(basename "$profile" .prof)
|
||||
analyze_profile "$profile" "$basename"
|
||||
fi
|
||||
done
|
||||
|
||||
# 5. Generate comparison with baseline if available
|
||||
BASELINE_DIR="./profiles/baseline"
|
||||
if [ -d "$BASELINE_DIR" ]; then
|
||||
echo -e "\n${BLUE}5. Comparing with Baseline${NC}"
|
||||
for profile in "${REPORT_DIR}"/*.prof; do
|
||||
basename=$(basename "$profile")
|
||||
if [ -f "${BASELINE_DIR}/${basename}" ]; then
|
||||
comparison_name=$(basename "$basename" .prof)
|
||||
generate_comparison "${BASELINE_DIR}/${basename}" "$profile" "$comparison_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 6. Generate summary report
|
||||
echo -e "\n${BLUE}6. Generating Summary Report${NC}"
|
||||
cat > "${REPORT_DIR}/ANALYSIS_SUMMARY.md" << EOF
|
||||
# Memory Analysis Report - ${TIMESTAMP}
|
||||
|
||||
## Benchmark Results
|
||||
|
||||
### Engine Package
|
||||
\`\`\`
|
||||
$(cat "${REPORT_DIR}/engine_memory_results.txt" | grep "Benchmark" | head -10)
|
||||
\`\`\`
|
||||
|
||||
### Renderer Package
|
||||
\`\`\`
|
||||
$(cat "${REPORT_DIR}/renderer_memory_results.txt" | grep "Benchmark" | head -10)
|
||||
\`\`\`
|
||||
|
||||
### Full Library
|
||||
\`\`\`
|
||||
$(cat "${REPORT_DIR}/full_memory_results.txt" | grep "Benchmark" | head -10)
|
||||
\`\`\`
|
||||
|
||||
## Top Memory Allocations
|
||||
|
||||
### Engine Hotspots
|
||||
\`\`\`
|
||||
$(head -20 "${REPORT_DIR}/engine_memory_top.txt")
|
||||
\`\`\`
|
||||
|
||||
### Renderer Hotspots
|
||||
\`\`\`
|
||||
$(head -20 "${REPORT_DIR}/renderer_memory_top.txt")
|
||||
\`\`\`
|
||||
|
||||
## Analysis Files Generated
|
||||
|
||||
- \`*_top.txt\`: Top allocation sources
|
||||
- \`*_hotspots.txt\`: Known hotspot function analysis
|
||||
- \`*_cumulative.txt\`: Cumulative allocation by package
|
||||
- \`*_diff.txt\`: Comparison with baseline (if available)
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Review top allocation sources in \`*_top.txt\` files
|
||||
2. Focus optimization on functions listed in hotspot analysis
|
||||
3. Use \`go tool pprof\` for interactive analysis:
|
||||
\`\`\`bash
|
||||
go tool pprof ${REPORT_DIR}/engine_memory.prof
|
||||
\`\`\`
|
||||
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✅ Summary report generated${NC}"
|
||||
|
||||
# 7. Interactive analysis option
|
||||
echo -e "\n${YELLOW}🎯 Analysis complete!${NC}"
|
||||
echo -e "${BLUE}📁 Results saved to: ${REPORT_DIR}${NC}"
|
||||
echo -e "\n${YELLOW}Quick commands for manual analysis:${NC}"
|
||||
echo -e " ${BLUE}Interactive engine analysis:${NC} go tool pprof ${REPORT_DIR}/engine_memory.prof"
|
||||
echo -e " ${BLUE}Interactive renderer analysis:${NC} go tool pprof ${REPORT_DIR}/renderer_memory.prof"
|
||||
echo -e " ${BLUE}Web interface:${NC} go tool pprof -http=:8080 ${REPORT_DIR}/engine_memory.prof"
|
||||
|
||||
# 8. Set baseline option
|
||||
if [ ! -d "./profiles/baseline" ]; then
|
||||
echo -e "\n${YELLOW}💡 Tip: Set this as baseline for future comparisons?${NC}"
|
||||
echo -e " ${BLUE}mkdir -p ./profiles/baseline && cp ${REPORT_DIR}/*.prof ./profiles/baseline/${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}🎉 Memory analysis completed successfully!${NC}"
|
||||
362
scripts/memory-leak-test.sh
Executable file
362
scripts/memory-leak-test.sh
Executable file
@@ -0,0 +1,362 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Memory Leak Detection Script for go-jdenticon
|
||||
# This script runs extended memory testing and captures heap profiles for leak analysis
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
TEST_DURATION="15m"
|
||||
WORKERS=8
|
||||
CACHE_SIZE=1000
|
||||
PPROF_PORT=6060
|
||||
PROFILE_DIR="./profiles"
|
||||
SNAPSHOT_INTERVAL="2m"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}=== go-jdenticon Memory Leak Detection Test ===${NC}"
|
||||
echo "Test duration: $TEST_DURATION"
|
||||
echo "Workers: $WORKERS"
|
||||
echo "Cache size: $CACHE_SIZE"
|
||||
echo "Profile interval: $SNAPSHOT_INTERVAL"
|
||||
echo ""
|
||||
|
||||
# Create profiles directory
|
||||
mkdir -p "$PROFILE_DIR"
|
||||
cd "$PROFILE_DIR" && rm -f *.prof *.json && cd ..
|
||||
|
||||
# Build the memory load test tool
|
||||
echo -e "${YELLOW}Building memory-load-test tool...${NC}"
|
||||
go build -o memory-load-test cmd/memory-load-test/main.go
|
||||
|
||||
# Start the load test in background
|
||||
echo -e "${YELLOW}Starting extended memory load test...${NC}"
|
||||
./memory-load-test \
|
||||
-duration="$TEST_DURATION" \
|
||||
-workers="$WORKERS" \
|
||||
-cache-size="$CACHE_SIZE" \
|
||||
-pprof-port="$PPROF_PORT" \
|
||||
-sample-interval="30s" \
|
||||
-report-interval="1m" \
|
||||
-varied-inputs=true &
|
||||
|
||||
LOAD_TEST_PID=$!
|
||||
|
||||
# Function to cleanup on exit
|
||||
cleanup() {
|
||||
echo -e "\n${YELLOW}Cleaning up...${NC}"
|
||||
if kill -0 $LOAD_TEST_PID 2>/dev/null; then
|
||||
kill $LOAD_TEST_PID
|
||||
wait $LOAD_TEST_PID 2>/dev/null || true
|
||||
fi
|
||||
rm -f memory-load-test
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Wait for pprof server to start
|
||||
echo -e "${YELLOW}Waiting for pprof server to start...${NC}"
|
||||
sleep 5
|
||||
|
||||
# Function to capture heap profile
|
||||
capture_heap_profile() {
|
||||
local suffix=$1
|
||||
local gc_param=""
|
||||
|
||||
# Force GC for some snapshots to get clean measurements
|
||||
if [[ "$suffix" == *"gc"* ]]; then
|
||||
gc_param="?gc=1"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Capturing heap profile: $suffix${NC}"
|
||||
curl -s "http://localhost:$PPROF_PORT/debug/pprof/heap$gc_param" > "$PROFILE_DIR/heap_$suffix.prof"
|
||||
|
||||
# Also capture goroutine profile to check for goroutine leaks
|
||||
curl -s "http://localhost:$PPROF_PORT/debug/pprof/goroutine" > "$PROFILE_DIR/goroutine_$suffix.prof"
|
||||
|
||||
# Get current memory stats from pprof
|
||||
curl -s "http://localhost:$PPROF_PORT/debug/pprof/heap?debug=1" | head -20 > "$PROFILE_DIR/memstats_$suffix.txt"
|
||||
}
|
||||
|
||||
# Function to wait and show progress
|
||||
wait_with_progress() {
|
||||
local duration=$1
|
||||
local message=$2
|
||||
local seconds=$(echo "$duration" | sed 's/m$//' | awk '{print $1*60}')
|
||||
|
||||
echo -e "${YELLOW}$message (${duration})...${NC}"
|
||||
for ((i=1; i<=seconds; i++)); do
|
||||
if ((i % 30 == 0)); then
|
||||
echo -e "${BLUE}Progress: ${i}s / ${seconds}s${NC}"
|
||||
fi
|
||||
sleep 1
|
||||
|
||||
# Check if load test is still running
|
||||
if ! kill -0 $LOAD_TEST_PID 2>/dev/null; then
|
||||
echo -e "${RED}Load test process died unexpectedly!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Capture initial baseline profiles
|
||||
echo -e "${GREEN}=== Capturing Baseline Profiles ===${NC}"
|
||||
capture_heap_profile "0min_baseline"
|
||||
|
||||
# Wait and capture profiles at intervals
|
||||
profiles_captured=1
|
||||
|
||||
# 2 minute mark
|
||||
wait_with_progress "2m" "Waiting for 2-minute mark"
|
||||
capture_heap_profile "2min"
|
||||
((profiles_captured++))
|
||||
|
||||
# 5 minute mark
|
||||
wait_with_progress "3m" "Waiting for 5-minute mark"
|
||||
capture_heap_profile "5min_gc"
|
||||
((profiles_captured++))
|
||||
|
||||
# 8 minute mark
|
||||
wait_with_progress "3m" "Waiting for 8-minute mark"
|
||||
capture_heap_profile "8min"
|
||||
((profiles_captured++))
|
||||
|
||||
# 10 minute mark (with GC)
|
||||
wait_with_progress "2m" "Waiting for 10-minute mark"
|
||||
capture_heap_profile "10min_gc"
|
||||
((profiles_captured++))
|
||||
|
||||
# 13 minute mark
|
||||
wait_with_progress "3m" "Waiting for 13-minute mark"
|
||||
capture_heap_profile "13min"
|
||||
((profiles_captured++))
|
||||
|
||||
# Wait for test completion and capture final profile
|
||||
echo -e "${BLUE}Waiting for test completion...${NC}"
|
||||
wait $LOAD_TEST_PID
|
||||
LOAD_TEST_EXIT_CODE=$?
|
||||
|
||||
# Capture final profile after test completion
|
||||
sleep 2
|
||||
capture_heap_profile "15min_final"
|
||||
((profiles_captured++))
|
||||
|
||||
echo -e "${GREEN}=== Memory Leak Analysis ===${NC}"
|
||||
echo "Captured $profiles_captured heap profiles"
|
||||
|
||||
# Analyze profiles for memory leaks
|
||||
echo -e "${YELLOW}Analyzing memory growth patterns...${NC}"
|
||||
|
||||
# Check if we have go tool pprof available
|
||||
if ! command -v go &> /dev/null; then
|
||||
echo -e "${RED}Go toolchain not found - cannot analyze profiles${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create leak analysis report
|
||||
REPORT_FILE="$PROFILE_DIR/MEMORY_LEAK_ANALYSIS_$(date +%Y%m%d_%H%M%S).md"
|
||||
|
||||
cat > "$REPORT_FILE" << 'EOF'
|
||||
# Memory Leak Analysis Report
|
||||
|
||||
## Test Configuration
|
||||
- **Duration**: 15 minutes sustained load
|
||||
- **Workers**: 8 concurrent goroutines
|
||||
- **Cache Size**: 1000 entries
|
||||
- **Input Variation**: Diverse input patterns to stress different code paths
|
||||
|
||||
## Profile Collection Points
|
||||
1. **0min_baseline**: Initial state after startup
|
||||
2. **2min**: Early runtime behavior
|
||||
3. **5min_gc**: Mid-test with forced GC
|
||||
4. **8min**: Sustained load behavior
|
||||
5. **10min_gc**: Late-test with forced GC
|
||||
6. **13min**: Near end-of-test
|
||||
7. **15min_final**: Final state after completion
|
||||
|
||||
EOF
|
||||
|
||||
# Function to analyze profile comparison
|
||||
analyze_profile_diff() {
|
||||
local base_profile=$1
|
||||
local comp_profile=$2
|
||||
local period_name=$3
|
||||
|
||||
echo -e "${BLUE}Analyzing: $base_profile vs $comp_profile ($period_name)${NC}"
|
||||
|
||||
# Add section to report
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
|
||||
## $period_name Analysis
|
||||
|
||||
### Profile Comparison: $(basename "$base_profile" .prof) → $(basename "$comp_profile" .prof)
|
||||
|
||||
EOF
|
||||
|
||||
# Generate diff analysis and save to report
|
||||
if go tool pprof -base="$base_profile" "$comp_profile" -top10 -cum >> "$REPORT_FILE" 2>/dev/null; then
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
|
||||
### Memory Growth Details
|
||||
\`\`\`
|
||||
EOF
|
||||
go tool pprof -base="$base_profile" "$comp_profile" -list=".*" 2>/dev/null | head -50 >> "$REPORT_FILE" || true
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
\`\`\`
|
||||
|
||||
EOF
|
||||
else
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
|
||||
**⚠️ Profile analysis failed - may indicate inconsistent data or tool issues**
|
||||
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
# Perform comparative analysis
|
||||
analyze_profile_diff "$PROFILE_DIR/heap_0min_baseline.prof" "$PROFILE_DIR/heap_5min_gc.prof" "Early Growth (0-5 min)"
|
||||
analyze_profile_diff "$PROFILE_DIR/heap_5min_gc.prof" "$PROFILE_DIR/heap_10min_gc.prof" "Mid Growth (5-10 min)"
|
||||
analyze_profile_diff "$PROFILE_DIR/heap_10min_gc.prof" "$PROFILE_DIR/heap_15min_final.prof" "Late Growth (10-15 min)"
|
||||
analyze_profile_diff "$PROFILE_DIR/heap_0min_baseline.prof" "$PROFILE_DIR/heap_15min_final.prof" "Overall Growth (0-15 min)"
|
||||
|
||||
# Analyze goroutine growth
|
||||
echo -e "${YELLOW}Analyzing goroutine patterns...${NC}"
|
||||
|
||||
cat >> "$REPORT_FILE" << 'EOF'
|
||||
|
||||
## Goroutine Analysis
|
||||
|
||||
### Goroutine Count Over Time
|
||||
EOF
|
||||
|
||||
for profile in "$PROFILE_DIR"/goroutine_*.prof; do
|
||||
if [[ -f "$profile" ]]; then
|
||||
timestamp=$(basename "$profile" .prof | sed 's/goroutine_//')
|
||||
count=$(go tool pprof -top "$profile" 2>/dev/null | grep "Total:" | awk '{print $2}' || echo "unknown")
|
||||
echo "- **$timestamp**: $count goroutines" >> "$REPORT_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# Memory stats analysis
|
||||
echo -e "${YELLOW}Analyzing memory statistics...${NC}"
|
||||
|
||||
cat >> "$REPORT_FILE" << 'EOF'
|
||||
|
||||
## Memory Statistics Summary
|
||||
|
||||
### Heap Memory Usage Over Time
|
||||
EOF
|
||||
|
||||
for memstat in "$PROFILE_DIR"/memstats_*.txt; do
|
||||
if [[ -f "$memstat" ]]; then
|
||||
timestamp=$(basename "$memstat" .txt | sed 's/memstats_//')
|
||||
heap_inuse=$(grep "# HeapInuse" "$memstat" | awk '{print $3}' || echo "unknown")
|
||||
heap_objects=$(grep "# HeapObjects" "$memstat" | awk '{print $3}' || echo "unknown")
|
||||
echo "- **$timestamp**: HeapInuse=$heap_inuse, HeapObjects=$heap_objects" >> "$REPORT_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# Load test results analysis
|
||||
if [[ $LOAD_TEST_EXIT_CODE -eq 0 ]]; then
|
||||
cat >> "$REPORT_FILE" << 'EOF'
|
||||
|
||||
## Load Test Results
|
||||
✅ **Load test completed successfully** - No crashes or critical errors detected.
|
||||
|
||||
EOF
|
||||
else
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
|
||||
## Load Test Results
|
||||
❌ **Load test failed with exit code $LOAD_TEST_EXIT_CODE** - May indicate memory issues or resource exhaustion.
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Generate conclusions
|
||||
cat >> "$REPORT_FILE" << 'EOF'
|
||||
|
||||
## Analysis Conclusions
|
||||
|
||||
### Memory Leak Assessment
|
||||
|
||||
EOF
|
||||
|
||||
# Try to detect obvious memory leaks from profile sizes
|
||||
baseline_size=$(stat -c%s "$PROFILE_DIR/heap_0min_baseline.prof" 2>/dev/null || echo "0")
|
||||
final_size=$(stat -c%s "$PROFILE_DIR/heap_15min_final.prof" 2>/dev/null || echo "0")
|
||||
|
||||
if [[ $baseline_size -gt 0 && $final_size -gt 0 ]]; then
|
||||
size_growth_ratio=$(echo "scale=2; $final_size / $baseline_size" | bc -l 2>/dev/null || echo "unknown")
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
- **Profile Size Growth**: ${size_growth_ratio}x (${baseline_size} → ${final_size} bytes)
|
||||
EOF
|
||||
|
||||
if (( $(echo "$size_growth_ratio > 3.0" | bc -l 2>/dev/null || echo "0") )); then
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
- ⚠️ **High profile growth** - Indicates potential memory accumulation
|
||||
EOF
|
||||
elif (( $(echo "$size_growth_ratio > 1.5" | bc -l 2>/dev/null || echo "0") )); then
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
- ⚠️ **Moderate profile growth** - Monitor for sustained increase trends
|
||||
EOF
|
||||
else
|
||||
cat >> "$REPORT_FILE" << EOF
|
||||
- ✅ **Reasonable profile growth** - Within expected bounds for sustained load
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
cat >> "$REPORT_FILE" << 'EOF'
|
||||
|
||||
### Recommendations
|
||||
|
||||
1. **Profile Inspection**: Use `go tool pprof -http=:8080 profiles/heap_15min_final.prof` for interactive analysis
|
||||
2. **Comparative Analysis**: Run `go tool pprof -base=profiles/heap_0min_baseline.prof profiles/heap_15min_final.prof` for detailed diff
|
||||
3. **Goroutine Monitoring**: Check `go tool pprof profiles/goroutine_15min_final.prof` for goroutine leaks
|
||||
4. **Long-term Testing**: Consider running tests for 1+ hours to detect slow leaks
|
||||
|
||||
### Commands for Manual Analysis
|
||||
|
||||
```bash
|
||||
# Interactive heap analysis
|
||||
go tool pprof -http=:8080 profiles/heap_15min_final.prof
|
||||
|
||||
# Memory growth analysis
|
||||
go tool pprof -base=profiles/heap_0min_baseline.prof profiles/heap_15min_final.prof
|
||||
|
||||
# Goroutine analysis
|
||||
go tool pprof profiles/goroutine_15min_final.prof
|
||||
|
||||
# View memory stats
|
||||
cat profiles/memstats_15min_final.txt
|
||||
```
|
||||
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}=== Memory Leak Test Complete ===${NC}"
|
||||
echo -e "${BLUE}Results:${NC}"
|
||||
echo " - Profiles captured: $profiles_captured"
|
||||
echo " - Profile directory: $PROFILE_DIR"
|
||||
echo " - Analysis report: $REPORT_FILE"
|
||||
echo " - Load test exit code: $LOAD_TEST_EXIT_CODE"
|
||||
|
||||
if [[ $LOAD_TEST_EXIT_CODE -eq 0 ]]; then
|
||||
echo -e "${GREEN} ✅ Load test completed successfully${NC}"
|
||||
else
|
||||
echo -e "${RED} ❌ Load test failed (exit code: $LOAD_TEST_EXIT_CODE)${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo "1. Review the analysis report: cat $REPORT_FILE"
|
||||
echo "2. Interactive analysis: go tool pprof -http=:8080 $PROFILE_DIR/heap_15min_final.prof"
|
||||
echo "3. Compare profiles: go tool pprof -base=$PROFILE_DIR/heap_0min_baseline.prof $PROFILE_DIR/heap_15min_final.prof"
|
||||
|
||||
# Final cleanup handled by trap
|
||||
390
scripts/memory-profile.sh
Executable file
390
scripts/memory-profile.sh
Executable file
@@ -0,0 +1,390 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Memory profiling script for go-jdenticon
|
||||
# This script provides easy-to-use commands for profiling memory usage
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
PROFILES_DIR="$PROJECT_ROOT/profiles"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Ensure profiles directory exists
|
||||
mkdir -p "$PROFILES_DIR"
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [COMMAND] [OPTIONS]
|
||||
|
||||
Memory profiling commands for go-jdenticon:
|
||||
|
||||
COMMANDS:
|
||||
benchmark Run benchmarks with memory profiling
|
||||
load-test Run sustained load test with profiling
|
||||
analyze-heap Analyze existing heap profile
|
||||
analyze-cpu Analyze existing CPU profile
|
||||
web-ui Start web UI for profile analysis
|
||||
continuous Run continuous profiling session
|
||||
compare Compare two profile files
|
||||
|
||||
OPTIONS:
|
||||
--duration=5m Test duration (default: 5m)
|
||||
--workers=4 Number of workers (default: 4)
|
||||
--cache=1000 Cache size (default: 1000)
|
||||
--output=FILE Output profile file
|
||||
--baseline=FILE Baseline profile for comparison
|
||||
|
||||
EXAMPLES:
|
||||
$0 benchmark --duration=10m
|
||||
$0 load-test --workers=8 --cache=500
|
||||
$0 analyze-heap profiles/heap-latest.prof
|
||||
$0 continuous --duration=1h
|
||||
$0 compare profiles/baseline.prof profiles/after-fix.prof
|
||||
|
||||
PROFILING WORKFLOW:
|
||||
1. Run baseline: $0 benchmark --output=baseline
|
||||
2. Make changes to code
|
||||
3. Run comparison: $0 benchmark --output=after-changes
|
||||
4. Compare results: $0 compare profiles/baseline.prof profiles/after-changes.prof
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date +'%H:%M:%S')] WARNING:${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date +'%H:%M:%S')] ERROR:${NC} $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
COMMAND=""
|
||||
DURATION="5m"
|
||||
WORKERS="4"
|
||||
CACHE_SIZE="1000"
|
||||
OUTPUT_FILE=""
|
||||
BASELINE_FILE=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
benchmark|load-test|analyze-heap|analyze-cpu|web-ui|continuous|compare)
|
||||
COMMAND="$1"
|
||||
shift
|
||||
;;
|
||||
--duration=*)
|
||||
DURATION="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--workers=*)
|
||||
WORKERS="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--cache=*)
|
||||
CACHE_SIZE="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--output=*)
|
||||
OUTPUT_FILE="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--baseline=*)
|
||||
BASELINE_FILE="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$COMMAND" ]]; then
|
||||
COMMAND="$1"
|
||||
else
|
||||
error "Unknown argument: $1"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$COMMAND" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate timestamp for output files
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
|
||||
run_benchmark() {
|
||||
log "Running memory leak validation benchmarks..."
|
||||
|
||||
if [[ -n "$OUTPUT_FILE" ]]; then
|
||||
HEAP_PROF="$PROFILES_DIR/${OUTPUT_FILE}_heap.prof"
|
||||
CPU_PROF="$PROFILES_DIR/${OUTPUT_FILE}_cpu.prof"
|
||||
MEM_PROF="$PROFILES_DIR/${OUTPUT_FILE}_mem.prof"
|
||||
else
|
||||
HEAP_PROF="$PROFILES_DIR/benchmark_${TIMESTAMP}_heap.prof"
|
||||
CPU_PROF="$PROFILES_DIR/benchmark_${TIMESTAMP}_cpu.prof"
|
||||
MEM_PROF="$PROFILES_DIR/benchmark_${TIMESTAMP}_mem.prof"
|
||||
fi
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
log "Memory profiling benchmark with duration: $DURATION"
|
||||
log "Output files: heap=$HEAP_PROF, cpu=$CPU_PROF, mem=$MEM_PROF"
|
||||
|
||||
# Run memory leak benchmarks with profiling
|
||||
go test -bench=BenchmarkMemoryLeak \
|
||||
-benchtime="$DURATION" \
|
||||
-memprofile="$MEM_PROF" \
|
||||
-memprofilerate=1 \
|
||||
-cpuprofile="$CPU_PROF" \
|
||||
-benchmem \
|
||||
./jdenticon
|
||||
|
||||
# Capture heap profile at the end
|
||||
if pgrep -f "go.*test.*BenchmarkMemoryLeak" > /dev/null; then
|
||||
warn "Test process still running, heap profile may not be available"
|
||||
fi
|
||||
|
||||
log "Benchmark completed. Profile files saved:"
|
||||
log " Memory: $MEM_PROF"
|
||||
log " CPU: $CPU_PROF"
|
||||
|
||||
# Create symlinks to latest profiles
|
||||
ln -sf "$(basename "$MEM_PROF")" "$PROFILES_DIR/mem-latest.prof"
|
||||
ln -sf "$(basename "$CPU_PROF")" "$PROFILES_DIR/cpu-latest.prof"
|
||||
|
||||
log "Analysis commands:"
|
||||
log " go tool pprof $MEM_PROF"
|
||||
log " go tool pprof -http=:8080 $MEM_PROF"
|
||||
}
|
||||
|
||||
run_load_test() {
|
||||
log "Running sustained load test with profiling..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Build the load test binary if it doesn't exist
|
||||
if [[ ! -f "cmd/memory-load-test/memory-load-test" ]]; then
|
||||
log "Building memory load test binary..."
|
||||
go build -o cmd/memory-load-test/memory-load-test ./cmd/memory-load-test
|
||||
fi
|
||||
|
||||
HEAP_PROF="$PROFILES_DIR/loadtest_${TIMESTAMP}_heap.prof"
|
||||
CPU_PROF="$PROFILES_DIR/loadtest_${TIMESTAMP}_cpu.prof"
|
||||
|
||||
log "Starting load test: duration=$DURATION, workers=$WORKERS, cache=$CACHE_SIZE"
|
||||
log "pprof server will be available at http://localhost:6060/debug/pprof/"
|
||||
log "Use Ctrl+C to stop the test gracefully"
|
||||
|
||||
# Start the load test in background
|
||||
./cmd/memory-load-test/memory-load-test \
|
||||
-duration="$DURATION" \
|
||||
-workers="$WORKERS" \
|
||||
-cache-size="$CACHE_SIZE" \
|
||||
-pprof-port=6060 &
|
||||
|
||||
LOAD_TEST_PID=$!
|
||||
|
||||
# Give it time to start
|
||||
sleep 5
|
||||
|
||||
log "Load test started with PID $LOAD_TEST_PID"
|
||||
log "Monitoring will capture profiles every 30 seconds"
|
||||
|
||||
# Wait for the test to complete or user interrupt
|
||||
trap "kill $LOAD_TEST_PID 2>/dev/null; wait $LOAD_TEST_PID 2>/dev/null" EXIT
|
||||
|
||||
wait $LOAD_TEST_PID || true
|
||||
|
||||
log "Load test completed"
|
||||
}
|
||||
|
||||
analyze_heap() {
|
||||
local profile_file="$1"
|
||||
|
||||
if [[ -z "$profile_file" ]]; then
|
||||
profile_file="$PROFILES_DIR/mem-latest.prof"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$profile_file" ]]; then
|
||||
error "Profile file not found: $profile_file"
|
||||
fi
|
||||
|
||||
log "Analyzing heap profile: $profile_file"
|
||||
|
||||
# Interactive analysis
|
||||
echo -e "\n${BLUE}Starting interactive pprof session...${NC}"
|
||||
echo -e "${BLUE}Useful commands:${NC}"
|
||||
echo " top - Show top memory allocations"
|
||||
echo " top -cum - Show top cumulative allocations"
|
||||
echo " list <func> - Show source code for function"
|
||||
echo " web - Open web interface"
|
||||
echo " svg - Generate SVG call graph"
|
||||
echo " png - Generate PNG call graph"
|
||||
echo " traces - Show allocation traces"
|
||||
echo " help - Show all commands"
|
||||
echo ""
|
||||
|
||||
go tool pprof "$profile_file"
|
||||
}
|
||||
|
||||
analyze_cpu() {
|
||||
local profile_file="$1"
|
||||
|
||||
if [[ -z "$profile_file" ]]; then
|
||||
profile_file="$PROFILES_DIR/cpu-latest.prof"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$profile_file" ]]; then
|
||||
error "Profile file not found: $profile_file"
|
||||
fi
|
||||
|
||||
log "Analyzing CPU profile: $profile_file"
|
||||
go tool pprof "$profile_file"
|
||||
}
|
||||
|
||||
start_web_ui() {
|
||||
local profile_file="$1"
|
||||
|
||||
if [[ -z "$profile_file" ]]; then
|
||||
profile_file="$PROFILES_DIR/mem-latest.prof"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$profile_file" ]]; then
|
||||
error "Profile file not found: $profile_file"
|
||||
fi
|
||||
|
||||
log "Starting web UI for profile: $profile_file"
|
||||
log "Open http://localhost:8080 in your browser"
|
||||
|
||||
go tool pprof -http=:8080 "$profile_file"
|
||||
}
|
||||
|
||||
run_continuous() {
|
||||
log "Running continuous profiling session for $DURATION"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Build load test if needed
|
||||
if [[ ! -f "cmd/memory-load-test/memory-load-test" ]]; then
|
||||
log "Building memory load test binary..."
|
||||
go build -o cmd/memory-load-test/memory-load-test ./cmd/memory-load-test
|
||||
fi
|
||||
|
||||
log "Starting continuous load test..."
|
||||
|
||||
# Start load test in background
|
||||
./cmd/memory-load-test/memory-load-test \
|
||||
-duration="$DURATION" \
|
||||
-workers="$WORKERS" \
|
||||
-cache-size="$CACHE_SIZE" \
|
||||
-pprof-port=6060 \
|
||||
-varied-inputs=true &
|
||||
|
||||
LOAD_TEST_PID=$!
|
||||
sleep 3
|
||||
|
||||
log "Load test PID: $LOAD_TEST_PID"
|
||||
log "pprof endpoints available at http://localhost:6060/debug/pprof/"
|
||||
|
||||
# Capture profiles at intervals
|
||||
INTERVAL=300 # 5 minutes
|
||||
PROFILES_CAPTURED=0
|
||||
|
||||
while kill -0 $LOAD_TEST_PID 2>/dev/null; do
|
||||
sleep $INTERVAL
|
||||
|
||||
PROFILES_CAPTURED=$((PROFILES_CAPTURED + 1))
|
||||
SNAPSHOT_TIME=$(date +"%H%M%S")
|
||||
|
||||
log "Capturing profile snapshot $PROFILES_CAPTURED at $SNAPSHOT_TIME"
|
||||
|
||||
# Capture heap profile
|
||||
curl -s "http://localhost:6060/debug/pprof/heap" > \
|
||||
"$PROFILES_DIR/continuous_${TIMESTAMP}_${PROFILES_CAPTURED}_heap.prof" || true
|
||||
|
||||
# Capture goroutine profile
|
||||
curl -s "http://localhost:6060/debug/pprof/goroutine" > \
|
||||
"$PROFILES_DIR/continuous_${TIMESTAMP}_${PROFILES_CAPTURED}_goroutine.prof" || true
|
||||
|
||||
log " Saved heap and goroutine profiles"
|
||||
done
|
||||
|
||||
log "Continuous profiling completed. Captured $PROFILES_CAPTURED snapshots."
|
||||
log "Profile files are in: $PROFILES_DIR/"
|
||||
}
|
||||
|
||||
compare_profiles() {
|
||||
local baseline="$1"
|
||||
local current="$2"
|
||||
|
||||
if [[ -z "$baseline" || -z "$current" ]]; then
|
||||
error "Usage: $0 compare <baseline_profile> <current_profile>"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$baseline" ]]; then
|
||||
error "Baseline profile not found: $baseline"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$current" ]]; then
|
||||
error "Current profile not found: $current"
|
||||
fi
|
||||
|
||||
log "Comparing profiles:"
|
||||
log " Baseline: $baseline"
|
||||
log " Current: $current"
|
||||
|
||||
# Use pprof to compare profiles
|
||||
echo -e "\n${BLUE}Memory allocation differences:${NC}"
|
||||
go tool pprof -base="$baseline" -top "$current"
|
||||
|
||||
echo -e "\n${BLUE}Interactive comparison session:${NC}"
|
||||
echo "Useful commands:"
|
||||
echo " top - Show top differences"
|
||||
echo " top -cum - Show cumulative differences"
|
||||
echo " web - Visual diff in browser"
|
||||
|
||||
go tool pprof -base="$baseline" "$current"
|
||||
}
|
||||
|
||||
# Execute the requested command
|
||||
case "$COMMAND" in
|
||||
benchmark)
|
||||
run_benchmark
|
||||
;;
|
||||
load-test)
|
||||
run_load_test
|
||||
;;
|
||||
analyze-heap)
|
||||
analyze_heap "$2"
|
||||
;;
|
||||
analyze-cpu)
|
||||
analyze_cpu "$2"
|
||||
;;
|
||||
web-ui)
|
||||
start_web_ui "$2"
|
||||
;;
|
||||
continuous)
|
||||
run_continuous
|
||||
;;
|
||||
compare)
|
||||
compare_profiles "$2" "$3"
|
||||
;;
|
||||
*)
|
||||
error "Unknown command: $COMMAND"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user