34 Profiling und Diagnose von Build-Problemen

34.1 Build-Profiling-Werkzeuge und -Techniken

Build-Profiling ist essentiell für die Identifikation von Performance-Bottlenecks und die Optimierung von Build-Zeiten. Gradle bietet mehrere eingebaute Profiling-Mechanismen, die detaillierte Einblicke in Build-Execution liefern. Der --profile Flag generiert HTML-Reports mit Task-Execution-Times, Configuration-Duration und Dependency-Resolution-Metriken. Build Scans erweitern diese Capabilities um Cloud-basierte Analyse mit historischen Vergleichen und Team-Sharing. Der --debug und --info Output liefert granulare Logging-Information für Deep-Dive-Analysen.

Die systematische Profiling-Strategie beginnt mit Baseline-Messungen unter kontrollierten Bedingungen. Clean Builds ohne Cache etablieren Worst-Case-Performance, während Incremental Builds mit warmem Cache Best-Case-Szenarien repräsentieren. Die Differenz zwischen diesen Extremen quantifiziert Cache-Effectiveness und Incremental-Build-Optimierungen. Profiling sollte auf identischer Hardware mit konsistenten System-Loads erfolgen, um vergleichbare Ergebnisse zu gewährleisten. Mehrfache Messungen mit statistischer Analyse eliminieren Outliers und identifizieren verlässliche Performance-Charakteristika.

Das Profiling-Toolset erweitert sich durch Third-Party-Tools und JVM-Profiler. JProfiler, YourKit oder async-profiler bieten CPU-Sampling, Memory-Profiling und Thread-Analysis. Diese Tools integrieren mit Gradle über JVM-Arguments und liefern Insights in Gradle-Internals und Plugin-Performance. Die Kombination von Gradle-spezifischen und generischen Profiling-Tools ermöglicht holistische Performance-Analyse von High-Level-Task-Execution bis zu Low-Level-Method-Calls.

// Build Profiling Configuration
tasks.register("profileBuild") {
    description = "Execute comprehensive build profiling"
    group = "diagnostics"
    
    doLast {
        val profileDir = file("${buildDir}/profiling")
        profileDir.mkdirs()
        
        // Execute multiple profiling runs
        val profilingRuns = listOf(
            ProfilingRun("cold-cache", "--no-build-cache --no-daemon"),
            ProfilingRun("warm-cache", "--build-cache"),
            ProfilingRun("parallel", "--parallel --max-workers=4"),
            ProfilingRun("configure-on-demand", "--configure-on-demand")
        )
        
        val results = mutableMapOf<String, ProfilingResult>()
        
        profilingRuns.forEach { run ->
            logger.lifecycle("Executing profiling run: ${run.name}")
            
            // Clean before each run
            exec {
                commandLine("./gradlew", "clean")
            }
            
            // Execute profiled build
            val startTime = System.currentTimeMillis()
            
            exec {
                commandLine("./gradlew", "build", "--profile", "--scan")
                args(run.arguments.split(" "))
                
                // Capture output
                standardOutput = ByteArrayOutputStream()
                errorOutput = ByteArrayOutputStream()
            }
            
            val duration = System.currentTimeMillis() - startTime
            
            // Parse profile report
            val profileReport = fileTree("build/reports/profile").matching {
                include("profile-*.html")
            }.singleFile
            
            val profileData = parseProfileReport(profileReport)
            
            results[run.name] = ProfilingResult(
                duration = duration,
                configurationTime = profileData.configurationTime,
                taskExecutionTime = profileData.taskExecutionTime,
                tasks = profileData.tasks,
                cacheHits = profileData.cacheHits,
                memoryUsage = captureMemoryMetrics()
            )
            
            // Copy profile report
            profileReport.copyTo(
                file("${profileDir}/${run.name}-profile.html"),
                overwrite = true
            )
        }
        
        // Generate comparative analysis
        generateProfilingAnalysis(results, profileDir)
    }
}

// JVM Profiling Integration
tasks.withType<JavaExec>().configureEach {
    if (project.hasProperty("profile.jvm")) {
        jvmArgs(
            "-XX:+FlightRecorder",
            "-XX:StartFlightRecording=filename=${buildDir}/profiling/${name}.jfr,dumponexit=true",
            "-XX:FlightRecorderOptions=stackdepth=256",
            "-XX:+UnlockDiagnosticVMOptions",
            "-XX:+DebugNonSafepoints"
        )
    }
    
    if (project.hasProperty("profile.async")) {
        jvmArgs(
            "-agentpath:/opt/async-profiler/lib/libasyncProfiler.so=start,event=cpu,file=${buildDir}/profiling/${name}.html"
        )
    }
}

// Custom Profiling DSL
abstract class ProfilingExtension {
    abstract val enabled: Property<Boolean>
    abstract val outputDir: DirectoryProperty
    abstract val includeTaskDetails: Property<Boolean>
    abstract val includeMemoryMetrics: Property<Boolean>
    abstract val includeDependencyResolution: Property<Boolean>
    
    fun configureFor(task: Task) {
        if (!enabled.get()) return
        
        task.doFirst {
            task.extensions.extraProperties["profiling.startTime"] = System.nanoTime()
            task.extensions.extraProperties["profiling.startMemory"] = captureMemorySnapshot()
        }
        
        task.doLast {
            val startTime = task.extensions.extraProperties["profiling.startTime"] as Long
            val duration = System.nanoTime() - startTime
            
            val profileData = TaskProfileData(
                name = task.path,
                type = task::class.simpleName ?: "Unknown",
                duration = duration / 1_000_000, // Convert to ms
                outcome = task.state.toString(),
                cacheable = task.outputs.hasOutput,
                inputs = if (includeTaskDetails.get()) captureTaskInputs(task) else emptyMap(),
                outputs = if (includeTaskDetails.get()) captureTaskOutputs(task) else emptyMap(),
                memory = if (includeMemoryMetrics.get()) captureMemoryDelta(
                    task.extensions.extraProperties["profiling.startMemory"] as MemorySnapshot
                ) else null
            )
            
            storeProfileData(profileData)
        }
    }
}

// Apply profiling to all tasks
val profiling = extensions.create<ProfilingExtension>("profiling")

afterEvaluate {
    if (profiling.enabled.get()) {
        tasks.configureEach {
            profiling.configureFor(this)
        }
    }
}

34.2 Performance-Bottleneck-Identifikation

Performance-Bottlenecks in Gradle-Builds manifestieren sich auf verschiedenen Ebenen. Configuration-Time-Bottlenecks entstehen durch komplexe Build-Logik, excessive Task-Creation oder ineffiziente Plugin-Initialization. Task-Execution-Bottlenecks resultieren aus langsamen Compilations, umfangreichen Test-Suites oder Resource-intensiven Operations. Dependency-Resolution-Bottlenecks occur bei Large-Dependency-Trees, Slow-Repositories oder Dynamic-Version-Resolution. Die systematische Identifikation dieser Bottlenecks erfordert Multi-Level-Analysis mit verschiedenen Tools und Techniken.

Critical-Path-Analysis identifiziert die längste Kette von Task-Dependencies, die Build-Duration bestimmt. Tasks auf dem Critical Path sind primäre Optimization-Targets, da ihre Beschleunigung direkt Build-Time reduziert. Parallel-Execution-Analysis zeigt ungenutztes Parallelisierungs-Potential. Tasks ohne Dependencies oder mit erfüllten Dependencies können parallel ausgeführt werden. Die Identifikation von Serialization-Points revealing Opportunities für Task-Restructuring oder Dependency-Optimization.

// Bottleneck Analysis Tool
tasks.register("analyzeBottlenecks") {
    description = "Identify and analyze build bottlenecks"
    group = "diagnostics"
    
    doLast {
        val buildData = collectBuildData()
        
        // Configuration Time Analysis
        val configurationBottlenecks = analyzeConfigurationTime(buildData)
        
        if (configurationBottlenecks.totalTime > 5000) {
            logger.warn("Configuration time excessive: ${configurationBottlenecks.totalTime}ms")
            
            configurationBottlenecks.slowestScripts.forEach { script ->
                logger.warn("  Slow script: ${script.name} (${script.duration}ms)")
            }
            
            configurationBottlenecks.expensivePlugins.forEach { plugin ->
                logger.warn("  Expensive plugin: ${plugin.name} (${plugin.duration}ms)")
            }
        }
        
        // Task Execution Analysis
        val taskBottlenecks = analyzeTaskExecution(buildData)
        
        logger.lifecycle("Critical Path Analysis:")
        val criticalPath = calculateCriticalPath(taskBottlenecks.taskGraph)
        criticalPath.forEach { task ->
            logger.lifecycle("  ${task.path}: ${task.duration}ms")
        }
        logger.lifecycle("  Total critical path: ${criticalPath.sumOf { it.duration }}ms")
        
        // Parallelization Analysis
        val parallelizationOpportunities = analyzeParallelization(taskBottlenecks.taskGraph)
        
        if (parallelizationOpportunities.isNotEmpty()) {
            logger.lifecycle("Parallelization opportunities found:")
            parallelizationOpportunities.forEach { opportunity ->
                logger.lifecycle("  ${opportunity.tasks.size} tasks can run in parallel:")
                opportunity.tasks.forEach { task ->
                    logger.lifecycle("    - ${task.path} (${task.duration}ms)")
                }
                logger.lifecycle("  Potential time saving: ${opportunity.timeSaving}ms")
            }
        }
        
        // Dependency Resolution Analysis
        val dependencyBottlenecks = analyzeDependencyResolution(buildData)
        
        if (dependencyBottlenecks.totalTime > 10000) {
            logger.warn("Dependency resolution slow: ${dependencyBottlenecks.totalTime}ms")
            
            dependencyBottlenecks.slowestRepositories.forEach { repo ->
                logger.warn("  Slow repository: ${repo.name} (${repo.responseTime}ms)")
            }
            
            dependencyBottlenecks.dynamicVersions.forEach { dep ->
                logger.warn("  Dynamic version: ${dep.module} (${dep.resolutionTime}ms)")
            }
        }
        
        // Generate detailed report
        generateBottleneckReport(
            configurationBottlenecks,
            taskBottlenecks,
            parallelizationOpportunities,
            dependencyBottlenecks
        )
    }
}

// Task Dependency Visualization
tasks.register("visualizeDependencies") {
    description = "Generate task dependency graph visualization"
    group = "diagnostics"
    
    doLast {
        val dotFile = file("${buildDir}/reports/task-graph.dot")
        dotFile.parentFile.mkdirs()
        
        val taskGraph = gradle.taskGraph
        
        dotFile.writeText(buildString {
            appendLine("digraph TaskGraph {")
            appendLine("  rankdir=LR;")
            appendLine("  node [shape=box];")
            
            // Color code by task type
            taskGraph.allTasks.forEach { task ->
                val color = when {
                    task is JavaCompile -> "lightblue"
                    task is Test -> "lightgreen"
                    task.name.contains("jar") -> "lightyellow"
                    else -> "white"
                }
                
                appendLine("  \"${task.path}\" [fillcolor=$color, style=filled];")
            }
            
            // Add edges for dependencies
            taskGraph.allTasks.forEach { task ->
                task.taskDependencies.getDependencies(task).forEach { dependency ->
                    appendLine("  \"${dependency.path}\" -> \"${task.path}\";")
                }
            }
            
            appendLine("}")
        })
        
        // Generate SVG if graphviz available
        if (isGraphvizAvailable()) {
            exec {
                commandLine("dot", "-Tsvg", dotFile.absolutePath, 
                    "-o", "${buildDir}/reports/task-graph.svg")
            }
            
            logger.lifecycle("Task graph visualization: ${buildDir}/reports/task-graph.svg")
        }
    }
}

// Performance Hotspot Detection
class HotspotDetector {
    fun detectHotspots(profileData: ProfileData): List<Hotspot> {
        val hotspots = mutableListOf<Hotspot>()
        
        // CPU hotspots
        val cpuIntensiveTasks = profileData.tasks
            .filter { it.cpuTime > it.wallTime * 0.9 }
            .sortedByDescending { it.cpuTime }
            .take(10)
        
        cpuIntensiveTasks.forEach { task ->
            hotspots.add(Hotspot(
                type = HotspotType.CPU,
                location = task.path,
                impact = task.cpuTime,
                recommendation = "Consider parallelizing or optimizing algorithm"
            ))
        }
        
        // Memory hotspots
        val memoryIntensiveTasks = profileData.tasks
            .filter { it.memoryDelta > 100 * 1024 * 1024 } // 100MB
            .sortedByDescending { it.memoryDelta }
            .take(10)
        
        memoryIntensiveTasks.forEach { task ->
            hotspots.add(Hotspot(
                type = HotspotType.MEMORY,
                location = task.path,
                impact = task.memoryDelta,
                recommendation = "Review memory usage, consider streaming or chunking"
            ))
        }
        
        // I/O hotspots
        val ioIntensiveTasks = profileData.tasks
            .filter { it.ioTime > it.wallTime * 0.5 }
            .sortedByDescending { it.ioTime }
            .take(10)
        
        ioIntensiveTasks.forEach { task ->
            hotspots.add(Hotspot(
                type = HotspotType.IO,
                location = task.path,
                impact = task.ioTime,
                recommendation = "Consider caching, parallel I/O, or SSD storage"
            ))
        }
        
        return hotspots
    }
}

34.3 Memory-Analyse und Leak-Detection

Memory-Probleme in Gradle-Builds manifestieren sich als OutOfMemoryErrors, excessive Garbage Collection oder degradierte Performance. Der Gradle Daemon behält State zwischen Builds, wodurch Memory-Leaks akkumulieren können. Plugin-Code, Custom Tasks oder Cache-Implementations können Memory ineffizient nutzen oder Referenzen länger als notwendig halten. Die systematische Memory-Analyse identifiziert diese Probleme und ermöglicht gezielte Optimierungen.

Heap-Dump-Analyse bietet Deep Insight in Memory-Usage-Patterns. Der -XX:+HeapDumpOnOutOfMemoryError JVM-Flag generiert automatisch Heap-Dumps bei Memory-Errors. Eclipse MAT, VisualVM oder JProfiler analysieren diese Dumps und identifizieren Memory-Leaks, Large Objects und Retention-Paths. Die Analyse fokussiert auf Gradle-spezifische Klassen, Task-Instances und Cache-Structures. Dominator-Tree-Analysis revealing Objects, die signifikante Memory retainen.

// Memory Analysis Configuration
tasks.register("analyzeMemory") {
    description = "Analyze build memory usage"
    group = "diagnostics"
    
    doLast {
        val memoryReport = file("${buildDir}/reports/memory-analysis.txt")
        memoryReport.parentFile.mkdirs()
        
        val runtime = Runtime.getRuntime()
        
        memoryReport.writeText(buildString {
            appendLine("=== Gradle Memory Analysis ===")
            appendLine("Timestamp: ${Instant.now()}")
            appendLine()
            
            // JVM Memory Settings
            appendLine("JVM Memory Configuration:")
            appendLine("  Max Heap: ${runtime.maxMemory() / 1024 / 1024} MB")
            appendLine("  Total Heap: ${runtime.totalMemory() / 1024 / 1024} MB")
            appendLine("  Free Heap: ${runtime.freeMemory() / 1024 / 1024} MB")
            appendLine("  Used Heap: ${(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024} MB")
            appendLine()
            
            // Gradle Daemon Status
            appendLine("Gradle Daemon Status:")
            val daemonInfo = captureDaemonInfo()
            appendLine("  PID: ${daemonInfo.pid}")
            appendLine("  Uptime: ${daemonInfo.uptime}")
            appendLine("  Builds Executed: ${daemonInfo.buildCount}")
            appendLine()
            
            // Memory by Component
            appendLine("Memory Usage by Component:")
            val componentMemory = analyzeComponentMemory()
            componentMemory.forEach { (component, usage) ->
                appendLine("  $component: ${usage / 1024 / 1024} MB")
            }
            appendLine()
            
            // Task Memory Usage
            appendLine("Top Memory-Consuming Tasks:")
            val taskMemory = analyzeTaskMemory()
            taskMemory.take(10).forEach { (task, memory) ->
                appendLine("  $task: ${memory / 1024 / 1024} MB")
            }
            appendLine()
            
            // Cache Memory Usage
            appendLine("Cache Memory Usage:")
            val cacheMemory = analyzeCacheMemory()
            appendLine("  Configuration Cache: ${cacheMemory.configurationCache / 1024 / 1024} MB")
            appendLine("  Build Cache: ${cacheMemory.buildCache / 1024 / 1024} MB")
            appendLine("  Dependency Cache: ${cacheMemory.dependencyCache / 1024 / 1024} MB")
            appendLine()
            
            // Memory Leak Detection
            appendLine("Potential Memory Leaks:")
            val leaks = detectMemoryLeaks()
            if (leaks.isEmpty()) {
                appendLine("  No obvious memory leaks detected")
            } else {
                leaks.forEach { leak ->
                    appendLine("  ${leak.description}")
                    appendLine("    Retained Size: ${leak.retainedSize / 1024 / 1024} MB")
                    appendLine("    Suspected Cause: ${leak.suspectedCause}")
                }
            }
        })
        
        logger.lifecycle("Memory analysis report: ${memoryReport.absolutePath}")
        
        // Generate heap dump if requested
        if (project.hasProperty("heap.dump")) {
            generateHeapDump()
        }
    }
}

// Memory Leak Detection
abstract class MemoryLeakDetector : BuildService<MemoryLeakDetector.Params> {
    interface Params : BuildServiceParameters
    
    private val objectTracker = mutableMapOf<String, WeakReference<Any>>()
    private val retentionData = mutableMapOf<String, Long>()
    
    fun trackObject(key: String, obj: Any) {
        objectTracker[key] = WeakReference(obj)
        retentionData[key] = System.currentTimeMillis()
    }
    
    fun analyzeRetention(): List<MemoryLeak> {
        val leaks = mutableListOf<MemoryLeak>()
        val currentTime = System.currentTimeMillis()
        
        objectTracker.forEach { (key, ref) ->
            val obj = ref.get()
            if (obj != null) {
                val retentionTime = currentTime - retentionData[key]!!
                
                // Object retained for more than 5 minutes
                if (retentionTime > 300000) {
                    val size = estimateObjectSize(obj)
                    
                    if (size > 10 * 1024 * 1024) { // 10MB
                        leaks.add(MemoryLeak(
                            description = "Long-lived object: $key",
                            retainedSize = size,
                            retentionTime = retentionTime,
                            suspectedCause = identifyCause(obj)
                        ))
                    }
                }
            }
        }
        
        // Force GC and recheck
        System.gc()
        Thread.sleep(100)
        
        return leaks
    }
    
    private fun identifyCause(obj: Any): String {
        return when {
            obj is Task -> "Task not properly cleaned up"
            obj is Configuration -> "Configuration held in memory"
            obj.javaClass.name.contains("Cache") -> "Cache not evicting entries"
            else -> "Unknown retention cause"
        }
    }
}

// JVM Options for Memory Profiling
tasks.withType<JavaExec>().configureEach {
    if (project.hasProperty("memory.profile")) {
        jvmArgs(
            "-XX:+PrintGCDetails",
            "-XX:+PrintGCTimeStamps",
            "-XX:+PrintHeapAtGC",
            "-Xloggc:${buildDir}/gc.log",
            "-XX:+HeapDumpOnOutOfMemoryError",
            "-XX:HeapDumpPath=${buildDir}/heap-dumps",
            "-XX:NativeMemoryTracking=detail",
            "-XX:+PrintNMTStatistics"
        )
    }
}

// Memory Optimization Recommendations
tasks.register("memoryOptimizationReport") {
    description = "Generate memory optimization recommendations"
    group = "diagnostics"
    
    doLast {
        val recommendations = mutableListOf<String>()
        
        val runtime = Runtime.getRuntime()
        val maxMemory = runtime.maxMemory()
        val processors = runtime.availableProcessors()
        
        // Heap size recommendations
        if (maxMemory < 2L * 1024 * 1024 * 1024) {
            recommendations.add("Increase heap size to at least 2GB for better performance")
        }
        
        // Metaspace recommendations
        val metaspaceUsage = getMetaspaceUsage()
        if (metaspaceUsage > 256 * 1024 * 1024) {
            recommendations.add("Consider increasing MetaspaceSize to avoid frequent resizing")
        }
        
        // Parallel execution recommendations
        val optimalWorkers = minOf(processors, 4)
        recommendations.add("Use --max-workers=$optimalWorkers for optimal parallel execution")
        
        // Daemon recommendations
        recommendations.add("Configure daemon with: org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m")
        
        // Task-specific recommendations
        tasks.withType<JavaCompile>().forEach { task ->
            recommendations.add("Configure ${task.name} with: options.fork = true; options.forkOptions.memoryMaximumSize = '1g'")
        }
        
        logger.lifecycle("Memory Optimization Recommendations:")
        recommendations.forEach { recommendation ->
            logger.lifecycle("  - $recommendation")
        }
    }
}

34.4 Dependency-Resolution-Diagnose

Dependency-Resolution-Probleme manifestieren sich als Version-Conflicts, Missing-Dependencies oder Slow-Resolution. Die Komplexität moderner Dependency-Trees mit transitiven Dependencies, Version-Ranges und Platform-Constraints macht Diagnose challenging. Gradle bietet mehrere Tools zur Dependency-Analysis: dependencies Task zeigt Dependency-Trees, dependencyInsight trackt spezifische Dependencies, buildEnvironment zeigt Build-Script-Dependencies. Diese Tools kombiniert mit Custom-Analysis ermöglichen systematische Problem-Resolution.

Resolution-Strategy-Debugging identifiziert, warum bestimmte Versions gewählt wurden. Conflict-Resolution-Rules, Force-Declarations und Platform-Constraints beeinflussen Version-Selection. Die Analyse zeigt Complete-Resolution-Path von requested Version über verschiedene Constraints zu final selected Version. Failed-Resolution-Attempts mit Rejection-Reasons helfen bei der Diagnose von unerfüllbaren Constraints. Resolution-Time-Analysis identifiziert langsame Repositories oder problematische Dependencies.

// Dependency Resolution Diagnostics
tasks.register("diagnoseDependencies") {
    description = "Comprehensive dependency resolution diagnosis"
    group = "diagnostics"
    
    doLast {
        val report = file("${buildDir}/reports/dependency-diagnosis.md")
        report.parentFile.mkdirs()
        
        report.writeText(buildString {
            appendLine("# Dependency Resolution Diagnosis")
            appendLine("Generated: ${Instant.now()}")
            appendLine()
            
            // Configuration Analysis
            appendLine("## Configurations")
            configurations.filter { it.isCanBeResolved }.forEach { config ->
                appendLine("### ${config.name}")
                
                try {
                    val resolved = config.resolvedConfiguration
                    
                    appendLine("- State: RESOLVED")
                    appendLine("- Dependencies: ${resolved.lenientConfiguration.allModuleDependencies.size}")
                    
                    // Version conflicts
                    val conflicts = detectVersionConflicts(config)
                    if (conflicts.isNotEmpty()) {
                        appendLine("- **Version Conflicts:**")
                        conflicts.forEach { conflict ->
                            appendLine("  - ${conflict.module}: ${conflict.versions.joinToString(" vs ")}")
                            appendLine("    - Selected: ${conflict.selected}")
                            appendLine("    - Reason: ${conflict.reason}")
                        }
                    }
                    
                } catch (e: Exception) {
                    appendLine("- State: FAILED")
                    appendLine("- Error: ${e.message}")
                    
                    // Diagnose resolution failure
                    val diagnosis = diagnoseResolutionFailure(config, e)
                    appendLine("- Diagnosis: $diagnosis")
                }
                
                appendLine()
            }
            
            // Dependency Sources
            appendLine("## Repository Usage")
            val repoUsage = analyzeRepositoryUsage()
            repositories.forEach { repo ->
                appendLine("### ${repo.name}")
                val usage = repoUsage[repo.name] ?: emptyList()
                appendLine("- Resolved artifacts: ${usage.size}")
                if (usage.isNotEmpty()) {
                    appendLine("- Average response time: ${usage.map { it.responseTime }.average()}ms")
                    appendLine("- Cache hit rate: ${usage.count { it.cached } * 100.0 / usage.size}%")
                }
            }
            appendLine()
            
            // Dynamic Versions
            appendLine("## Dynamic Version Usage")
            val dynamicVersions = findDynamicVersions()
            if (dynamicVersions.isEmpty()) {
                appendLine("No dynamic versions found (good!)")
            } else {
                dynamicVersions.forEach { dep ->
                    appendLine("- ${dep.group}:${dep.name}:${dep.version}")
                    appendLine("  - Resolved to: ${dep.resolvedVersion}")
                    appendLine("  - Configuration: ${dep.configuration}")
                }
            }
            appendLine()
            
            // Large Dependencies
            appendLine("## Large Dependencies")
            val largeDeps = findLargeDependencies()
            largeDeps.forEach { dep ->
                appendLine("- ${dep.module}: ${dep.size / 1024 / 1024}MB")
            }
        })
        
        logger.lifecycle("Dependency diagnosis report: ${report.absolutePath}")
    }
}

// Dependency Conflict Resolution Tracer
configurations.all {
    resolutionStrategy {
        eachDependency {
            // Log resolution decisions
            if (project.hasProperty("trace.dependencies")) {
                logger.info("Resolving ${requested.group}:${requested.name}:${requested.version}")
                logger.info("  Reason: ${reason}")
                
                if (requested.version != requested.version) {
                    logger.info("  Version changed: ${requested.version} -> ${target.version}")
                }
            }
        }
        
        // Fail fast on conflicts for diagnosis
        if (project.hasProperty("strict.dependencies")) {
            failOnVersionConflict()
        }
    }
}

// Custom Dependency Analysis Tasks
tasks.register("findDuplicateClasses") {
    description = "Find duplicate classes in dependencies"
    group = "diagnostics"
    
    doLast {
        val classOccurrences = mutableMapOf<String, MutableSet<String>>()
        
        configurations.named("runtimeClasspath").get().resolvedConfiguration.files.forEach { jar ->
            if (jar.extension == "jar") {
                ZipFile(jar).use { zip ->
                    zip.entries().asSequence()
                        .filter { it.name.endsWith(".class") }
                        .forEach { entry ->
                            classOccurrences
                                .getOrPut(entry.name) { mutableSetOf() }
                                .add(jar.name)
                        }
                }
            }
        }
        
        val duplicates = classOccurrences.filter { it.value.size > 1 }
        
        if (duplicates.isNotEmpty()) {
            logger.warn("Found ${duplicates.size} duplicate classes:")
            duplicates.forEach { (className, jars) ->
                logger.warn("  $className found in:")
                jars.forEach { jar ->
                    logger.warn("    - $jar")
                }
            }
        }
    }
}

// Resolution Performance Analysis
tasks.register("analyzeDependencyPerformance") {
    description = "Analyze dependency resolution performance"
    group = "diagnostics"
    
    doLast {
        val performanceData = mutableMapOf<String, Long>()
        
        configurations.filter { it.isCanBeResolved }.forEach { config ->
            val startTime = System.currentTimeMillis()
            
            try {
                // Force resolution
                config.resolve()
                
                val duration = System.currentTimeMillis() - startTime
                performanceData[config.name] = duration
                
                if (duration > 5000) {
                    logger.warn("Slow resolution for ${config.name}: ${duration}ms")
                    
                    // Analyze slow resolution
                    val slowDeps = identifySlowDependencies(config)
                    slowDeps.forEach { dep ->
                        logger.warn("  Slow dependency: ${dep.module} (${dep.time}ms)")
                    }
                }
                
            } catch (e: Exception) {
                logger.error("Failed to resolve ${config.name}: ${e.message}")
            }
        }
        
        // Generate performance report
        val report = file("${buildDir}/reports/dependency-performance.json")
        report.writeText(groovy.json.JsonOutput.toJson(performanceData))
    }
}

34.5 Diagnose-Automation und Reporting

Die Automatisierung von Build-Diagnose transformiert reaktives Debugging in proaktive Problem-Prevention. Automated Diagnostic Suites laufen regelmäßig oder bei Build-Failures und generieren umfassende Reports. CI-Integration triggert Diagnostics bei Performance-Degradation oder unerwarteten Failures. Machine-Learning-Models trainiert auf Historical Diagnostic Data predicten potentielle Probleme. Diese Automation reduziert Mean-Time-To-Resolution und verhindert Problem-Recurrence.

Diagnostic Reports konsolidieren Informationen aus verschiedenen Sources in actionable Formats. HTML-Reports bieten interaktive Navigation durch Diagnostic-Results. JSON-Output ermöglicht Tool-Integration und automated Analysis. Markdown-Reports integrieren in Documentation-Systems. Die Reports priorisieren Issues nach Severity und Impact, bieten konkrete Remediation-Steps und tracken Problem-History. Regular Diagnostic Reports werden zu valuable Documentation für Build-Evolution und Optimization-Decisions.

// Automated Diagnostic Suite
tasks.register("runDiagnostics") {
    description = "Execute comprehensive build diagnostics"
    group = "diagnostics"
    
    dependsOn(
        "analyzeBottlenecks",
        "analyzeMemory",
        "diagnoseDependencies",
        "profileBuild"
    )
    
    doLast {
        val diagnosticReport = DiagnosticReport(
            timestamp = Instant.now(),
            buildVersion = gradle.gradleVersion,
            projectName = project.name,
            projectVersion = project.version.toString()
        )
        
        // Collect all diagnostic results
        diagnosticReport.bottlenecks = parseBottleneckResults()
        diagnosticReport.memoryAnalysis = parseMemoryResults()
        diagnosticReport.dependencyIssues = parseDependencyResults()
        diagnosticReport.performanceProfile = parseProfileResults()
        
        // Analyze and prioritize issues
        val issues = identifyIssues(diagnosticReport)
        diagnosticReport.issues = prioritizeIssues(issues)
        
        // Generate recommendations
        diagnosticReport.recommendations = generateRecommendations(diagnosticReport)
        
        // Generate reports in multiple formats
        generateHtmlReport(diagnosticReport)
        generateJsonReport(diagnosticReport)
        generateMarkdownReport(diagnosticReport)
        
        // CI integration
        if (System.getenv("CI") != null) {
            uploadDiagnosticResults(diagnosticReport)
            
            // Fail build on critical issues
            val criticalIssues = diagnosticReport.issues.filter { it.severity == Severity.CRITICAL }
            if (criticalIssues.isNotEmpty()) {
                throw GradleException("Critical build issues detected: ${criticalIssues.size}")
            }
        }
        
        logger.lifecycle("Diagnostic suite completed. Report: ${buildDir}/reports/diagnostics/index.html")
    }
}

// Diagnostic Report Generator
fun generateHtmlReport(report: DiagnosticReport) {
    val htmlFile = file("${buildDir}/reports/diagnostics/index.html")
    htmlFile.parentFile.mkdirs()
    
    htmlFile.writeText("""
        <!DOCTYPE html>
        <html>
        <head>
            <title>Build Diagnostics Report</title>
            <style>
                body { font-family: -apple-system, sans-serif; margin: 20px; }
                .header { background: #f5f5f5; padding: 20px; border-radius: 8px; }
                .section { margin: 20px 0; }
                .issue { 
                    padding: 15px; 
                    margin: 10px 0; 
                    border-left: 4px solid #ccc;
                    background: #fafafa;
                }
                .issue.critical { border-left-color: #f44336; background: #ffebee; }
                .issue.warning { border-left-color: #ff9800; background: #fff3e0; }
                .issue.info { border-left-color: #2196f3; background: #e3f2fd; }
                .metric { display: inline-block; margin: 10px; padding: 15px; background: white; border: 1px solid #ddd; }
                .recommendation { padding: 10px; margin: 5px 0; background: #e8f5e9; }
                pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
            </style>
        </head>
        <body>
            <div class="header">
                <h1>Build Diagnostics Report</h1>
                <p>Project: ${report.projectName} v${report.projectVersion}</p>
                <p>Generated: ${report.timestamp}</p>
                <p>Gradle: ${report.buildVersion}</p>
            </div>
            
            <div class="section">
                <h2>Summary</h2>
                <div class="metric">
                    <h3>${report.issues.size}</h3>
                    <p>Total Issues</p>
                </div>
                <div class="metric">
                    <h3>${report.issues.count { it.severity == Severity.CRITICAL }}</h3>
                    <p>Critical</p>
                </div>
                <div class="metric">
                    <h3>${report.bottlenecks.criticalPath.sumOf { it.duration }}ms</h3>
                    <p>Critical Path</p>
                </div>
                <div class="metric">
                    <h3>${report.memoryAnalysis.peakUsage / 1024 / 1024}MB</h3>
                    <p>Peak Memory</p>
                </div>
            </div>
            
            <div class="section">
                <h2>Issues</h2>
                ${report.issues.joinToString("\n") { issue ->
                    """
                    <div class="issue ${issue.severity.toString().lowercase()}">
                        <h3>${issue.title}</h3>
                        <p>${issue.description}</p>
                        <p><strong>Impact:</strong> ${issue.impact}</p>
                        <p><strong>Solution:</strong> ${issue.solution}</p>
                    </div>
                    """
                }}
            </div>
            
            <div class="section">
                <h2>Recommendations</h2>
                ${report.recommendations.joinToString("\n") { rec ->
                    """
                    <div class="recommendation">
                        <strong>${rec.title}:</strong> ${rec.description}
                        <pre>${rec.implementation}</pre>
                    </div>
                    """
                }}
            </div>
            
            <div class="section">
                <h2>Performance Profile</h2>
                <canvas id="profileChart"></canvas>
                <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
                <script>
                    new Chart(document.getElementById('profileChart'), {
                        type: 'bar',
                        data: {
                            labels: ${report.performanceProfile.tasks.map { it.name }.toJson()},
                            datasets: [{
                                label: 'Task Duration (ms)',
                                data: ${report.performanceProfile.tasks.map { it.duration }.toJson()},
                                backgroundColor: 'rgba(33, 150, 243, 0.2)',
                                borderColor: 'rgba(33, 150, 243, 1)',
                                borderWidth: 1
                            }]
                        }
                    });
                </script>
            </div>
        </body>
        </html>
    """.trimIndent())
}

// Continuous Diagnostic Monitoring
tasks.register("enableContinuousDiagnostics") {
    description = "Enable continuous build diagnostics"
    group = "diagnostics"
    
    doLast {
        // Configure Gradle properties for continuous diagnostics
        val propertiesFile = file("gradle.properties")
        val properties = """
            # Continuous Diagnostics
            org.gradle.warning.mode=all
            org.gradle.console=verbose
            org.gradle.logging.level=info
            org.gradle.caching.debug=true
            org.gradle.vfs.verbose=true
            
            # Performance monitoring
            systemProp.gradle.performance.monitoring=true
            systemProp.gradle.performance.trace=true
        """.trimIndent()
        
        propertiesFile.appendText("\n$properties\n")
        
        // Create diagnostic daemon script
        val scriptFile = file("diagnostic-daemon.sh")
        scriptFile.writeText("""
            #!/bin/bash
            while true; do
                ./gradlew runDiagnostics --no-daemon
                sleep 3600  # Run every hour
            done
        """.trimIndent())
        
        scriptFile.setExecutable(true)
        
        logger.lifecycle("Continuous diagnostics enabled")
    }
}