Skip to content

Commit

Permalink
Usability improvements to Origins.
Browse files Browse the repository at this point in the history
  • Loading branch information
paulp committed May 23, 2012
1 parent 402b5e4 commit 32ee111
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 39 deletions.
86 changes: 49 additions & 37 deletions src/compiler/scala/reflect/internal/util/Origins.scala
Expand Up @@ -15,15 +15,15 @@ import Origins._
* You could do this: * You could do this:
* *
* {{{ * {{{
* private lazy val origins = Origins[SymbolTable]("phase_=") * private lazy val origins = Origins("arbitraryTag")
* // Commented out original enclosed for contrast * // Commented out original enclosed for contrast
* // final def phase_=(p: Phase): Unit = { * // final def phase_=(p: Phase): Unit = {
* final def phase_=(p: Phase): Unit = origins { * final def phase_=(p: Phase): Unit = origins {
* }}} * }}}
* *
* And that's it. When the JVM exits it would issue a report something like this: * And that's it. When the JVM exits it would issue a report something like this:
{{{ {{{
>> Origins scala.tools.nsc.symtab.SymbolTable.phase_= logged 145585 calls from 51 distinguished sources. >> Origins tag 'arbitraryTag' logged 145585 calls from 51 distinguished sources.
71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862) 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862)
16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757) 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757)
Expand All @@ -37,37 +37,29 @@ import Origins._
*/ */
abstract class Origins { abstract class Origins {
type Rep type Rep
type StackSlice = Array[StackTraceElement]

def tag: String
def isCutoff(el: StackTraceElement): Boolean
def newRep(xs: StackSlice): Rep def newRep(xs: StackSlice): Rep
def repString(rep: Rep): String def repString(rep: Rep): String
def originClass: String

private var _tag: String = null
def tag: String = _tag
def setTag(tag: String): this.type = {
_tag = tag
this
}


private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0 private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0
private def add(xs: Rep) = origins(xs) += 1 private def add(xs: Rep) = origins(xs) += 1
private def total = origins.values.foldLeft(0L)(_ + _) private def total = origins.values.foldLeft(0L)(_ + _)


// We find the right line by dropping any from around here and any
// from the method's origin class.
private def dropStackElement(cn: String) =
(cn startsWith OriginsName) || (cn startsWith originClass)

// Create a stack and whittle it down to the interesting part. // Create a stack and whittle it down to the interesting part.
private def readStack(): Array[StackTraceElement] = def readStack(): Array[StackTraceElement] = (
(new Throwable).getStackTrace dropWhile (el => dropStackElement(el.getClassName)) Thread.currentThread.getStackTrace dropWhile (x => !isCutoff(x)) dropWhile isCutoff drop 1
)


def apply[T](body: => T): T = { def apply[T](body: => T): T = {
add(newRep(readStack())) add(newRep(readStack()))
body body
} }
def clear() = origins.clear() def clear() = origins.clear()
def show() = { def show() = {
println("\n>> Origins %s.%s logged %s calls from %s distinguished sources.\n".format(originClass, tag, total, origins.keys.size)) println("\n>> Origins tag '%s' logged %s calls from %s distinguished sources.\n".format(tag, total, origins.keys.size))
origins.toList sortBy (-_._2) foreach { origins.toList sortBy (-_._2) foreach {
case (k, v) => println("%7s %s".format(v, repString(k))) case (k, v) => println("%7s %s".format(v, repString(k)))
} }
Expand All @@ -79,29 +71,49 @@ abstract class Origins {
} }


object Origins { object Origins {
private type StackSlice = Array[StackTraceElement] private val counters = mutable.HashMap[String, Origins]()
private val OriginsName = classOf[Origins].getName private val thisClass = this.getClass.getName
private val counters = new mutable.HashSet[Origins]


{ locally {
// Console.println("\nOrigins loaded: registering shutdown hook to display results.") sys.addShutdownHook(counters.values foreach (_.purge()))
sys.addShutdownHook(counters foreach (_.purge()))
} }


def apply[T: ClassTag](tag: String): Origins = apply(tag, classTag[T].erasure) case class OriginId(className: String, methodName: String) {
def apply(tag: String, clazz: Class[_]): Origins = apply(tag, new OneLine(clazz)) def matches(el: StackTraceElement) = (
def apply(tag: String, orElse: => Origins): Origins = { (methodName == el.getMethodName) && (className startsWith el.getClassName)
counters find (_.tag == tag) getOrElse { )
val res = orElse setTag tag
counters += res
res
}
} }


class OneLine(clazz: Class[_]) extends Origins { def lookup(tag: String, orElse: String => Origins): Origins =
type Rep = StackTraceElement counters.getOrElseUpdate(tag, orElse(tag))
val originClass = clazz.getName stripSuffix MODULE_SUFFIX_STRING def register(x: Origins): Origins = {
def newRep(xs: StackSlice): Rep = xs(0) counters(x.tag) = x
def repString(rep: Rep) = " " + rep 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
} }
} }
2 changes: 1 addition & 1 deletion test/files/run/origins.check
@@ -1,5 +1,5 @@


>> Origins goxbox.Socks.boop logged 65 calls from 3 distinguished sources. >> Origins tag 'boop' logged 65 calls from 3 distinguished sources.


50 Test$$anonfun$f3$1.apply(origins.scala:16) 50 Test$$anonfun$f3$1.apply(origins.scala:16)
10 Test$$anonfun$f2$1.apply(origins.scala:15) 10 Test$$anonfun$f2$1.apply(origins.scala:15)
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/origins.scala
Expand Up @@ -2,7 +2,7 @@ import scala.reflect.internal.util.Origins


package goxbox { package goxbox {
object Socks { object Socks {
val origins = Origins[Socks.type]("boop") val origins = Origins("boop")


def boop(x: Int): Int = origins { 5 } def boop(x: Int): Int = origins { 5 }
} }
Expand Down

0 comments on commit 32ee111

Please sign in to comment.