Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.smplio.gradle.build.insights
import com.smplio.gradle.build.insights.modules.graph.GraphBuilder
import com.smplio.gradle.build.insights.modules.load.SystemLoadModule
import com.smplio.gradle.build.insights.modules.timing.ExecutionTimeMeasurementModule
import com.smplio.gradle.build.insights.report.CompositeReportBuildService
import com.smplio.gradle.build.insights.report.impl.html.HTMLReporter
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ReportingBasePlugin
Expand All @@ -20,18 +22,41 @@ class GradleInsightsPlugin @Inject constructor(private val registry: BuildEvents
project,
)

ExecutionTimeMeasurementModule(
val executionTimeMeasurementModule = ExecutionTimeMeasurementModule(
project,
registry,
pluginConfig.getExecutionTimeMeasurementConfiguration(),
pluginConfig.gatherHtmlReport,
).initialize()
)
executionTimeMeasurementModule.initialize()

SystemLoadModule(
val systemLoadModule = SystemLoadModule(
project,
registry,
pluginConfig.gatherHtmlReport,
).initialize()
)
systemLoadModule.initialize()

val compositeReportBuildService = project.gradle.sharedServices.registerIfAbsent(
CompositeReportBuildService::class.java.simpleName,
CompositeReportBuildService::class.java,
) { buildServiceSpec ->
buildServiceSpec.parameters.reporters.set(mutableListOf(
pluginConfig.getExecutionTimeMeasurementConfiguration().executionTimeReporter.get(),
).also { list ->
if (pluginConfig.gatherHtmlReport.get()) {
list.add(HTMLReporter(project))
}
})
executionTimeMeasurementModule.getConfigurationTimeReportProvider().let {
buildServiceSpec.parameters.configurationTimeReportProvider.set(it)
}
executionTimeMeasurementModule.getExecutionTimeReportProvider()?.let {
buildServiceSpec.parameters.executionTimeReportService.set(it)
}
systemLoadModule.getSystemLoadReportProvider()?.let {
buildServiceSpec.parameters.systemLoadReportService.set(it)
}
}
registry.onTaskCompletion(compositeReportBuildService)

GraphBuilder().also {
it.buildProjectDependencyGraph(project)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.smplio.gradle.build.insights.modules.load

import com.codahale.metrics.*
import com.codahale.metrics.Timer
import com.smplio.gradle.build.insights.report.load.ISystemLoadReportProvider
import java.util.SortedMap
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.TimeUnit
Expand All @@ -18,7 +19,7 @@ class LocalCacheReporter(
filter,
rateUnit,
durationUnit,
) {
), ISystemLoadReportProvider {
private val measurements: ConcurrentLinkedQueue<Pair<Long, List<Pair<String, Number>>>> = ConcurrentLinkedQueue()

override fun report(
Expand All @@ -42,8 +43,8 @@ class LocalCacheReporter(
measurements.add(measurementTime to measurementsValues)
}

fun close(reporter: ISystemLoadReporter) {
override fun provideSystemLoadReport(): SystemLoadReport? {
super.close()
reporter.reportSystemLoad(measurements)
return measurements
}
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,26 @@
package com.smplio.gradle.build.insights.modules.load

import com.smplio.gradle.build.insights.reporters.CompositeReporter
import com.smplio.gradle.build.insights.reporters.html.HTMLReporter
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.build.event.BuildEventsListenerRegistry

class SystemLoadModule(
private val project: Project,
private val registry: BuildEventsListenerRegistry,
private val gatherHtmlReport: Property<Boolean>,
) {

private var systemLoadService: Provider<SystemLoadService>? = null

fun initialize() {
val sharedServices = project.gradle.sharedServices
val systemLoadService = sharedServices.registerIfAbsent(
systemLoadService = project.gradle.sharedServices.registerIfAbsent(
SystemLoadService::class.java.simpleName,
SystemLoadService::class.java,
) {}

project.gradle.taskGraph.whenReady {
val compositeReporter = CompositeReporter(
if (gatherHtmlReport.get()) {
listOf(
HTMLReporter(
project,
),
)
} else {
emptyList()
}
)
val service = systemLoadService.get()
service.reporter = compositeReporter
SystemLoadService::class.java
) {}.also {
registry.onTaskCompletion(it)
}
registry.onTaskCompletion(systemLoadService)
}
}

fun getSystemLoadReportProvider(): Provider<SystemLoadService>? {
return systemLoadService
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.smplio.gradle.build.insights.modules.load

import java.util.concurrent.ConcurrentLinkedQueue

typealias SystemLoadReport = ConcurrentLinkedQueue<Pair<Long, List<Pair<String, Number>>>>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.smplio.gradle.build.insights.modules.load

import com.codahale.metrics.*
import com.smplio.gradle.build.insights.report.load.ISystemLoadReportProvider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.tooling.events.FinishEvent
Expand All @@ -9,12 +10,11 @@ import java.util.concurrent.TimeUnit

abstract class SystemLoadService: BuildService<BuildServiceParameters.None>,
OperationCompletionListener,
AutoCloseable
ISystemLoadReportProvider
{

private val registry: MetricRegistry = MetricRegistry()
private val metricsReporter: LocalCacheReporter
var reporter: ISystemLoadReporter? = null

init {
registry.register(SystemLoadMetric.SystemLoadAverageMetric())
Expand All @@ -33,11 +33,7 @@ abstract class SystemLoadService: BuildService<BuildServiceParameters.None>,

override fun onFinish(event: FinishEvent?) {}

override fun close() {
reporter?.let {
metricsReporter.close(it)
} ?: kotlin.run {
metricsReporter.close()
}
override fun provideSystemLoadReport(): SystemLoadReport? {
return metricsReporter.provideSystemLoadReport()
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.smplio.gradle.build.insights.modules.timing

import com.smplio.gradle.build.insights.modules.timing.report.ConsoleExecutionTimeReporter
import com.smplio.gradle.build.insights.modules.timing.report.IExecutionTimeReporter
import com.smplio.gradle.build.insights.report.timing.ITaskExecutionTimeReportReceiver
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject

abstract class ExecutionTimeMeasurementConfiguration @Inject constructor(objects: ObjectFactory) {
val enabled: Property<Boolean> = objects.property(Boolean::class.java).convention(true)

val executionTimeReporter: Property<IExecutionTimeReporter> = (objects.property(
IExecutionTimeReporter::class.java
) as Property<IExecutionTimeReporter>).convention(
val executionTimeReporter: Property<ITaskExecutionTimeReportReceiver> = (objects.property(
ITaskExecutionTimeReportReceiver::class.java
) as Property<ITaskExecutionTimeReportReceiver>).convention(
ConsoleExecutionTimeReporter()
)
}
Original file line number Diff line number Diff line change
@@ -1,71 +1,49 @@
package com.smplio.gradle.build.insights.modules.timing

import com.smplio.gradle.build.insights.reporters.CompositeReporter
import com.smplio.gradle.build.insights.reporters.html.HTMLReporter
import com.smplio.gradle.build.insights.report.timing.IConfigurationTimeReportProvider
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.build.event.BuildEventsListenerRegistry
import com.smplio.gradle.build.insights.modules.timing.ExecutionTimeMeasurementService.SerializableStartParameter
import com.smplio.gradle.build.insights.modules.timing.models.ConfigurationInfo
import com.smplio.gradle.build.insights.modules.timing.models.Measured
import java.util.concurrent.ConcurrentHashMap
import com.smplio.gradle.build.insights.modules.timing.report_providers.ConfigurationTimeReportProvider
import com.smplio.gradle.build.insights.modules.timing.report_providers.TaskTaskExecutionTimeMeasurementService
import org.gradle.api.provider.Provider

class ExecutionTimeMeasurementModule(
private val project: Project,
private val registry: BuildEventsListenerRegistry,
private val configuration: ExecutionTimeMeasurementConfiguration,
private val gatherHtmlReport: Property<Boolean>,
) {
fun initialize() {
val buildStartTime = System.currentTimeMillis()

val configurationStartTimes = ConcurrentHashMap<String, Long>()
private var taskExecutionTimeMeasurementService: Provider<TaskTaskExecutionTimeMeasurementService>? = null
private val configurationTimeReportProvider = ConfigurationTimeReportProvider()

val configurationTimeline = mutableListOf<Measured<ConfigurationInfo>>()
fun initialize() {
val buildStartTime = System.currentTimeMillis()

project.gradle.beforeProject {
configurationStartTimes[it.displayName] = System.currentTimeMillis()
configurationTimeReportProvider.onBeforeProject(it)
}

project.gradle.afterProject {
val startTime = configurationStartTimes[it.displayName] ?: return@afterProject
configurationTimeline.add(Measured(
measuredInstance = ConfigurationInfo(
projectName = it.path,
),
startTime = startTime,
endTime = System.currentTimeMillis(),
))
configurationTimeReportProvider.onAfterProject(it)
}

project.gradle.taskGraph.whenReady {
val default = CompositeReporter(mutableListOf(
configuration.executionTimeReporter.get(),
).also { list ->
if (gatherHtmlReport.get()) {
list.add(HTMLReporter(project))
}
})

if (configuration.enabled.get()) {

val startParameter = SerializableStartParameter.create(
startParameter = project.gradle.startParameter,
taskExecutionGraph = it,
)

val sharedServices = project.gradle.sharedServices
val timerService = sharedServices.registerIfAbsent(
ExecutionTimeMeasurementService::class.java.simpleName,
ExecutionTimeMeasurementService::class.java,
) {
it.parameters.startParameters.set(startParameter)
it.parameters.reporter.set(default)
it.parameters.buildStartTime.set(buildStartTime)
it.parameters.configurationsTimeline.set(configurationTimeline)
}
registry.onTaskCompletion(timerService)
if (configuration.enabled.get()) {
taskExecutionTimeMeasurementService = project.gradle.sharedServices.registerIfAbsent(
TaskTaskExecutionTimeMeasurementService::class.java.simpleName,
TaskTaskExecutionTimeMeasurementService::class.java,
) {
it.parameters.buildStartTime.set(buildStartTime)
}.also {
registry.onTaskCompletion(it)
}
}
}

fun getConfigurationTimeReportProvider(): IConfigurationTimeReportProvider {
return configurationTimeReportProvider
}

fun getExecutionTimeReportProvider(): Provider<TaskTaskExecutionTimeMeasurementService>? {
return taskExecutionTimeMeasurementService
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,68 @@
package com.smplio.gradle.build.insights.modules.timing.report
import com.smplio.gradle.build.insights.report.timing.IConfigurationTimeReportReceiver
import com.smplio.gradle.build.insights.report.timing.ITaskExecutionTimeReportReceiver
import java.time.Duration

class ConsoleExecutionTimeReporter: IExecutionTimeReporter {
override fun reportExecutionTime(executionTimeReport: ExecutionTimeReport) {
val firstTaskStartTime = executionTimeReport.taskExecutionTimeline.minOf { it.startTime }
val lastTaskEndTime = executionTimeReport.taskExecutionTimeline.maxOf { it.endTime }
val totalTaskExecutionTime: Long = lastTaskEndTime - firstTaskStartTime
val longestTaskName: Int = executionTimeReport.taskExecutionTimeline.maxOf { it.measuredInstance.path.length } + 1

println("Build took: ${Duration.ofMillis(executionTimeReport.buildInfo.duration).seconds}s")

for (measuredTaskInfo in executionTimeReport.taskExecutionTimeline) {
val taskInfo = measuredTaskInfo.measuredInstance
val taskDuration = Duration.ofMillis(measuredTaskInfo.duration).seconds
val progress = createProgressBar((measuredTaskInfo.duration) * 1.0f / totalTaskExecutionTime)
val taskNamePadded = taskInfo.path.padStart(longestTaskName).padEnd(longestTaskName + 1)
println("|${progress}| $taskNamePadded | ${"${taskDuration}s".padStart(4)}")
}
class ConsoleExecutionTimeReporter: IConfigurationTimeReportReceiver, ITaskExecutionTimeReportReceiver {

private var configurationTimeReport: ConfigurationTimeReport? = null
private var taskExecutionTimeReport: TaskExecutionTimeReport? = null

override fun reportConfigurationTime(configurationTimeReport: ConfigurationTimeReport) {
this.configurationTimeReport = configurationTimeReport
}

override fun reportTaskExecutionTime(taskExecutionTimeReport: TaskExecutionTimeReport) {
this.taskExecutionTimeReport = taskExecutionTimeReport
}

private fun createProgressBar(progress: Float): String {
val numberOfBars = ((progress * 25).toInt() - 1).coerceAtLeast(0)
return "${"=".repeat(numberOfBars)}>${" ".repeat(24 - numberOfBars)}"
}

override fun submitReport() {
var firstConfigurationStartTime = 0L
var lastTaskEndTime = 0L
configurationTimeReport?.let { report ->
println("Configuration time:")
firstConfigurationStartTime = report.minOf { it.startTime }
val lastConfigurationEndTime = report.maxOf { it.endTime }
val totalConfigurationTime: Long = lastConfigurationEndTime - firstConfigurationStartTime
val longestProjectName: Int = report.maxOf { it.measuredInstance.projectName.length } + 1

for (measuredConfigurationInfo in report) {
val projectInfo = measuredConfigurationInfo.measuredInstance
val configurationDuration = Duration.ofMillis(measuredConfigurationInfo.duration).seconds
val progress = createProgressBar((measuredConfigurationInfo.duration) * 1.0f / totalConfigurationTime)
val projectNamePadded = projectInfo.projectName.padStart(longestProjectName).padEnd(longestProjectName + 1)
println("|${progress}| $projectNamePadded | ${"${configurationDuration}s".padStart(4)}")
}
}

taskExecutionTimeReport?.let { report ->
println("Task execution time:")
val firstTaskStartTime = report.minOf { it.startTime }

if (firstConfigurationStartTime == 0L) {
firstConfigurationStartTime = firstTaskStartTime
}

lastTaskEndTime = report.maxOf { it.endTime }
val totalTaskExecutionTime: Long = lastTaskEndTime - firstTaskStartTime
val longestTaskName: Int = report.maxOf { it.measuredInstance.path.length } + 1

for (measuredTaskInfo in report) {
val taskInfo = measuredTaskInfo.measuredInstance
val taskDuration = Duration.ofMillis(measuredTaskInfo.duration).seconds
val progress = createProgressBar((measuredTaskInfo.duration) * 1.0f / totalTaskExecutionTime)
val taskNamePadded = taskInfo.path.padStart(longestTaskName).padEnd(longestTaskName + 1)
println("|${progress}| $taskNamePadded | ${"${taskDuration}s".padStart(4)}")
}
}

if (firstConfigurationStartTime != 0L && lastTaskEndTime != 0L) {
println("Build took: ${Duration.ofMillis(lastTaskEndTime - firstConfigurationStartTime).seconds}s")
}
}
}
Loading
Loading