Extract the layouts of the running streams as an AST (#25831)

This commit is contained in:
Johan Andrén 2019-02-11 13:35:38 +01:00 committed by GitHub
parent 1b98ae8601
commit df089016fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 433 additions and 167 deletions

View file

@ -15,8 +15,8 @@ import akka.annotation.InternalApi
import scala.concurrent.Promise
import scala.util.control.NonFatal
import akka.stream.Attributes.LogLevels
import akka.stream.snapshot._
/**
* INTERNAL API
@ -85,10 +85,6 @@ import akka.stream.Attributes.LogLevels
var outHandler: OutHandler) {
var portState: Int = InReady
var slot: Any = Empty
override def toString =
if (GraphInterpreter.Debug) s"Connection($id, $inOwner, $outOwner, $inHandler, $outHandler, $portState, $slot)"
else s"Connection($id, $portState, $slot, $inHandler, $outHandler)"
}
private val _currentInterpreter = new ThreadLocal[Array[AnyRef]] {
@ -640,49 +636,40 @@ import akka.stream.Attributes.LogLevels
}
/**
* Debug utility to dump the "waits-on" relationships in DOT format to the console for analysis of deadlocks.
* Use dot/graphviz to render graph.
* Debug utility to dump the "waits-on" relationships in an AST format for rendering in some suitable format for
* analysis of deadlocks.
*
* Only invoke this after the interpreter completely settled, otherwise the results might be off. This is a very
* simplistic tool, make sure you are understanding what you are doing and then it will serve you well.
*/
def dumpWaits(): Unit = println(toString)
def toSnapshot: RunningInterpreter = {
override def toString: String = {
try {
val builder = new StringBuilder("\ndot format graph for deadlock analysis:\n")
builder.append("================================================================\n")
builder.append("digraph waits {\n")
for (i logics.indices) {
val logic = logics(i)
val logicSnapshots = logics.zipWithIndex.map {
case (logic, idx)
val label = logic.originalStage.getOrElse(logic).toString
builder.append(s""" N$i [label="$label"];""").append('\n')
}
val logicIndexes = logics.zipWithIndex.map { case (stage, idx) stage idx }.toMap
for (connection connections if connection != null) {
val inName = "N" + logicIndexes(connection.inOwner)
val outName = "N" + logicIndexes(connection.outOwner)
builder.append(s" $inName -> $outName ")
connection.portState match {
case InReady
builder.append("[label=shouldPull, color=blue];")
case OutReady
builder.append(s"[label=shouldPush, color=red];")
case x if (x | InClosed | OutClosed) == (InClosed | OutClosed)
builder.append("[style=dotted, label=closed, dir=both];")
case _
}
builder.append("\n")
}
builder.append("}\n================================================================\n")
builder.append(s"// $queueStatus (running=$runningStages, shutdown=${shutdownCounter.mkString(",")})")
builder.toString()
} catch {
case _: NoSuchElementException "Not all logics has a stage listed, cannot create graph"
LogicSnapshotImpl(idx, label, logic.attributes)
}
val logicIndexes = logics.zipWithIndex.map { case (stage, idx) stage idx }.toMap
val connectionSnapshots = connections.filter(_ != null)
.map { connection
ConnectionSnapshotImpl(
connection.id,
logicSnapshots(logicIndexes(connection.inOwner)),
logicSnapshots(logicIndexes(connection.outOwner)),
connection.portState match {
case InReady ConnectionSnapshot.ShouldPull
case OutReady ConnectionSnapshot.ShouldPush
case x if (x | InClosed | OutClosed) == (InClosed | OutClosed) ConnectionSnapshot.Closed
}
)
}
RunningInterpreterImpl(
logicSnapshots.toVector,
connectionSnapshots.toVector,
queueStatus,
runningStages,
shutdownCounter.toList.map(n logicSnapshots(n)))
}
}