
ch.epfl.scala.profilers.ProfilingImpl.scala Maven / Gradle / Ivy
/* *\
** _____ __ ______ __ **
** / ___/_________ _/ /___ _ / ____/__ ____ / /____ _____ **
** \__ \/ ___/ __ `/ / __ `/ / / / _ \/ __ \/ __/ _ \/ ___/ Scala Center **
** ___/ / /__/ /_/ / / /_/ / / /___/ /__/ / / / /_/ /__/ / https://scala.epfl.ch **
** /____/\___/\__,_/_/\__,_/ \____/\___/_/ /_/\__/\___/_/ (c) 2017-2018, LAMP/EPFL **
** **
\* */
package ch.epfl.scala.profilers
import java.nio.file.{Files, Path, StandardOpenOption}
import ch.epfl.scala.PluginConfig
import ch.epfl.scala.profiledb.utils.AbsolutePath
import ch.epfl.scala.profilers.tools.{Logger, QuantitiesHijacker, SettingsOps}
import scala.tools.nsc.Global
import scala.reflect.internal.util.SourceFile
final class ProfilingImpl[G <: Global](
override val global: G,
config: PluginConfig,
logger: Logger[G]
) extends ProfilingStats {
import global._
def registerProfilers(): Unit = {
// Register our profiling macro plugin
analyzer.addMacroPlugin(ProfilingMacroPlugin)
analyzer.addAnalyzerPlugin(ProfilingAnalyzerPlugin)
}
/**
* Represents the profiling information about expanded macros.
*
* Note that we could derive the value of expanded macros from the
* number of instances of [[MacroInfo]] if it were not by the fact
* that a macro can expand in the same position more than once. We
* want to be able to report/analyse such cases on their own, so
* we keep it as a paramater of this entity.
*/
case class MacroInfo(expandedMacros: Int, expandedNodes: Int, expansionNanos: Long) {
def +(other: MacroInfo): MacroInfo = {
val totalExpanded = expandedMacros + other.expandedMacros
val totalNodes = expandedNodes + other.expandedNodes
val totalTime = expansionNanos + other.expansionNanos
MacroInfo(totalExpanded, totalNodes, totalTime)
}
}
object MacroInfo {
final val Empty = MacroInfo(0, 0, 0L)
implicit val macroInfoOrdering: Ordering[MacroInfo] = Ordering.by(_.expansionNanos)
def aggregate(infos: Iterator[MacroInfo]): MacroInfo = {
infos.foldLeft(MacroInfo.Empty)(_ + _)
}
}
import scala.reflect.internal.util.SourceFile
case class MacroProfiler(
perCallSite: Map[Position, MacroInfo],
perFile: Map[SourceFile, MacroInfo],
inTotal: MacroInfo,
repeatedExpansions: Map[Tree, Int]
)
def toMillis(nanos: Long): Long =
java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(nanos)
def groupPerFile[V](
kvs: Map[Position, V]
)(empty: V, aggregate: (V, V) => V): Map[SourceFile, V] = {
kvs.groupBy(_._1.source).map {
case (sf, posInfos: Map[Position, V]) => sf -> posInfos.valuesIterator.fold(empty)(aggregate)
}
}
lazy val macroProfiler: MacroProfiler = {
import ProfilingMacroPlugin.macroInfos // , repeatedTrees}
val perCallSite = macroInfos.toMap
val perFile = groupPerFile(perCallSite)(MacroInfo.Empty, _ + _)
val inTotal = MacroInfo.aggregate(perFile.valuesIterator)
/* val repeated = repeatedTrees.toMap.valuesIterator
.filter(_.count > 1)
.map(v => v.original -> v.count)
.toMap*/
// perFile and inTotal are already converted to millis
val callSiteNanos = perCallSite
MacroProfiler(callSiteNanos, perFile, inTotal, Map.empty) // repeated)
}
case class ImplicitInfo(count: Int) {
def +(other: ImplicitInfo): ImplicitInfo = ImplicitInfo(count + other.count)
}
object ImplicitInfo {
final val Empty = ImplicitInfo(0)
def aggregate(infos: Iterator[ImplicitInfo]): ImplicitInfo = infos.fold(Empty)(_ + _)
implicit val infoOrdering: Ordering[ImplicitInfo] = Ordering.by(_.count)
}
case class ImplicitProfiler(
perCallSite: Map[Position, ImplicitInfo],
perFile: Map[SourceFile, ImplicitInfo],
perType: Map[Type, ImplicitInfo],
inTotal: ImplicitInfo
)
lazy val implicitProfiler: ImplicitProfiler = {
val perCallSite = implicitSearchesByPos.map {
case (pos, i) => pos -> ImplicitInfo.apply(i)
}.toMap
val perFile = groupPerFile[ImplicitInfo](perCallSite)(ImplicitInfo.Empty, _ + _)
val perType = implicitSearchesByType.map {
case (pos, i) => pos -> ImplicitInfo.apply(i)
}.toMap
val inTotal = ImplicitInfo.aggregate(perFile.valuesIterator)
ImplicitProfiler(perCallSite, perFile, perType, inTotal)
}
// Copied from `TypeDiagnostics` to have expanded types in implicit search
private object DealiasedType extends TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, _) if sym.isAliasType && !sym.isInDefaultNamespace =>
mapOver(tp.dealias)
case _ => mapOver(tp)
}
}
def concreteTypeFromSearch(tree: Tree, default: Type): Type = {
tree match {
case EmptyTree => default
case Block(_, expr) => expr.tpe
case Try(block, _, _) =>
block match {
case Block(_, expr) => expr.tpe
case t => t.tpe
}
case t =>
val treeType = t.tpe
if (treeType == null || treeType == NoType) default else treeType
}
}
def generateGraphData(
outputDir: AbsolutePath,
globalDirMaybe: Option[AbsolutePath]
): List[AbsolutePath] = {
Files.createDirectories(outputDir.underlying)
val randomId = java.lang.Long.toString(System.currentTimeMillis())
/*val dotFile = outputDir.resolve(s"$graphName.dot")
ProfilingAnalyzerPlugin.dottify(graphName, dotFile.underlying)*/
val implicitFlamegraphFiles = {
val mkImplicitGraphName: String => String =
postfix => s"implicit-searches-$postfix.flamegraph"
val compileUnitFlamegraphFile = outputDir.resolve(mkImplicitGraphName(randomId))
globalDirMaybe match {
case Some(globalDir) =>
Files.createDirectories(globalDir.underlying)
val globalFile =
globalDir
.resolve(mkImplicitGraphName("global"))
List(compileUnitFlamegraphFile, globalFile)
case None =>
List(compileUnitFlamegraphFile)
}
}
val macroFlamegraphFiles =
if (config.generateMacroFlamegraph) {
val macroGraphName = s"macros-$randomId"
val file = outputDir.resolve(s"$macroGraphName.flamegraph")
List(file)
} else Nil
ProfilingAnalyzerPlugin.foldImplicitStacks(implicitFlamegraphFiles)
ProfilingMacroPlugin.foldMacroStacks(macroFlamegraphFiles)
implicitFlamegraphFiles ::: macroFlamegraphFiles
}
private val registeredQuantities = QuantitiesHijacker.getRegisteredQuantities(global)
def registerTyperTimerFor(prefix: String): statistics.Timer = {
val typerTimer = statistics.newTimer(prefix, "typer")
registeredQuantities.remove(s"/$prefix")
typerTimer
}
private def typeToString(`type`: Type): String =
global.exitingTyper(`type`.toLongString).trim
// Moving this here so that it's accessible to the macro plugin
private type Entry =
(global.analyzer.ImplicitSearch, statistics.TimerSnapshot, statistics.TimerSnapshot)
private var implicitsStack: List[Entry] = Nil
private object ProfilingAnalyzerPlugin extends global.analyzer.AnalyzerPlugin {
import scala.collection.mutable
private val implicitsTimers = perRunCaches.newAnyRefMap[Type, statistics.Timer]()
private val searchIdsToTargetTypes = perRunCaches.newMap[Int, Type]()
private val stackedNanos = perRunCaches.newMap[Int, (Long, Type)]()
private val stackedNames = perRunCaches.newMap[Int, List[String]]()
private val searchIdsToTimers = perRunCaches.newMap[Int, statistics.Timer]()
private val implicitsDependants = new mutable.AnyRefMap[Type, mutable.HashSet[Type]]()
private val searchIdChildren = perRunCaches.newMap[Int, List[analyzer.ImplicitSearch]]()
def foldImplicitStacks(outputPaths: Seq[AbsolutePath]): Unit =
if (outputPaths.nonEmpty) {
// This part is memory intensive and hence the use of java collections
val stacksJavaList = new java.util.ArrayList[String]()
stackedNanos.foreach {
case (id, (nanos, _)) =>
val names =
stackedNames.getOrElse(
id,
sys.error(s"Stack name for search id ${id} doesn't exist!")
)
val stackName = names.mkString(";")
// val count = implicitSearchesByType.getOrElse(tpe, sys.error(s"No counter for ${tpe}"))
stacksJavaList.add(s"$stackName ${nanos / 1000}")
}
java.util.Collections.sort(stacksJavaList)
outputPaths.foreach(path =>
Files.write(
path.underlying,
stacksJavaList,
StandardOpenOption.APPEND,
StandardOpenOption.CREATE
)
)
} else ()
def dottify(graphName: String, outputPath: Path): Unit = {
def clean(`type`: Type) = typeToString(`type`).replace("\"", "\'")
def qualify(node: String, timing: Long, counter: Int): String = {
val nodeName = node.stripPrefix("\"").stripSuffix("\"")
val style = if (timing >= 500) "style=filled, fillcolor=\"#ea9d8f\"," else ""
s"""$node [${style}label="${nodeName}\\l${counter} times = ${timing}ms"];"""
}
val nodes = implicitSearchesByType.keys
val nodesIds = nodes.map(`type` => `type` -> s""""${clean(`type`)}"""").toMap
def getNodeId(`type`: Type): String = {
nodesIds.getOrElse(
`type`,
sys.error {
s"""Id for ${`type`} doesn't exist.
|
| Information about the type:
| - `structure` -> ${global.showRaw(`type`)}
| - `safeToString` -> ${`type`.safeToString}
| - `toLongString` after typer -> ${typeToString(`type`)}
| - `typeSymbol` -> ${`type`.typeSymbol}
""".stripMargin
}
)
}
val connections = for {
(dependee, dependants) <- implicitsDependants.toSet
dependant <- dependants
dependantId = getNodeId(dependant)
dependeeId = getNodeId(dependee)
if dependeeId != dependantId && !dependantId.isEmpty && !dependeeId.isEmpty
} yield s"$dependantId -> $dependeeId;"
val nodeInfos = nodes.map { `type` =>
val id = getNodeId(`type`)
val timer = getImplicitTimerFor(`type`).nanos / 1000000
val count = implicitSearchesByType.getOrElse(`type`, sys.error(s"No counter for ${`type`}"))
qualify(id, timer, count)
}
val graph = s"""digraph "$graphName" {
| graph [ranksep=0, rankdir=LR];
|${nodeInfos.mkString(" ", "\n ", "\n ")}
|${connections.mkString(" ", "\n ", "\n ")}
|}""".stripMargin.getBytes
Files.write(outputPath, graph, StandardOpenOption.WRITE, StandardOpenOption.CREATE)
}
private def getImplicitTimerFor(candidate: Type): statistics.Timer =
implicitsTimers.getOrElse(candidate, sys.error(s"Timer for ${candidate} doesn't exist"))
private def getSearchTimerFor(searchId: Int): statistics.Timer = {
searchIdsToTimers
.getOrElse(searchId, sys.error(s"Missing non-cumulative timer for $searchId"))
}
override def pluginsNotifyImplicitSearch(search: global.analyzer.ImplicitSearch): Unit = {
if (SettingsOps.areStatisticsEnabled(global)) {
val targetType = search.pt
val targetPos = search.pos
// Stop counter of dependant implicit search
implicitsStack.headOption.foreach {
case (search, _, searchStart) =>
val searchTimer = getSearchTimerFor(search.searchId)
statistics.stopTimer(searchTimer, searchStart)
}
// We add ourselves to the child list of our parent implicit search
implicitsStack.headOption match {
case Some((prevSearch, _, _)) =>
val prevId = prevSearch.searchId
val prevChilds = searchIdChildren.getOrElse(prevId, Nil)
searchIdChildren.update(prevId, search :: prevChilds)
case None => ()
}
// Create timer and unregister it so that it is invisible in console output
val prefix = s" $targetType"
val perTypeTimer = implicitsTimers
.getOrElseUpdate(targetType, statistics.newTimer(prefix, "typer"))
registeredQuantities.remove(s"/$prefix")
// Create non-cumulative timer for the search and unregister it too
val searchId = search.searchId
val searchPrefix = s" implicit search ${searchId}"
val searchTimer = registerTyperTimerFor(searchPrefix)
searchIdsToTimers.+=(searchId -> searchTimer)
// Start the timer as soon as possible
val implicitTypeStart = statistics.startTimer(perTypeTimer)
val searchStart = statistics.startTimer(searchTimer)
// Update all timers and counters
val typeCounter = implicitSearchesByType.getOrElse(targetType, 0)
implicitSearchesByType.update(targetType, typeCounter + 1)
val posCounter = implicitSearchesByPos.getOrElse(targetPos, 0)
implicitSearchesByPos.update(targetPos, posCounter + 1)
if (config.showProfiles) {
val sourceFiles =
implicitSearchesSourceFilesByType.getOrElseUpdate(targetType, mutable.HashSet.empty)
if (!sourceFiles.contains(targetPos.source)) {
sourceFiles.add(targetPos.source)
}
}
if (global.analyzer.openMacros.nonEmpty)
statistics.incCounter(implicitSearchesByMacrosCount)
searchIdsToTargetTypes.+=((search.searchId, targetType))
/* // Add dependants once we hit a concrete node
search.context.openImplicits.headOption.foreach { dependant =>
implicitsDependants
.getOrElseUpdate(targetType, new mutable.HashSet())
.+=(dependant.pt)
}*/
implicitsStack = (search, implicitTypeStart, searchStart) :: implicitsStack
}
}
override def pluginsNotifyImplicitSearchResult(result: global.analyzer.SearchResult): Unit = {
super.pluginsNotifyImplicitSearchResult(result)
if (SettingsOps.areStatisticsEnabled(global)) {
// 1. Get timer of the running search
val (search, implicitTypeStart, searchStart) = implicitsStack.head
val targetType = search.pt
val timer = getImplicitTimerFor(targetType)
// 2. Register the timing diff for every stacked name.
def stopTimerFlamegraph(prev: Option[analyzer.ImplicitSearch]): Unit = {
val searchId = search.searchId
def missing(name: String): Nothing =
sys.error(s"Missing $name for $searchId ($targetType).")
val forcedExpansions =
ProfilingMacroPlugin.searchIdsToMacroStates.getOrElse(searchId, Nil)
val expandedStr = s"(expanded macros ${forcedExpansions.size})"
// Detect macro name if the type we get comes from a macro to add it to the stack
val suffix = {
val errorTag = if (result.isFailure) " _[j]" else ""
result.tree.attachments.get[analyzer.MacroExpansionAttachment] match {
case Some(analyzer.MacroExpansionAttachment(expandee: Tree, _)) =>
val expandeeSymbol = treeInfo.dissectApplied(expandee).core.symbol
analyzer.loadMacroImplBinding(expandeeSymbol) match {
case Some(a) =>
val l = if (errorTag.isEmpty) " _[i]" else errorTag
s" (id ${searchId}) $expandedStr (tree from `${a.className}.${a.methName}`)$l"
case None => s" $expandedStr $errorTag"
}
case None => s" $expandedStr $errorTag"
}
}
// Complete stack names of triggered implicit searches
val children = searchIdChildren.getOrElse(searchId, Nil)
prev.foreach { p =>
val current = searchIdChildren.getOrElse(p.searchId, Nil)
searchIdChildren.update(p.searchId, children ::: current)
}
val typeForStack = DealiasedType {
if (!config.concreteTypeParamsInImplicits) targetType
else concreteTypeFromSearch(result.subst(result.tree), targetType)
}
if (
config.printSearchIds.contains(
searchId
) || (result.isFailure && config.printFailedMacroImplicits)
) {
logger.info(
s"""implicit search ${searchId}:
| -> valid ${result.isSuccess}
| -> type `${typeForStack}`
| -> ${search.undet_s}
| -> ${search.ctx_s}
| -> tree:
|${showCode(result.tree)}
| -> forced expansions:
|${forcedExpansions.mkString(" ", " \n", "\n")}
|""".stripMargin
)
}
val thisStackName = s"${typeToString(typeForStack)}$suffix"
stackedNames.update(searchId, List(thisStackName))
children.foreach { childSearch =>
val id = childSearch.searchId
val childrenStackName = stackedNames.getOrElse(id, missing("stack name"))
stackedNames.update(id, thisStackName :: childrenStackName)
}
// Save the nanos for this implicit search
val searchTimer = getSearchTimerFor(searchId)
val stackedType = searchIdsToTargetTypes.getOrElse(searchId, missing("stack type"))
statistics.stopTimer(searchTimer, searchStart)
val (previousNanos, _) = stackedNanos.getOrElse(searchId, (0L, stackedType))
stackedNanos.+=((searchId, ((searchTimer.nanos + previousNanos), stackedType)))
}
// 3. Reset the stack and stop timer if there is a dependant search
val previousImplicits = implicitsStack.tail
implicitsStack = previousImplicits.headOption match {
case Some((prevSearch, prevImplicitTypeStart, _)) =>
stopTimerFlamegraph(Some(prevSearch))
statistics.stopTimer(timer, implicitTypeStart)
val newPrevStart = statistics.startTimer(getSearchTimerFor(prevSearch.searchId))
(prevSearch, prevImplicitTypeStart, newPrevStart) :: previousImplicits.tail
case None =>
stopTimerFlamegraph(None)
statistics.stopTimer(timer, implicitTypeStart)
previousImplicits
}
}
}
}
sealed trait MacroState {
def pt: Type
def tree: Tree
}
case class DelayedMacro(pt: Type, tree: Tree) extends MacroState
case class SkippedMacro(pt: Type, tree: Tree) extends MacroState
case class SuppressedMacro(pt: Type, tree: Tree) extends MacroState
case class FallbackMacro(pt: Type, tree: Tree) extends MacroState
case class FailedMacro(pt: Type, tree: Tree) extends MacroState
case class SucceededMacro(pt: Type, tree: Tree) extends MacroState
case class MacroEntry(
id: Int,
originalPt: Type,
start: statistics.TimerSnapshot,
state: Option[MacroState]
)
private var macrosStack: List[MacroEntry] = Nil
private var macroCounter: Int = 0
object ProfilingMacroPlugin extends global.analyzer.MacroPlugin {
type Typer = analyzer.Typer
private def guessTreeSize(tree: Tree): Int =
1 + tree.children.map(guessTreeSize).sum
type RepeatedKey = (String, String)
// case class RepeatedValue(original: Tree, result: Tree, count: Int)
// private final val EmptyRepeatedValue = RepeatedValue(EmptyTree, EmptyTree, 0)
// private[ProfilingImpl] val repeatedTrees = perRunCaches.newMap[RepeatedKey, RepeatedValue]
val macroInfos = perRunCaches.newAnyRefMap[Position, MacroInfo]()
val searchIdsToMacroStates = perRunCaches.newMap[Int, List[MacroState]]()
private val macroIdsToTimers = perRunCaches.newMap[Int, statistics.Timer]()
private val macroChildren = perRunCaches.newMap[Int, List[MacroEntry]]()
private val stackedNanos = perRunCaches.newMap[Int, Long]()
private val stackedNames = perRunCaches.newMap[Int, List[String]]()
def foldMacroStacks(outputPaths: Seq[AbsolutePath]): Unit =
if (outputPaths.nonEmpty) {
// This part is memory intensive and hence the use of java collections
val stacksJavaList = new java.util.ArrayList[String]()
stackedNanos.foreach {
case (id, nanos) =>
val names =
stackedNames.getOrElse(id, sys.error(s"Stack name for macro id ${id} doesn't exist!"))
val stackName = names.mkString(";")
stacksJavaList.add(s"$stackName ${nanos / 1000}")
}
java.util.Collections.sort(stacksJavaList)
outputPaths.foreach(path =>
Files.write(
path.underlying,
stacksJavaList,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE
)
)
} else ()
import scala.tools.nsc.Mode
override def pluginsMacroExpand(t: Typer, expandee: Tree, md: Mode, pt: Type): Option[Tree] = {
val macroId = macroCounter
macroCounter = macroCounter + 1
object expander extends analyzer.DefMacroExpander(t, expandee, md, pt) {
private var alreadyTracking: Boolean = false
/** The default method that expands all macros. */
override def apply(desugared: Tree): Tree = {
def updateExpansionTime(desugared: Tree, start: statistics.TimerSnapshot): Unit = {
statistics.stopTimer(preciseMacroTimer, start)
val (nanos0, _) = start
val timeNanos = (preciseMacroTimer.nanos - nanos0)
val callSitePos = desugared.pos
// Those that are not present failed to expand
macroInfos.get(callSitePos).foreach { found =>
val updatedInfo = found.copy(expansionNanos = timeNanos)
macroInfos(callSitePos) = updatedInfo
}
}
val shouldTrack = statistics.enabled && !alreadyTracking
val prevData = macrosStack.headOption.map { prev =>
macroIdsToTimers.getOrElse(
prev.id,
sys.error(s"fatal error: missing timer for ${prev.id}")
) -> prev
}
// Let's first stop the previous timer to have consistent times for the flamegraph
prevData.foreach {
case (prevTimer, prev) => statistics.stopTimer(prevTimer, prev.start)
}
// Let's create our own timer
val searchPrefix = s" macro ${macroId}"
val macroTimer = registerTyperTimerFor(searchPrefix)
macroIdsToTimers += ((macroId, macroTimer))
val start = {
alreadyTracking = true
statistics.startTimer(macroTimer)
}
val entry = MacroEntry(macroId, pt, start, None)
if (config.generateMacroFlamegraph) {
// We add ourselves to the child list of our parent macro
prevData.foreach {
case (_, entry) =>
val prevId = entry.id
val prevChilds = macroChildren.getOrElse(prevId, Nil)
macroChildren.update(prevId, entry :: prevChilds)
}
}
macrosStack = entry :: macrosStack
try super.apply(desugared)
finally {
if (shouldTrack) {
alreadyTracking = false
updateExpansionTime(desugared, start)
}
val children = macroChildren.getOrElse(macroId, Nil)
if (config.generateMacroFlamegraph) {
// Complete stack names of triggered implicit searches
prevData.foreach {
case (_, p) =>
val prevChildren = macroChildren.getOrElse(p.id, Nil)
macroChildren.update(p.id, children ::: prevChildren)
}
}
// We need to fetch the entry from the stack as it can be modified
val parents = macrosStack.tail
macrosStack.headOption match {
case Some(head) =>
if (config.generateMacroFlamegraph) {
val thisStackName = head.state match {
case Some(FailedMacro(pt, _)) => s"${typeToString(pt)} [failed]"
case Some(DelayedMacro(pt, _)) => s"${typeToString(pt)} [delayed]"
case Some(SucceededMacro(pt, _)) => s"${typeToString(pt)}"
case Some(SuppressedMacro(pt, _)) => s"${typeToString(pt)} [suppressed]"
case Some(SkippedMacro(pt, _)) => s"${typeToString(pt)} [skipped]"
case Some(FallbackMacro(pt, _)) => s"${typeToString(pt)} [fallback]"
case None => sys.error("Fatal error: macro has no state!")
}
stackedNames.update(macroId, thisStackName :: Nil)
children.foreach { childSearch =>
val id = childSearch.id
val childrenStackName = stackedNames.getOrElse(id, sys.error("no stack name"))
stackedNames.update(id, thisStackName :: childrenStackName)
}
}
statistics.stopTimer(macroTimer, head.start)
val previousNanos = stackedNanos.getOrElse(macroId, 0L)
stackedNanos.+=((macroId, macroTimer.nanos + previousNanos))
prevData match {
case Some((prevTimer, prev)) =>
// Let's restart the timer of the previous macro expansion
val newStart = statistics.startTimer(prevTimer)
// prev is the head of `parents`, so let's replace it on stack with the new start
macrosStack = prev.copy(start = newStart) :: parents.tail
case None => macrosStack = parents
}
case None => sys.error(s"fatal error: expected macro entry for macro id $macroId")
}
}
}
def mapToCurrentImplicitSearch(exp: MacroState): Unit = {
implicitsStack.headOption match {
case Some(i) =>
val id = i._1.searchId
val currentMacros = searchIdsToMacroStates.getOrElse(id, Nil)
searchIdsToMacroStates.update(id, exp :: currentMacros)
case None => ()
}
}
def updateStack(state: MacroState): Unit = {
macrosStack.headOption match {
case Some(entry) =>
macrosStack = entry.copy(state = Some(state)) :: macrosStack.tail
case None => sys.error("fatal error: stack cannot be empty while updating!")
}
}
override def onFailure(expanded: Tree) = {
val state = FailedMacro(pt, expanded)
mapToCurrentImplicitSearch(state)
statistics.incCounter(failedMacros)
updateStack(state)
super.onFailure(expanded)
}
override def onSkipped(expanded: Tree) = {
val state = SkippedMacro(pt, expanded)
mapToCurrentImplicitSearch(state)
statistics.incCounter(skippedMacros)
updateStack(state)
super.onDelayed(expanded)
}
override def onFallback(expanded: Tree) = {
val state = FallbackMacro(pt, expanded)
mapToCurrentImplicitSearch(state)
statistics.incCounter(fallbackMacros)
updateStack(state)
super.onFallback(expanded)
}
override def onSuppressed(expanded: Tree) = {
val state = SuppressedMacro(pt, expanded)
mapToCurrentImplicitSearch(state)
statistics.incCounter(suppressedMacros)
updateStack(state)
super.onSuppressed(expanded)
}
override def onDelayed(expanded: Tree) = {
val state = DelayedMacro(pt, expanded)
mapToCurrentImplicitSearch(state)
statistics.incCounter(delayedMacros)
updateStack(state)
super.onDelayed(expanded)
}
override def onSuccess(expanded0: Tree) = {
val expanded = super.onSuccess(expanded0)
val expandedType = concreteTypeFromSearch(expanded, pt)
val state = SucceededMacro(expandedType, expanded)
mapToCurrentImplicitSearch(state)
updateStack(state)
// Update macro counter per type returned
val macroTypeCounter = macrosByType.getOrElse(expandedType, 0)
macrosByType.update(expandedType, macroTypeCounter + 1)
val callSitePos = this.expandee.pos
/* val printedExpandee = showRaw(expandee)
val printedExpanded = showRaw(expanded)
val key = (printedExpandee, printedExpanded)
val currentValue = repeatedTrees.getOrElse(key, EmptyRepeatedValue)
val newValue = RepeatedValue(expandee, expanded, currentValue.count + 1)
repeatedTrees.put(key, newValue)*/
val macroInfo = macroInfos.getOrElse(callSitePos, MacroInfo.Empty)
val expandedMacros = macroInfo.expandedMacros + 1
val treeSize = 0 // macroInfo.expandedNodes + guessTreeSize(expanded)
// Use 0L for the timer because it will be filled in by the caller `apply`
macroInfos.put(callSitePos, MacroInfo(expandedMacros, treeSize, 0L))
expanded
}
}
Some(expander(expandee))
}
}
}
trait ProfilingStats {
val global: Global
import global.statistics.{newSubCounter, macroExpandCount, implicitSearchCount, newTimer}
macroExpandCount.children.clear()
final val preciseMacroTimer = newTimer("precise time in macroExpand")
final val failedMacros = newSubCounter(" of which failed macros", macroExpandCount)
final val delayedMacros = newSubCounter(" of which delayed macros", macroExpandCount)
final val suppressedMacros = newSubCounter(" of which suppressed macros", macroExpandCount)
final val fallbackMacros = newSubCounter(" of which fallback macros", macroExpandCount)
final val skippedMacros = newSubCounter(" of which skipped macros", macroExpandCount)
final val implicitSearchesByMacrosCount = newSubCounter(" from macros", implicitSearchCount)
import scala.reflect.internal.util.Position
import scala.collection.mutable
final val macrosByType = new mutable.HashMap[global.Type, Int]()
final val implicitSearchesByType = global.perRunCaches.newMap[global.Type, Int]()
final val implicitSearchesByPos = global.perRunCaches.newMap[Position, Int]()
final val implicitSearchesSourceFilesByType =
global.perRunCaches.newMap[global.Type, mutable.HashSet[SourceFile]]()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy