summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/util/Origins.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect/scala/reflect/internal/util/Origins.scala')
-rw-r--r--src/reflect/scala/reflect/internal/util/Origins.scala119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/reflect/scala/reflect/internal/util/Origins.scala b/src/reflect/scala/reflect/internal/util/Origins.scala
new file mode 100644
index 0000000000..0bd5ad55ca
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/util/Origins.scala
@@ -0,0 +1,119 @@
+/* NSC -- new scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.reflect
+package internal.util
+
+import NameTransformer._
+import scala.collection.{ mutable, immutable }
+import Origins._
+
+/** A debugging class for logging from whence a method is being called.
+ * Say you wanted to discover who was calling phase_= in SymbolTable.
+ * You could do this:
+ *
+ * {{{
+ * private lazy val origins = Origins("arbitraryTag")
+ * // Commented out original enclosed for contrast
+ * // final def phase_=(p: Phase): Unit = {
+ * final def phase_=(p: Phase): Unit = origins {
+ * }}}
+ *
+ * And that's it. When the JVM exits it would issue a report something like this:
+ {{{
+ >> Origins tag 'arbitraryTag' logged 145585 calls from 51 distinguished sources.
+
+ 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862)
+ 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757)
+ 15411 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:869)
+ 11507 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:770)
+ 10285 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:864)
+ 6860 scala.tools.nsc.transform.SpecializeTypes.specializedTypeVars(SpecializeTypes.scala:304)
+ ...
+ }}}
+ *
+ */
+abstract class Origins {
+ type Rep
+ type StackSlice = Array[StackTraceElement]
+
+ def tag: String
+ def isCutoff(el: StackTraceElement): Boolean
+ def newRep(xs: StackSlice): Rep
+ def repString(rep: Rep): String
+
+ private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0
+ private def add(xs: Rep) = origins(xs) += 1
+ private def total = origins.values.foldLeft(0L)(_ + _)
+
+ // Create a stack and whittle it down to the interesting part.
+ def readStack(): Array[StackTraceElement] = (
+ Thread.currentThread.getStackTrace dropWhile (x => !isCutoff(x)) dropWhile isCutoff drop 1
+ )
+
+ def apply[T](body: => T): T = {
+ add(newRep(readStack()))
+ body
+ }
+ def clear() = origins.clear()
+ def show() = {
+ println("\n>> Origins tag '%s' logged %s calls from %s distinguished sources.\n".format(tag, total, origins.keys.size))
+ origins.toList sortBy (-_._2) foreach {
+ case (k, v) => println("%7s %s".format(v, repString(k)))
+ }
+ }
+ def purge() = {
+ show()
+ clear()
+ }
+}
+
+object Origins {
+ private val counters = mutable.HashMap[String, Origins]()
+ private val thisClass = this.getClass.getName
+
+ locally {
+ sys.addShutdownHook(counters.values foreach (_.purge()))
+ }
+
+ case class OriginId(className: String, methodName: String) {
+ def matches(el: StackTraceElement) = (
+ (methodName == el.getMethodName) && (className startsWith el.getClassName)
+ )
+ }
+
+ def lookup(tag: String, orElse: String => Origins): Origins =
+ counters.getOrElseUpdate(tag, orElse(tag))
+ def register(x: Origins): Origins = {
+ counters(x.tag) = x
+ x
+ }
+
+ private def preCutoff(el: StackTraceElement) = (
+ (el.getClassName == thisClass)
+ || (el.getClassName startsWith "java.lang.")
+ )
+ private def findCutoff() = {
+ val cutoff = Thread.currentThread.getStackTrace dropWhile preCutoff head;
+ OriginId(cutoff.getClassName, cutoff.getMethodName)
+ }
+
+ def apply(tag: String): Origins = counters.getOrElseUpdate(tag, new OneLine(tag, findCutoff()))
+ def apply(tag: String, frames: Int): Origins = counters.getOrElseUpdate(tag, new MultiLine(tag, findCutoff(), frames))
+
+ class OneLine(val tag: String, id: OriginId) extends Origins {
+ type Rep = StackTraceElement
+ def isCutoff(el: StackTraceElement) = id matches el
+ def newRep(xs: StackSlice): Rep = if ((xs eq null) || (xs.length == 0)) null else xs(0)
+ def repString(rep: Rep) = " " + rep
+ }
+ class MultiLine(val tag: String, id: OriginId, numLines: Int) extends Origins {
+ type Rep = List[StackTraceElement]
+ def isCutoff(el: StackTraceElement) = id matches el
+ def newRep(xs: StackSlice): Rep = (xs take numLines).toList
+ def repString(rep: Rep) = rep.map("\n " + _).mkString
+ override def readStack() = super.readStack() drop 1
+ }
+}