# DTail Profiling Framework This document describes the profiling framework for dtail commands (dcat, dgrep, dmap) to analyze CPU usage and memory allocations. ## Overview The profiling framework provides: - CPU profiling to identify performance bottlenecks - Memory profiling to track allocations and detect leaks - Integration with existing benchmarks - Analysis tools for profile interpretation ## Quick Start ### 1. Build the Tools ```bash make build # Builds all tools including dprofile ``` ### 2. Run Commands with Profiling Each command now supports profiling flags: ```bash # Profile dcat ./dcat -profile -profiledir profiles -plain -cfg none /path/to/file.log # Profile dgrep with specific profiling types ./dgrep -cpuprofile -memprofile -profiledir profiles -regex "error" /path/to/file.log # Profile dmap ./dmap -profile -query "select count(*) from data.csv" ``` ### 3. Analyze Profiles Use dtail-tools for quick analysis: ```bash # List all profiles ./dtail-tools profile -mode list # Analyze a specific profile ./dtail-tools profile -mode analyze profiles/dcat_cpu_20240101_120000.prof # Open web browser with flame graph ./dtail-tools profile -mode analyze profiles/dcat_cpu_*.prof -web # You can also use go tool pprof directly: go tool pprof profiles/dcat_cpu_20240101_120000.prof ``` ## Profiling Options ### Command-line Flags All dtail commands support these profiling flags: - `-cpuprofile`: Enable CPU profiling only - `-memprofile`: Enable memory profiling only - `-profile`: Enable both CPU and memory profiling - `-profiledir `: Directory to store profiles (default: "profiles") ### Profile Types 1. **CPU Profile** (`*_cpu_*.prof`) - Samples CPU usage during execution - Identifies hot functions and code paths - Useful for optimizing computational bottlenecks 2. **Memory Profile** (`*_mem_*.prof`) - Captures heap allocations at end of execution - Shows memory usage by function - Helps identify memory leaks 3. **Allocation Profile** (`*_alloc_*.prof`) - Tracks all allocations during execution - More detailed than memory profile - Useful for reducing allocation pressure ## Using with Benchmarks ### Automated Profiling Run profiling using dtail-tools: ```bash # Quick profiling with small datasets ./dtail-tools profile -mode quick # Full profiling suite ./dtail-tools profile -mode full # Profile dmap specifically (with MapReduce format) ./dtail-tools profile -mode dmap ``` This tool: - Generates test data of various sizes - Profiles dcat, dgrep, and dmap with different workloads - Stores profiles in the `profiles` directory - Provides immediate analysis of results ### Using Make Targets ```bash # Quick profiling with immediate results make profile-quick # Full profiling suite make profile-all # Profile dmap specifically make profile-dmap # List available profiles make profile-list # Analyze a specific profile make profile-analyze PROFILE=profiles/dcat_cpu_*.prof # Open web interface for profile make profile-web PROFILE=profiles/dcat_cpu_*.prof ``` ### Benchmark Integration Run profiling-enabled benchmarks: ```bash cd benchmarks go test -bench="WithProfiling" -benchtime=1x ``` ### Custom Profile Runner Use the profile runner in your benchmarks: ```go import "github.com/mimecast/dtail/benchmarks" func BenchmarkMyFeature(b *testing.B) { benchmarks.ProfileBenchmark(b, "MyFeature", "dcat", "--plain", "--cfg", "none", "testfile.log") } ``` ## Profile Analysis ### Using go tool pprof For interactive analysis: ```bash # Interactive mode go tool pprof profiles/dcat_cpu_*.prof # Common pprof commands: # top - Show top functions # list func - Show source code for function # web - Generate SVG graph # peek func - Show callers/callees of function ``` Generate visualizations: ```bash # Flame graph (requires graphviz) go tool pprof -http=:8080 profiles/dcat_cpu_*.prof # Generate SVG go tool pprof -svg profiles/dgrep_mem_*.prof > profile.svg # Generate text report go tool pprof -text profiles/dmap_alloc_*.prof > report.txt ``` ### Using dtail-tools profile The dtail-tools profile command provides quick summaries: ```bash # List all profiles ./dtail-tools profile -mode list # Analyze specific profile ./dtail-tools profile -mode analyze profiles/dcat_cpu_20240101_120000.prof # Get help ./dtail-tools profile -h ``` ## Optimization Workflow 1. **Baseline Performance** ```bash # Run benchmarks without profiling cd benchmarks go test -bench="BenchmarkDCat" -benchtime=10s ``` 2. **Profile Execution** ```bash # Run with profiling ./dcat -profile -profiledir profiles large_file.log ``` 3. **Identify Bottlenecks** ```bash # Analyze CPU profile ./dprofile -profile profiles/dcat_cpu_*.prof -top 10 # Check memory allocations go tool pprof -alloc_space profiles/dcat_alloc_*.prof ``` 4. **Optimize Code** - Focus on functions with high Flat% (direct CPU usage) - Reduce allocations in hot paths - Consider buffering and pooling 5. **Verify Improvements** ```bash # Re-run benchmarks after optimization go test -bench="BenchmarkDCat" -benchtime=10s ``` ## Common Performance Issues ### CPU Bottlenecks Look for: - Regex compilation in loops - Excessive string operations - Inefficient algorithms (O(n²) or worse) - Unnecessary type conversions Example optimization: ```go // Before: Regex compiled every time for _, line := range lines { if regexp.MustCompile(pattern).MatchString(line) { // ... } } // After: Compile once re := regexp.MustCompile(pattern) for _, line := range lines { if re.MatchString(line) { // ... } } ``` ### Memory Issues Common patterns: - String concatenation in loops - Large temporary slices - Unclosed resources - Excessive goroutines Example optimization: ```go // Before: Many allocations result := "" for _, s := range strings { result += s + "\n" } // After: Single allocation var buf strings.Builder buf.Grow(estimatedSize) for _, s := range strings { buf.WriteString(s) buf.WriteByte('\n') } result := buf.String() ``` ## Tips and Best Practices 1. **Profile Real Workloads** - Use production-like data sizes - Test with actual file formats - Include network operations if relevant 2. **Compare Profiles** ```bash # Compare before/after optimization go tool pprof -diff_base=before.prof after.prof ``` 3. **Focus on Hot Paths** - Optimize functions with >5% CPU usage first - Small improvements in hot paths have big impact 4. **Memory Profiling** - Use `-alloc_space` for total allocations - Use `-inuse_space` for current heap usage - Check for growing heap over time 5. **Benchmark Regularly** - Add profiling to CI/CD pipeline - Track performance over releases - Set performance regression alerts ## Troubleshooting ### No profiles generated - Check write permissions for profile directory - Ensure command completes successfully - Verify profiling flags are correct ### Empty or small profiles - Run command with larger workload - Increase execution time - Check if command exits too quickly ### Analysis tools fail - Ensure profile format is valid - Check Go version compatibility - Verify graphviz is installed for visualizations ## Advanced Usage ### Custom Profiling Points Add profiling snapshots in code: ```go import "github.com/mimecast/dtail/internal/profiling" func processLargeFile() { profiler := profiling.GetProfiler() // Assumes global profiler // Take memory snapshot before processing profiler.Snapshot("before_processing") // ... process file ... // Take snapshot after profiler.Snapshot("after_processing") } ``` ### Continuous Profiling For long-running operations: ```go // Start periodic metrics logging ticker := time.NewTicker(30 * time.Second) go func() { for range ticker.C { profiler.LogMetrics("periodic") } }() defer ticker.Stop() ``` ## Contributing When adding new features: 1. Include benchmark tests 2. Run profiling before submitting PR 3. Document any performance implications 4. Add profiling examples for new commands ## References - [Go Profiling Documentation](https://go.dev/blog/pprof) - [pprof Tool Guide](https://github.com/google/pprof) - [Go Performance Tips](https://go.dev/wiki/Performance)