@@ -2,13 +2,19 @@ package scala.tools.nsc.profile
22
33import java .io .{FileWriter , PrintWriter }
44import java .lang .management .ManagementFactory
5+ import java .nio .file .Files
56import java .util .ServiceLoader
67import java .util .concurrent .TimeUnit
78import java .util .concurrent .atomic .AtomicInteger
9+
810import javax .management .openmbean .CompositeData
911import javax .management .{Notification , NotificationEmitter , NotificationListener }
1012
11- import scala .tools .nsc .{Phase , Settings }
13+ import scala .collection .mutable
14+ import scala .collection .mutable .ArrayBuffer
15+ import scala .reflect .internal .util .ChromeTrace
16+ import scala .reflect .io .{AbstractFile , File }
17+ import scala .tools .nsc .{Global , Phase , Settings }
1218
1319object Profiler {
1420 def apply (settings : Settings ): Profiler =
@@ -20,13 +26,15 @@ object Profiler {
2026 new RealProfiler (reporter, settings)
2127 }
2228
23- private [profile] val emptySnap = ProfileSnap (0 , " " , 0 , 0 , 0 , 0 , 0 , 0 )
29+ private [profile] val emptySnap = ProfileSnap (0 , " " , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
30+ }
31+ case class GcEventData (pool: String , reportTimeNs : Long , gcStartMillis: Long , gcEndMillis: Long , durationMillis : Long , name: String , action: String , cause: String , threads: Long ) {
32+ val endNanos = System .nanoTime()
2433}
25- case class GcEventData (pool: String , reportTimeNs : Long , gcStartMillis: Long , gcEndMillis: Long , name: String , action: String , cause: String , threads: Long )
2634
2735case class ProfileSnap (threadId : Long , threadName : String , snapTimeNanos : Long ,
28- idleTimeNanos: Long , cpuTimeNanos : Long , userTimeNanos : Long ,
29- allocatedBytes: Long , heapBytes: Long ) {
36+ idleTimeNanos: Long , cpuTimeNanos : Long , userTimeNanos : Long ,
37+ allocatedBytes: Long , heapBytes: Long , totalClassesLoaded : Long , totalJITCompilationTime : Long ) {
3038 def updateHeap (heapBytes: Long ) = {
3139 copy(heapBytes = heapBytes)
3240 }
@@ -61,20 +69,38 @@ case class ProfileRange(start: ProfileSnap, end:ProfileSnap, phase:Phase, purpos
6169 def retainedHeapMB = toMegaBytes(end.heapBytes - start.heapBytes)
6270}
6371
64- sealed trait Profiler {
72+ sealed abstract class Profiler {
6573
6674 def finished (): Unit
6775
6876 def beforePhase (phase : Phase ): ProfileSnap
6977
7078 def afterPhase (phase : Phase , profileBefore : ProfileSnap ): Unit
79+
80+ def beforeUnit (phase : Phase , file : AbstractFile ): ProfileSnap
81+
82+ def afterUnit (phase : Phase , file : AbstractFile , profileBefore : ProfileSnap ): Unit
83+
84+ def beforeTypedImplDef (sym : Global # Symbol ): Unit = ()
85+ def afterTypedImplDef (sym : Global # Symbol ): Unit = ()
86+
87+ def beforeImplicitSearch (pt : Global # Symbol ): Unit = ()
88+ def afterImplicitSearch (pt : Global # Symbol ): Unit = ()
89+
90+ def beforeMacroExpansion (macroSym : Global # Symbol ): Unit = ()
91+ def afterMacroExpansion (macroSym : Global # Symbol ): Unit = ()
92+
93+ def beforeCompletion (root : Global # Symbol ): Unit = ()
94+ def afterCompletion (root : Global # Symbol ): Unit = ()
7195}
7296private [profile] object NoOpProfiler extends Profiler {
7397
7498 override def beforePhase (phase : Phase ): ProfileSnap = Profiler .emptySnap
7599
76100 override def afterPhase (phase : Phase , profileBefore : ProfileSnap ): Unit = ()
77101
102+ override def beforeUnit (phase : Phase , file : AbstractFile ): ProfileSnap = Profiler .emptySnap
103+ override def afterUnit (phase : Phase , file : AbstractFile , profileBefore : ProfileSnap ): Unit = ()
78104 override def finished (): Unit = ()
79105}
80106private [profile] object RealProfiler {
@@ -91,6 +117,14 @@ private [profile] object RealProfiler {
91117}
92118
93119private [profile] class RealProfiler (reporter : ProfileReporter , val settings : Settings ) extends Profiler with NotificationListener {
120+
121+ val tracePath = {
122+ if (settings.YprofileDestination .isSetByUser) new File (new java.io.File (settings.YprofileDestination .value)).changeExtension(" trace" ).jfile.toPath
123+ else Files .createTempFile(" scalac-" , " .trace" )
124+ }
125+ private val chromeTrace = new ChromeTrace (tracePath)
126+ chromeTrace.traceAsyncEventStart(" scalac" , 0 , " 0" )
127+
94128 def completeBackground (threadRange : ProfileRange ): Unit = {
95129 reporter.reportBackground(this , threadRange)
96130 }
@@ -110,16 +144,18 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
110144 private [profile] def snapThread ( idleTimeNanos: Long ): ProfileSnap = {
111145 import RealProfiler ._
112146 val current = Thread .currentThread()
113-
147+ val allocatedBytes = threadMx.getThreadAllocatedBytes( Thread .currentThread().getId)
114148 ProfileSnap (
115149 threadId = current.getId,
116150 threadName = current.getName,
117151 snapTimeNanos = System .nanoTime(),
118152 idleTimeNanos = idleTimeNanos,
119153 cpuTimeNanos = threadMx.getCurrentThreadCpuTime,
120154 userTimeNanos = threadMx.getCurrentThreadUserTime,
121- allocatedBytes = threadMx.getThreadAllocatedBytes(Thread .currentThread().getId),
122- heapBytes = readHeapUsage()
155+ allocatedBytes = allocatedBytes,
156+ heapBytes = readHeapUsage(),
157+ totalClassesLoaded = classLoaderMx.getTotalLoadedClassCount,
158+ totalJITCompilationTime = compileMx.getTotalCompilationTime
123159 )
124160 }
125161 private def readHeapUsage () = RealProfiler .memoryMx.getHeapMemoryUsage.getUsed
@@ -139,8 +175,19 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
139175 case gc =>
140176 }
141177 reporter.close(this )
178+ for (gcEvent <- gcEvents) {
179+ val durationNanos = TimeUnit .MILLISECONDS .toNanos(gcEvent.durationMillis)
180+ val startNanos = gcEvent.endNanos - durationNanos
181+ chromeTrace.traceDurationEvent(gcEvent.name, startNanos, durationNanos, GcThreadId )
182+ }
183+ chromeTrace.traceAsyncEventEnd(" scalac" , 0 , " 0" )
184+
185+ println(" Trace file: " + tracePath)
186+ chromeTrace.close()
142187 }
143188
189+ private val gcEvents = ArrayBuffer [GcEventData ]()
190+ private val GcThreadId = 0
144191
145192 override def handleNotification (notification : Notification , handback : scala.Any ): Unit = {
146193 import java .lang .{Long => jLong }
@@ -161,7 +208,9 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
161208 val startTime = info.get(" startTime" ).asInstanceOf [jLong].longValue()
162209 val endTime = info.get(" endTime" ).asInstanceOf [jLong].longValue()
163210 val threads = info.get(" GcThreadCount" ).asInstanceOf [jInt].longValue()
164- reporter.reportGc(GcEventData (" " , reportNs, startTime, endTime, name, action, cause, threads))
211+ val gcEvent = GcEventData (" " , reportNs, startTime, endTime, duration, name, action, cause, threads)
212+ gcEvents += gcEvent
213+ reporter.reportGc(gcEvent)
165214 }
166215 }
167216
@@ -177,22 +226,89 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
177226 doGC
178227 initialSnap.updateHeap(readHeapUsage())
179228 } else initialSnap
229+ chromeTrace.traceAsyncEventEnd(phase.name, 0 , " 0" )
180230
181231 reporter.reportForeground(this , ProfileRange (snapBefore, finalSnap, phase, " " , 0 , Thread .currentThread))
182232 }
183233
234+ override def beforeUnit (phase : Phase , file : AbstractFile ): ProfileSnap = {
235+ assert(mainThread eq Thread .currentThread())
236+ chromeTrace.traceAsyncEventStart(file.name, 0 , " 0" )
237+ snapThread(0 )
238+ }
239+
240+ override def afterUnit (phase : Phase , file : AbstractFile , snapBefore : ProfileSnap ): Unit = {
241+ assert(mainThread eq Thread .currentThread())
242+ val initialSnap = snapThread(0 )
243+ chromeTrace.traceAsyncEventEnd(file.name, 0 , " 0" )
244+ chromeTrace.traceCounterEvent(" allocBytes" , " allocBytes" , initialSnap.allocatedBytes - this .baseline.allocatedBytes)
245+ chromeTrace.traceCounterEvent(" heapBytes" , " heapBytes" , initialSnap.heapBytes - this .baseline.heapBytes)
246+ chromeTrace.traceCounterEvent(" classesLoaded" , " classesLoaded" , initialSnap.totalClassesLoaded - this .baseline.totalClassesLoaded)
247+ chromeTrace.traceCounterEvent(" jitCompilationTime" , " jitCompilationTime" , initialSnap.totalJITCompilationTime - this .baseline.totalJITCompilationTime)
248+ chromeTrace.traceCounterEvent(" userTime" , " userTime" , initialSnap.userTimeNanos - this .baseline.userTimeNanos)
249+ chromeTrace.traceCounterEvent(" cpuTime" , " cpuTime" , initialSnap.cpuTimeNanos - this .baseline.cpuTimeNanos)
250+ chromeTrace.traceCounterEvent(" idleTime" , " idleTime" , initialSnap.idleTimeNanos - this .baseline.idleTimeNanos)
251+ }
252+
253+ private var baseline : ProfileSnap = _
254+
184255 override def beforePhase (phase : Phase ): ProfileSnap = {
185256 assert(mainThread eq Thread .currentThread())
257+ chromeTrace.traceAsyncEventStart(phase.name, 0 , " 0" )
186258 if (settings.YprofileRunGcBetweenPhases .containsPhase(phase))
187259 doGC
188260 if (settings.YprofileExternalTool .containsPhase(phase)) {
189261 println(" Profile hook start" )
190262 ExternalToolHook .before()
191263 }
192264 active foreach {_.beforePhase(phase)}
193- snapThread(0 )
265+ val snap = snapThread(0 )
266+ if (phase.prev eq null ) baseline = snap
267+ snap
268+ }
269+
270+ private val completingStack = new mutable.ArrayStack [Global # Symbol ]
271+ private val stringsOf = (0 to 128 ).map(_.toString).toArray
272+ def intToString (i : Int ) = if (i < 128 ) stringsOf(i) else i.toString
273+
274+ import scala .reflect .internal .util .ChromeTrace
275+ private def traceId = if (completingStack.isEmpty) 0 else completingStack.top.id
276+ override def beforeTypedImplDef (sym : Global # Symbol ): Unit = {
277+ chromeTrace.traceAsyncEventStart(sym.rawname.toString, traceId, intToString(completingStack.length))
278+ }
279+ override def afterTypedImplDef (sym : Global # Symbol ): Unit = {
280+ chromeTrace.traceAsyncEventEnd(sym.rawname.toString, traceId, intToString(completingStack.length))
281+ }
282+
283+ override def beforeImplicitSearch (pt : Global # Symbol ): Unit = {
284+ chromeTrace.traceAsyncEventStart(" <implicit> " + pt.rawname, traceId, intToString(completingStack.length))
194285 }
195286
287+ override def afterImplicitSearch (pt : Global # Symbol ): Unit = {
288+ chromeTrace.traceAsyncEventEnd(" <implicit> " + pt.rawname, traceId, intToString(completingStack.length))
289+ }
290+
291+ override def beforeMacroExpansion (macroSym : Global # Symbol ): Unit = {
292+ chromeTrace.traceAsyncEventStart(" <macro> " + macroSym.rawname, traceId, intToString(completingStack.length))
293+ }
294+
295+ override def afterMacroExpansion (macroSym : Global # Symbol ): Unit = {
296+ chromeTrace.traceAsyncEventEnd(" <macro> " + macroSym.rawname, traceId, intToString(completingStack.length))
297+ }
298+
299+ override def beforeCompletion (root : Global # Symbol ): Unit = {
300+ chromeTrace.traceAsyncEventStart(" <wait>" , traceId, intToString(completingStack.length))
301+ completingStack.push(root)
302+ chromeTrace.traceAsyncEventStart(" <completion>" , traceId, intToString(completingStack.length))
303+ chromeTrace.traceAsyncEventStart(root.rawname.toString, traceId, intToString(completingStack.length))
304+ }
305+
306+ override def afterCompletion (root : Global # Symbol ): Unit = {
307+ chromeTrace.traceAsyncEventEnd(root.rawname.toString, traceId, intToString(completingStack.length))
308+ chromeTrace.traceAsyncEventEnd(" <completion>" , traceId, intToString(completingStack.length))
309+ completingStack.pop()
310+ chromeTrace.traceAsyncEventEnd(" <wait>" , traceId, intToString(completingStack.length))
311+ }
196312}
197313
198314object EventType extends Enumeration {
0 commit comments