Skip to content

Commit db8e0db

Browse files
committed
Fix scala#2909: Port per-phase profiling from scalac
scala/scala#5848
1 parent fa6c579 commit db8e0db

File tree

6 files changed

+767
-1
lines changed

6 files changed

+767
-1
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,24 @@ import Symbols._
88
import Phases._
99
import Types._
1010
import Scopes._
11-
import typer.{FrontEnd, Typer, ImportInfo, RefChecks}
11+
import typer.{FrontEnd, ImportInfo, RefChecks, Typer}
1212
import Decorators._
1313
import io.{AbstractFile, PlainFile}
14+
1415
import scala.io.Codec
1516
import util.{Set => _, _}
1617
import reporting.Reporter
1718
import transform.TreeChecker
1819
import rewrite.Rewrites
1920
import java.io.{BufferedWriter, OutputStreamWriter}
21+
22+
import dotty.tools.dotc.profile.Profiler
2023
import printing.XprintMode
2124
import typer.ImplicitRunInfo
2225

2326
import scala.annotation.tailrec
2427
import dotty.tools.io.VirtualFile
28+
2529
import scala.util.control.NonFatal
2630

2731
/** A compiler run. Exports various methods to compile source files */
@@ -118,11 +122,14 @@ class Run(comp: Compiler, ictx: Context) {
118122

119123
def runPhases(implicit ctx: Context) = {
120124
var lastPrintedTree: PrintedTree = NoPrintedTree
125+
val profiler = Profiler()
121126
for (phase <- ctx.allPhases)
122127
if (phase.isRunnable)
123128
Stats.trackTime(s"$phase ms ") {
124129
val start = System.currentTimeMillis
130+
val profileBefore = profiler.beforePhase(phase)
125131
ctx.runInfo.units = phase.runOn(ctx.runInfo.units)
132+
profiler.afterPhase(phase, profileBefore)
126133
if (ctx.settings.Xprint.value.containsPhase(phase)) {
127134
for (unit <- ctx.runInfo.units) {
128135
lastPrintedTree =
@@ -134,6 +141,8 @@ class Run(comp: Compiler, ictx: Context) {
134141
for (unit <- ctx.runInfo.units)
135142
Stats.record(s"retained typed trees at end of $phase", unit.tpdTree.treeSize)
136143
}
144+
145+
profiler.finished()
137146
}
138147

139148
val runCtx = ctx.fresh

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ class ScalaSettings extends Settings.SettingGroup {
111111
val YretainTrees = BooleanSetting("-Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree")
112112
val YshowTreeIds = BooleanSetting("-Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.")
113113

114+
val YprofileEnabled = BooleanSetting("-Yprofile-enabled", "Enable profiling.")
115+
val YprofileDestination = StringSetting("-Yprofile-destination", "file", "where to send profiling output - specify a file, default is to the console.", "")
116+
//.withPostSetHook( _ => YprofileEnabled.value = true )
117+
val YprofileExternalTool = PhasesSetting("-Yprofile-external-tool", "Enable profiling for a phase using an external tool hook. Generally only useful for a single phase", "typer")
118+
//.withPostSetHook( _ => YprofileEnabled.value = true )
119+
val YprofileRunGcBetweenPhases = PhasesSetting("-Yprofile-run-gc", "Run a GC between phases - this allows heap size to be accurate at the expense of more time. Specify a list of phases, or *", "_")
120+
//.withPostSetHook( _ => YprofileEnabled.value = true )
121+
114122
/** Area-specific debug output */
115123
val YexplainLowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
116124
val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
package dotty.tools.dotc.profile;
2+
3+
import javax.management.ObjectName;
4+
import java.lang.management.ThreadInfo;
5+
import java.lang.management.ThreadMXBean;
6+
import java.lang.management.ManagementFactory;
7+
import java.lang.reflect.Method;
8+
9+
@SuppressWarnings("unused")
10+
public abstract class ExtendedThreadMxBean implements ThreadMXBean {
11+
static final ExtendedThreadMxBean proxy;
12+
13+
static {
14+
ExtendedThreadMxBean local;
15+
ThreadMXBean threadMx = ManagementFactory.getThreadMXBean();
16+
try {
17+
Class cls = Class.forName("com.sun.management.ThreadMXBean");
18+
if (cls.isInstance(threadMx)) {
19+
local = new SunThreadMxBean(threadMx);
20+
} else {
21+
local = new OtherThreadMxBean(threadMx);
22+
}
23+
} catch (ClassNotFoundException e) {
24+
local = new OtherThreadMxBean(threadMx);
25+
}
26+
proxy = local;
27+
}
28+
29+
protected final ThreadMXBean underlying;
30+
31+
protected ExtendedThreadMxBean(ThreadMXBean underlying) {
32+
this.underlying = underlying;
33+
}
34+
35+
public abstract long[] getThreadUserTime(long[] longs) throws Exception;
36+
37+
public abstract boolean isThreadAllocatedMemoryEnabled() throws Exception;
38+
39+
public abstract void setThreadAllocatedMemoryEnabled(boolean b) throws Exception;
40+
41+
public abstract long getThreadAllocatedBytes(long l) throws Exception;
42+
43+
public abstract long[] getThreadAllocatedBytes(long[] longs) throws Exception;
44+
45+
public abstract boolean isThreadAllocatedMemorySupported() throws Exception;
46+
47+
public abstract long[] getThreadCpuTime(long[] longs) throws Exception;
48+
//common features from java.lang.management.ThreadMXBean
49+
50+
@Override
51+
public int getThreadCount() {
52+
return underlying.getThreadCount();
53+
}
54+
55+
@Override
56+
public int getPeakThreadCount() {
57+
return underlying.getPeakThreadCount();
58+
}
59+
60+
@Override
61+
public long getTotalStartedThreadCount() {
62+
return underlying.getTotalStartedThreadCount();
63+
}
64+
65+
@Override
66+
public int getDaemonThreadCount() {
67+
return underlying.getDaemonThreadCount();
68+
}
69+
70+
@Override
71+
public long[] getAllThreadIds() {
72+
return underlying.getAllThreadIds();
73+
}
74+
75+
@Override
76+
public ThreadInfo getThreadInfo(long id) {
77+
return underlying.getThreadInfo(id);
78+
}
79+
80+
@Override
81+
public ThreadInfo[] getThreadInfo(long[] ids) {
82+
return underlying.getThreadInfo(ids);
83+
}
84+
85+
@Override
86+
public ThreadInfo getThreadInfo(long id, int maxDepth) {
87+
return underlying.getThreadInfo(id, maxDepth);
88+
}
89+
90+
@Override
91+
public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) {
92+
return underlying.getThreadInfo(ids, maxDepth);
93+
}
94+
95+
@Override
96+
public boolean isThreadContentionMonitoringSupported() {
97+
return underlying.isThreadContentionMonitoringSupported();
98+
}
99+
100+
@Override
101+
public boolean isThreadContentionMonitoringEnabled() {
102+
return underlying.isThreadContentionMonitoringEnabled();
103+
}
104+
105+
@Override
106+
public void setThreadContentionMonitoringEnabled(boolean enable) {
107+
underlying.setThreadContentionMonitoringEnabled(enable);
108+
}
109+
110+
@Override
111+
public long getCurrentThreadCpuTime() {
112+
return underlying.getCurrentThreadCpuTime();
113+
}
114+
115+
@Override
116+
public long getCurrentThreadUserTime() {
117+
return underlying.getCurrentThreadUserTime();
118+
}
119+
120+
@Override
121+
public long getThreadCpuTime(long id) {
122+
return underlying.getThreadCpuTime(id);
123+
}
124+
125+
@Override
126+
public long getThreadUserTime(long id) {
127+
return underlying.getThreadUserTime(id);
128+
}
129+
130+
@Override
131+
public boolean isThreadCpuTimeSupported() {
132+
return underlying.isThreadCpuTimeSupported();
133+
}
134+
135+
@Override
136+
public boolean isCurrentThreadCpuTimeSupported() {
137+
return underlying.isCurrentThreadCpuTimeSupported();
138+
}
139+
140+
@Override
141+
public boolean isThreadCpuTimeEnabled() {
142+
return underlying.isThreadCpuTimeEnabled();
143+
}
144+
145+
@Override
146+
public void setThreadCpuTimeEnabled(boolean enable) {
147+
underlying.setThreadCpuTimeEnabled(enable);
148+
}
149+
150+
@Override
151+
public long[] findMonitorDeadlockedThreads() {
152+
return underlying.findMonitorDeadlockedThreads();
153+
}
154+
155+
@Override
156+
public void resetPeakThreadCount() {
157+
underlying.resetPeakThreadCount();
158+
}
159+
160+
@Override
161+
public long[] findDeadlockedThreads() {
162+
return underlying.findDeadlockedThreads();
163+
}
164+
165+
@Override
166+
public boolean isObjectMonitorUsageSupported() {
167+
return underlying.isObjectMonitorUsageSupported();
168+
}
169+
170+
@Override
171+
public boolean isSynchronizerUsageSupported() {
172+
return underlying.isSynchronizerUsageSupported();
173+
}
174+
175+
@Override
176+
public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers) {
177+
return underlying.getThreadInfo(ids, lockedMonitors, lockedSynchronizers);
178+
}
179+
180+
@Override
181+
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers) {
182+
return underlying.dumpAllThreads(lockedMonitors, lockedSynchronizers);
183+
}
184+
185+
@Override
186+
public ObjectName getObjectName() {
187+
return underlying.getObjectName();
188+
}
189+
}
190+
191+
class OtherThreadMxBean extends ExtendedThreadMxBean {
192+
OtherThreadMxBean(ThreadMXBean underlying) {
193+
super(underlying);
194+
}
195+
196+
@Override
197+
public long[] getThreadUserTime(long[] longs) throws Exception {
198+
return new long[0];
199+
}
200+
201+
@Override
202+
public boolean isThreadAllocatedMemoryEnabled() throws Exception {
203+
return false;
204+
}
205+
206+
@Override
207+
public void setThreadAllocatedMemoryEnabled(boolean b) throws Exception {
208+
209+
}
210+
211+
@Override
212+
public long getThreadAllocatedBytes(long l) throws Exception {
213+
return -1;
214+
}
215+
216+
@Override
217+
public long[] getThreadAllocatedBytes(long[] longs) throws Exception {
218+
return new long[0];
219+
}
220+
221+
@Override
222+
public boolean isThreadAllocatedMemorySupported() throws Exception {
223+
return false;
224+
}
225+
226+
@Override
227+
public long[] getThreadCpuTime(long[] longs) throws Exception {
228+
return new long[0];
229+
}
230+
231+
}
232+
233+
234+
class SunThreadMxBean extends ExtendedThreadMxBean {
235+
236+
private final ThreadMXBean real;
237+
238+
private final Method getThreadUserTimeMethod;
239+
private final Method isThreadAllocatedMemoryEnabledMethod;
240+
private final Method setThreadAllocatedMemoryEnabledMethod;
241+
private final Method getThreadAllocatedBytesMethod1;
242+
private final Method getThreadAllocatedBytesMethod2;
243+
private final Method isThreadAllocatedMemorySupportedMethod;
244+
private final Method getThreadCpuTimeMethod;
245+
246+
247+
public SunThreadMxBean(ThreadMXBean underlying) {
248+
super(underlying);
249+
this.real = underlying;
250+
try {
251+
getThreadUserTimeMethod = real.getClass().getMethod("getThreadUserTime", long[].class);
252+
isThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("isThreadAllocatedMemoryEnabled");
253+
setThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("setThreadAllocatedMemoryEnabled", Boolean.TYPE);
254+
getThreadAllocatedBytesMethod1 = real.getClass().getMethod("getThreadAllocatedBytes", Long.TYPE);
255+
getThreadAllocatedBytesMethod2 = real.getClass().getMethod("getThreadAllocatedBytes", long[].class);
256+
isThreadAllocatedMemorySupportedMethod = real.getClass().getMethod("isThreadAllocatedMemorySupported");
257+
getThreadCpuTimeMethod = real.getClass().getMethod("getThreadCpuTime", long[].class);
258+
259+
getThreadUserTimeMethod.setAccessible(true);
260+
isThreadAllocatedMemoryEnabledMethod.setAccessible(true);
261+
setThreadAllocatedMemoryEnabledMethod.setAccessible(true);
262+
getThreadAllocatedBytesMethod1.setAccessible(true);
263+
getThreadAllocatedBytesMethod2.setAccessible(true);
264+
isThreadAllocatedMemorySupportedMethod.setAccessible(true);
265+
getThreadCpuTimeMethod.setAccessible(true);
266+
} catch (Exception e) {
267+
throw new IllegalStateException(e);
268+
}
269+
}
270+
271+
272+
public boolean isExtended() {
273+
return true;
274+
}
275+
276+
public long[] getThreadUserTime(long[] longs) throws Exception {
277+
return (long[]) getThreadUserTimeMethod.invoke(real, longs);
278+
}
279+
280+
public boolean isThreadAllocatedMemoryEnabled() throws Exception {
281+
return (boolean) isThreadAllocatedMemoryEnabledMethod.invoke(real);
282+
}
283+
284+
public void setThreadAllocatedMemoryEnabled(boolean b) throws Exception {
285+
setThreadAllocatedMemoryEnabledMethod.invoke(real, b);
286+
}
287+
288+
public long getThreadAllocatedBytes(long l) throws Exception {
289+
return (long) getThreadAllocatedBytesMethod1.invoke(real,l);
290+
}
291+
292+
public long[] getThreadAllocatedBytes(long[] longs) throws Exception {
293+
return (long[]) getThreadAllocatedBytesMethod2.invoke(real, longs);
294+
}
295+
296+
public boolean isThreadAllocatedMemorySupported() throws Exception {
297+
return (boolean) isThreadAllocatedMemorySupportedMethod.invoke(real);
298+
}
299+
300+
public long[] getThreadCpuTime(long[] longs) throws Exception {
301+
return (long[]) getThreadCpuTimeMethod.invoke(real, longs);
302+
303+
}
304+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dotty.tools.dotc.profile;
2+
3+
/**
4+
* This is an external tool hook, it allows an external tool such as YourKit or JProfiler to instrument a
5+
* particular phase of the compiler.
6+
* Profilers have hooks to allow starting and stopping profiling on a given method invocation.
7+
* To use add -Yprofile-external-tool (defaults to typer) or -Yprofile-external-tool:<phase> (for a specific compiler phase)
8+
* to the compiler flags.
9+
*
10+
* 'before' will be called at the start of the target phase and 'after' at the end, allowing custom profiling to be
11+
* triggered.
12+
*/
13+
public class ExternalToolHook {
14+
private ExternalToolHook() {}
15+
public static void before() {}
16+
public static void after() {}
17+
}

0 commit comments

Comments
 (0)