@@ -15,15 +15,15 @@ import Origins._
1515 * You could do this:
1616 *
1717 * {{{
18- * private lazy val origins = Origins[SymbolTable]("phase_= ")
18+ * private lazy val origins = Origins("arbitraryTag ")
1919 * // Commented out original enclosed for contrast
2020 * // final def phase_=(p: Phase): Unit = {
2121 * final def phase_=(p: Phase): Unit = origins {
2222 * }}}
2323 *
2424 * And that's it. When the JVM exits it would issue a report something like this:
2525 {{{
26- >> Origins scala.tools.nsc.symtab.SymbolTable.phase_= logged 145585 calls from 51 distinguished sources.
26+ >> Origins tag 'arbitraryTag' logged 145585 calls from 51 distinguished sources.
2727
2828 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862)
2929 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757)
@@ -37,37 +37,29 @@ import Origins._
3737 */
3838abstract class Origins {
3939 type Rep
40+ type StackSlice = Array [StackTraceElement ]
41+
42+ def tag : String
43+ def isCutoff (el : StackTraceElement ): Boolean
4044 def newRep (xs : StackSlice ): Rep
4145 def repString (rep : Rep ): String
42- def originClass : String
43-
44- private var _tag : String = null
45- def tag : String = _tag
46- def setTag (tag : String ): this .type = {
47- _tag = tag
48- this
49- }
5046
5147 private val origins = new mutable.HashMap [Rep , Int ] withDefaultValue 0
5248 private def add (xs : Rep ) = origins(xs) += 1
5349 private def total = origins.values.foldLeft(0L )(_ + _)
5450
55- // We find the right line by dropping any from around here and any
56- // from the method's origin class.
57- private def dropStackElement (cn : String ) =
58- (cn startsWith OriginsName ) || (cn startsWith originClass)
59-
6051 // Create a stack and whittle it down to the interesting part.
61- private def readStack (): Array [StackTraceElement ] =
62- (new Throwable ).getStackTrace dropWhile (el => dropStackElement(el.getClassName))
52+ def readStack (): Array [StackTraceElement ] = (
53+ Thread .currentThread.getStackTrace dropWhile (x => ! isCutoff(x)) dropWhile isCutoff drop 1
54+ )
6355
6456 def apply [T ](body : => T ): T = {
6557 add(newRep(readStack()))
6658 body
6759 }
6860 def clear () = origins.clear()
6961 def show () = {
70- println(" \n >> Origins %s.%s logged %s calls from %s distinguished sources.\n " .format(originClass, tag, total, origins.keys.size))
62+ println(" \n >> Origins tag '%s' logged %s calls from %s distinguished sources.\n " .format(tag, total, origins.keys.size))
7163 origins.toList sortBy (- _._2) foreach {
7264 case (k, v) => println(" %7s %s" .format(v, repString(k)))
7365 }
@@ -79,29 +71,49 @@ abstract class Origins {
7971}
8072
8173object Origins {
82- private type StackSlice = Array [StackTraceElement ]
83- private val OriginsName = classOf [Origins ].getName
84- private val counters = new mutable.HashSet [Origins ]
74+ private val counters = mutable.HashMap [String , Origins ]()
75+ private val thisClass = this .getClass.getName
8576
86- {
87- // Console.println("\nOrigins loaded: registering shutdown hook to display results.")
88- sys.addShutdownHook(counters foreach (_.purge()))
77+ locally {
78+ sys.addShutdownHook(counters.values foreach (_.purge()))
8979 }
9080
91- def apply [T : ClassTag ](tag : String ): Origins = apply(tag, classTag[T ].erasure)
92- def apply (tag : String , clazz : Class [_]): Origins = apply(tag, new OneLine (clazz))
93- def apply (tag : String , orElse : => Origins ): Origins = {
94- counters find (_.tag == tag) getOrElse {
95- val res = orElse setTag tag
96- counters += res
97- res
98- }
81+ case class OriginId (className : String , methodName : String ) {
82+ def matches (el : StackTraceElement ) = (
83+ (methodName == el.getMethodName) && (className startsWith el.getClassName)
84+ )
9985 }
10086
101- class OneLine (clazz : Class [_]) extends Origins {
102- type Rep = StackTraceElement
103- val originClass = clazz.getName stripSuffix MODULE_SUFFIX_STRING
104- def newRep (xs : StackSlice ): Rep = xs(0 )
105- def repString (rep : Rep ) = " " + rep
87+ def lookup (tag : String , orElse : String => Origins ): Origins =
88+ counters.getOrElseUpdate(tag, orElse(tag))
89+ def register (x : Origins ): Origins = {
90+ counters(x.tag) = x
91+ x
92+ }
93+
94+ private def preCutoff (el : StackTraceElement ) = (
95+ (el.getClassName == thisClass)
96+ || (el.getClassName startsWith " java.lang." )
97+ )
98+ private def findCutoff () = {
99+ val cutoff = Thread .currentThread.getStackTrace dropWhile preCutoff head;
100+ OriginId (cutoff.getClassName, cutoff.getMethodName)
101+ }
102+
103+ def apply (tag : String ): Origins = counters.getOrElseUpdate(tag, new OneLine (tag, findCutoff()))
104+ def apply (tag : String , frames : Int ): Origins = counters.getOrElseUpdate(tag, new MultiLine (tag, findCutoff(), frames))
105+
106+ class OneLine (val tag : String , id : OriginId ) extends Origins {
107+ type Rep = StackTraceElement
108+ def isCutoff (el : StackTraceElement ) = id matches el
109+ def newRep (xs : StackSlice ): Rep = if ((xs eq null ) || (xs.length == 0 )) null else xs(0 )
110+ def repString (rep : Rep ) = " " + rep
111+ }
112+ class MultiLine (val tag : String , id : OriginId , numLines : Int ) extends Origins {
113+ type Rep = List [StackTraceElement ]
114+ def isCutoff (el : StackTraceElement ) = id matches el
115+ def newRep (xs : StackSlice ): Rep = (xs take numLines).toList
116+ def repString (rep : Rep ) = rep.map(" \n " + _).mkString
117+ override def readStack () = super .readStack() drop 1
106118 }
107119}
0 commit comments