diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/AsciiArt.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/AsciiArt.kt index fdd2b372..34aa3745 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/AsciiArt.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/AsciiArt.kt @@ -117,3 +117,47 @@ class AsciiArt { } } +class AsciiTable { + class Builder { + private val headers = arrayListOf() + fun header(name: String) = headers.add(name) + fun headers(vararg names: String) = headers.addAll(names) + + private val widths = arrayListOf() + fun width(w: Int) : Builder { + widths.add(w) + return this + } + + private val rows = arrayListOf>() + fun addRow(row: List) = rows.add(row) + + private fun col(width: Int, s: String) : String { + val format = " %1\$-${width.toString()}s" + val result = String.format(format, s) + return result + } + + val vb = AsciiArt.verticalBar + fun build() : String { + val formattedHeaders = + headers.mapIndexed { index, s -> + val s2 = col(widths[index], s) + s2 + }.joinToString(vb) + val result = buildString { + append(AsciiArt.logBox(formattedHeaders, AsciiArt.bottomLeft2, AsciiArt.bottomRight2)) + } + var lineLength = 0 + rows.forEachIndexed { index, row -> + val formattedRow = row.mapIndexed { i, s -> col(widths[i], s) }.joinToString(vb) + val line = vb + " " + formattedRow + " " + vb + AsciiArt.defaultLog(line) + lineLength = line.length + } + AsciiArt.defaultLog(AsciiArt.lowerBox(lineLength - 4)) + return result.toString() + } + + } +} diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt index f97ed2e0..c8ee2020 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt @@ -42,7 +42,7 @@ class ResolveDependency @Inject constructor( val seen = hashSetOf(dep.id) root.addChildren(findChildren(root, seen)) - AsciiArt.logBox(listOf(dep.id, url, dep.jarFile.get()).map { " $it" }, print = {s -> println(s) }) + AsciiArt.logBox(listOf(dep.id, url, dep.jarFile.get()).map { " $it" }) display(root.children) println("") diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt index a1cc82a7..1473d93b 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt @@ -1,5 +1,6 @@ package com.beust.kobalt.internal +import com.beust.kobalt.AsciiTable import com.beust.kobalt.KobaltException import com.beust.kobalt.TaskResult import com.beust.kobalt.misc.NamedThreadFactory @@ -162,6 +163,8 @@ interface IWorker : Callable> { */ // val tasks : List + val name: String + /** * @return the priority of this task. */ @@ -181,11 +184,16 @@ interface IThreadWorkerFactory { } class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadWorkerFactory, - threadCount: Int = 1) { + val threadCount: Int = 1) { val executor : ExecutorService = Executors.newFixedThreadPool(threadCount, NamedThreadFactory("DynamicGraphExecutor")) val completion = ExecutorCompletionService>(executor) + class HistoryLog(val name: String, val timestamp: Long, val threadId: Long, val start: Boolean) + + val historyLog = arrayListOf() + val threadIds = ConcurrentHashMap() + fun run() : TaskResult { try { return run2() @@ -201,7 +209,24 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW val newFreeNodes = HashSet(graph.freeNodes) while (failedResult == null && (running > 0 || newFreeNodes.size > 0)) { nodesRun.addAll(newFreeNodes) - val callables : List> = factory.createWorkers(newFreeNodes) + val callables : List> = factory.createWorkers(newFreeNodes).map { + it -> object: IWorker { + override val priority: Int + get() = it.priority + + override val name: String get() = it.name + override fun call(): TaskResult2 { + val threadId = Thread.currentThread().id + historyLog.add(HistoryLog(it.name, System.currentTimeMillis(), threadId, + start = true)) + threadIds.put(threadId, threadId) + val result = it.call() + historyLog.add(HistoryLog(it.name, System.currentTimeMillis(), Thread.currentThread().id, + start = false)) + return result + } + } + } callables.forEach { completion.submit(it) } running += callables.size @@ -241,6 +266,51 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW } return if (failedResult != null) failedResult else TaskResult() } + + fun dumpHistory() { + log(1, "Thread report") + + fun col1(s: String) = String.format(" %1\$-8s", s) + fun col2(s: String) = String.format(" %1\$-25s", s) + + val table = AsciiTable.Builder() + .width(11) + threadIds.keys.forEach { + table.width(20) + } + table.header("Time (sec)") + val header = StringBuffer().apply { + threadIds.keys.forEach { + table.header("Thread " + it.toString()) + } + } + + fun toSeconds(millis: Long) = (millis / 1000).toInt().toString() + + val start = historyLog[0].timestamp + val projectStart = ConcurrentHashMap() + historyLog.forEach { line -> + val row = arrayListOf() + row.add(toSeconds(line.timestamp - start)) + threadIds.keys.forEach { + if (line.threadId == it) { + var duration = "" + if (line.start) { + projectStart[line.name] = line.timestamp + } else { + duration = " (" + ((line.timestamp - projectStart[line.name]!!) / 1000) + .toInt().toString() + ")" + } + row.add((line.name + duration)) + } else { + row.add("") + } + } + table.addRow(row) + } + + println(table.build()) + } } fun main(argv: Array) { @@ -263,6 +333,7 @@ fun main(argv: Array) { } override val priority: Int get() = 0 + override val name: String = "workerName" } } } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/ParallelProjectRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/ParallelProjectRunner.kt index bee0248b..273dcbff 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/ParallelProjectRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/ParallelProjectRunner.kt @@ -72,9 +72,8 @@ class ParallelProjectRunner(val tasksByNames: (Project) -> ListMultimap): List> { val result = nodes.map { it -> object: IWorker { - override val priority: Int - get() = 0 - + override val priority: Int get() = 0 + override val name: String get() = it.project.name override fun call(): TaskResult2 { val tr = it.call() return tr @@ -94,8 +93,12 @@ class ParallelProjectRunner(val tasksByNames: (Project) -> ListMultimap, val dryRun: Boolean, val pluginInfo: Pl // override val timeOut : Long = 10000 override val priority: Int = 0 + override val name: String get() = "[Taskworker " + tasks.map { it.toString() }.joinToString(",") + "]" } diff --git a/src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt b/src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt index 4ad44cff..66f8c691 100644 --- a/src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt +++ b/src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt @@ -27,7 +27,7 @@ class DynamicGraphTest { class Worker(val runNodes: ArrayList, val n: T, val errorFunction: (T) -> Boolean) : IWorker { override val priority = 0 - + override val name: String get() = "[Worker " + runNodes.map { it.toString() }.joinToString(",") + "]" override fun call() : TaskResult2 { log(2, "Running node $n") runNodes.add(n)