@@ -2,13 +2,19 @@ package scala.tools.nsc.profile
2
2
3
3
import java .io .{FileWriter , PrintWriter }
4
4
import java .lang .management .ManagementFactory
5
+ import java .nio .file .Files
5
6
import java .util .ServiceLoader
6
7
import java .util .concurrent .TimeUnit
7
8
import java .util .concurrent .atomic .AtomicInteger
9
+
8
10
import javax .management .openmbean .CompositeData
9
11
import javax .management .{Notification , NotificationEmitter , NotificationListener }
10
12
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 }
12
18
13
19
object Profiler {
14
20
def apply (settings : Settings ): Profiler =
@@ -20,13 +26,15 @@ object Profiler {
20
26
new RealProfiler (reporter, settings)
21
27
}
22
28
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 )
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()
24
33
}
25
- case class GcEventData (pool: String , reportTimeNs : Long , gcStartMillis: Long , gcEndMillis: Long , name: String , action: String , cause: String , threads: Long )
26
34
27
35
case 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 ) {
30
38
def updateHeap (heapBytes: Long ) = {
31
39
copy(heapBytes = heapBytes)
32
40
}
@@ -61,20 +69,38 @@ case class ProfileRange(start: ProfileSnap, end:ProfileSnap, phase:Phase, purpos
61
69
def retainedHeapMB = toMegaBytes(end.heapBytes - start.heapBytes)
62
70
}
63
71
64
- sealed trait Profiler {
72
+ sealed abstract class Profiler {
65
73
66
74
def finished (): Unit
67
75
68
76
def beforePhase (phase : Phase ): ProfileSnap
69
77
70
78
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 = ()
71
95
}
72
96
private [profile] object NoOpProfiler extends Profiler {
73
97
74
98
override def beforePhase (phase : Phase ): ProfileSnap = Profiler .emptySnap
75
99
76
100
override def afterPhase (phase : Phase , profileBefore : ProfileSnap ): Unit = ()
77
101
102
+ override def beforeUnit (phase : Phase , file : AbstractFile ): ProfileSnap = Profiler .emptySnap
103
+ override def afterUnit (phase : Phase , file : AbstractFile , profileBefore : ProfileSnap ): Unit = ()
78
104
override def finished (): Unit = ()
79
105
}
80
106
private [profile] object RealProfiler {
@@ -91,6 +117,14 @@ private [profile] object RealProfiler {
91
117
}
92
118
93
119
private [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
+
94
128
def completeBackground (threadRange : ProfileRange ): Unit = {
95
129
reporter.reportBackground(this , threadRange)
96
130
}
@@ -110,16 +144,17 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
110
144
private [profile] def snapThread ( idleTimeNanos: Long ): ProfileSnap = {
111
145
import RealProfiler ._
112
146
val current = Thread .currentThread()
113
-
147
+ val allocatedBytes = threadMx.getThreadAllocatedBytes( Thread .currentThread().getId)
114
148
ProfileSnap (
115
149
threadId = current.getId,
116
150
threadName = current.getName,
117
151
snapTimeNanos = System .nanoTime(),
118
152
idleTimeNanos = idleTimeNanos,
119
153
cpuTimeNanos = threadMx.getCurrentThreadCpuTime,
120
154
userTimeNanos = threadMx.getCurrentThreadUserTime,
121
- allocatedBytes = threadMx.getThreadAllocatedBytes(Thread .currentThread().getId),
122
- heapBytes = readHeapUsage()
155
+ allocatedBytes = allocatedBytes,
156
+ heapBytes = readHeapUsage(),
157
+ totalClassesLoaded = classLoaderMx.getTotalLoadedClassCount
123
158
)
124
159
}
125
160
private def readHeapUsage () = RealProfiler .memoryMx.getHeapMemoryUsage.getUsed
@@ -139,8 +174,19 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
139
174
case gc =>
140
175
}
141
176
reporter.close(this )
177
+ for (gcEvent <- gcEvents) {
178
+ val durationNanos = TimeUnit .MILLISECONDS .toNanos(gcEvent.durationMillis)
179
+ val startNanos = gcEvent.endNanos - durationNanos
180
+ chromeTrace.traceDurationEvent(gcEvent.name, startNanos, durationNanos, GcThreadId )
181
+ }
182
+ chromeTrace.traceAsyncEventEnd(" scalac" , 0 , " 0" )
183
+
184
+ println(" Trace file: " + tracePath)
185
+ chromeTrace.close()
142
186
}
143
187
188
+ private val gcEvents = ArrayBuffer [GcEventData ]()
189
+ private val GcThreadId = 0
144
190
145
191
override def handleNotification (notification : Notification , handback : scala.Any ): Unit = {
146
192
import java .lang .{Long => jLong }
@@ -161,7 +207,9 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
161
207
val startTime = info.get(" startTime" ).asInstanceOf [jLong].longValue()
162
208
val endTime = info.get(" endTime" ).asInstanceOf [jLong].longValue()
163
209
val threads = info.get(" GcThreadCount" ).asInstanceOf [jInt].longValue()
164
- reporter.reportGc(GcEventData (" " , reportNs, startTime, endTime, name, action, cause, threads))
210
+ val gcEvent = GcEventData (" " , reportNs, startTime, endTime, duration, name, action, cause, threads)
211
+ gcEvents += gcEvent
212
+ reporter.reportGc(gcEvent)
165
213
}
166
214
}
167
215
@@ -177,22 +225,86 @@ private [profile] class RealProfiler(reporter : ProfileReporter, val settings: S
177
225
doGC
178
226
initialSnap.updateHeap(readHeapUsage())
179
227
} else initialSnap
228
+ chromeTrace.traceAsyncEventEnd(phase.name, 0 , " 0" )
180
229
181
230
reporter.reportForeground(this , ProfileRange (snapBefore, finalSnap, phase, " " , 0 , Thread .currentThread))
182
231
}
183
232
233
+ override def beforeUnit (phase : Phase , file : AbstractFile ): ProfileSnap = {
234
+ assert(mainThread eq Thread .currentThread())
235
+ snapThread(0 )
236
+ }
237
+
238
+ override def afterUnit (phase : Phase , file : AbstractFile , snapBefore : ProfileSnap ): Unit = {
239
+ assert(mainThread eq Thread .currentThread())
240
+ val initialSnap = snapThread(0 )
241
+ chromeTrace.traceCounterEvent(" allocBytes" , " allocBytes" , initialSnap.allocatedBytes - this .baseline.allocatedBytes)
242
+ chromeTrace.traceCounterEvent(" heapBytes" , " heapBytes" , initialSnap.heapBytes - this .baseline.heapBytes)
243
+ chromeTrace.traceCounterEvent(" classesLoaded" , " classesLoaded" , initialSnap.totalClassesLoaded - this .baseline.totalClassesLoaded)
244
+ chromeTrace.traceCounterEvent(" userTime" , " userTime" , initialSnap.userTimeNanos - this .baseline.userTimeNanos)
245
+ chromeTrace.traceCounterEvent(" cpuTime" , " cpuTime" , initialSnap.cpuTimeNanos - this .baseline.cpuTimeNanos)
246
+ chromeTrace.traceCounterEvent(" idleTime" , " idleTime" , initialSnap.idleTimeNanos - this .baseline.idleTimeNanos)
247
+ }
248
+
249
+ private var baseline : ProfileSnap = _
250
+
184
251
override def beforePhase (phase : Phase ): ProfileSnap = {
185
252
assert(mainThread eq Thread .currentThread())
253
+ chromeTrace.traceAsyncEventStart(phase.name, 0 , " 0" )
186
254
if (settings.YprofileRunGcBetweenPhases .containsPhase(phase))
187
255
doGC
188
256
if (settings.YprofileExternalTool .containsPhase(phase)) {
189
257
println(" Profile hook start" )
190
258
ExternalToolHook .before()
191
259
}
192
260
active foreach {_.beforePhase(phase)}
193
- snapThread(0 )
261
+ val snap = snapThread(0 )
262
+ if (phase.prev eq null ) baseline = snap
263
+ snap
264
+ }
265
+
266
+ private val completingStack = new mutable.ArrayStack [Global # Symbol ]
267
+ private val stringsOf = (0 to 128 ).map(_.toString).toArray
268
+ def intToString (i : Int ) = if (i < 128 ) stringsOf(i) else i.toString
269
+
270
+ import scala .reflect .internal .util .ChromeTrace
271
+ private def traceId = if (completingStack.isEmpty) 0 else completingStack.top.id
272
+ override def beforeTypedImplDef (sym : Global # Symbol ): Unit = {
273
+ chromeTrace.traceAsyncEventStart(sym.rawname.toString, traceId, intToString(completingStack.length))
274
+ }
275
+ override def afterTypedImplDef (sym : Global # Symbol ): Unit = {
276
+ chromeTrace.traceAsyncEventEnd(sym.rawname.toString, traceId, intToString(completingStack.length))
277
+ }
278
+
279
+ override def beforeImplicitSearch (pt : Global # Symbol ): Unit = {
280
+ chromeTrace.traceAsyncEventStart(" <implicit> " + pt.rawname, traceId, intToString(completingStack.length))
194
281
}
195
282
283
+ override def afterImplicitSearch (pt : Global # Symbol ): Unit = {
284
+ chromeTrace.traceAsyncEventEnd(" <implicit> " + pt.rawname, traceId, intToString(completingStack.length))
285
+ }
286
+
287
+ override def beforeMacroExpansion (macroSym : Global # Symbol ): Unit = {
288
+ chromeTrace.traceAsyncEventStart(" <macro> " + macroSym.rawname, traceId, intToString(completingStack.length))
289
+ }
290
+
291
+ override def afterMacroExpansion (macroSym : Global # Symbol ): Unit = {
292
+ chromeTrace.traceAsyncEventEnd(" <macro> " + macroSym.rawname, traceId, intToString(completingStack.length))
293
+ }
294
+
295
+ override def beforeCompletion (root : Global # Symbol ): Unit = {
296
+ chromeTrace.traceAsyncEventStart(" <wait>" , traceId, intToString(completingStack.length))
297
+ completingStack.push(root)
298
+ chromeTrace.traceAsyncEventStart(" <completion>" , traceId, intToString(completingStack.length))
299
+ chromeTrace.traceAsyncEventStart(root.rawname.toString, traceId, intToString(completingStack.length))
300
+ }
301
+
302
+ override def afterCompletion (root : Global # Symbol ): Unit = {
303
+ chromeTrace.traceAsyncEventEnd(root.rawname.toString, traceId, intToString(completingStack.length))
304
+ chromeTrace.traceAsyncEventEnd(" <completion>" , traceId, intToString(completingStack.length))
305
+ completingStack.pop()
306
+ chromeTrace.traceAsyncEventEnd(" <wait>" , traceId, intToString(completingStack.length))
307
+ }
196
308
}
197
309
198
310
object EventType extends Enumeration {
0 commit comments