Skip to content

Commit 1c083b5

Browse files
authored
Merge pull request oracle#5 from Shopify/thermometer
Introduce the thermometer tool
2 parents 0c01efd + d534d2d commit 1c083b5

File tree

27 files changed

+1101
-3
lines changed

27 files changed

+1101
-3
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@ test-output/
5050
workingsets.xml
5151
bench-results.json
5252
jmh_result.json
53-
/vm/src/installer/dist/
53+
/vm/src/installer/dist/
54+
/sdk/latest_graalvm
55+
/sdk/latest_graalvm_home

compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/CancellationBailoutException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public final class CancellationBailoutException extends RetryableBailoutExceptio
3232

3333
private static final long serialVersionUID = 6551793589275293360L;
3434

35-
public CancellationBailoutException() {
35+
private CancellationBailoutException() {
3636
super("Compilation cancelled.");
3737
}
3838

compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/BackgroundCompileQueue.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ public int getQueueSize() {
118118
}
119119
}
120120

121+
public int getRunning() {
122+
final ExecutorService threadPool = compilationExecutorService;
123+
if (threadPool instanceof ThreadPoolExecutor) {
124+
return ((ThreadPoolExecutor) threadPool).getActiveCount();
125+
} else {
126+
return 0;
127+
}
128+
}
129+
121130
public void shutdownAndAwaitTermination(long timeout) {
122131
final ExecutorService threadPool;
123132
synchronized (this) {

compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.graalvm.compiler.truffle.common.TruffleDebugJavaMethod;
6060
import org.graalvm.compiler.truffle.common.TruffleOutputGroup;
6161
import org.graalvm.compiler.truffle.runtime.BackgroundCompileQueue.Priority;
62+
import org.graalvm.compiler.truffle.runtime.debug.CompilationStatusListener;
6263
import org.graalvm.compiler.truffle.runtime.debug.StatisticsListener;
6364
import org.graalvm.compiler.truffle.runtime.debug.TraceASTCompilationListener;
6465
import org.graalvm.compiler.truffle.runtime.debug.TraceCallTreeListener;
@@ -391,6 +392,7 @@ public ResolvedJavaType resolveType(MetaAccessProvider metaAccess, String classN
391392
}
392393

393394
protected void installDefaultListeners() {
395+
CompilationStatusListener.install(this);
394396
TraceCompilationListener.install(this);
395397
TraceCompilationPolymorphismListener.install(this);
396398
TraceCallTreeListener.install(this);
@@ -854,6 +856,10 @@ public int getCompilationQueueSize() {
854856
return getCompileQueue().getQueueSize();
855857
}
856858

859+
public int getCompilationsRunning() {
860+
return getCompileQueue().getRunning();
861+
}
862+
857863
public boolean isCompiling(OptimizedCallTarget optimizedCallTarget) {
858864
return optimizedCallTarget.isCompiling();
859865
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.compiler.truffle.runtime.debug;
26+
27+
import com.oracle.truffle.api.instrumentation.CompilationState;
28+
29+
public class CompilationStateImpl implements CompilationState {
30+
31+
private final int queued;
32+
private final int running;
33+
private final int finished;
34+
private final int failures;
35+
private final int dequeues;
36+
private final int deoptimizations;
37+
38+
public CompilationStateImpl(int queued, int running, int finished, int failures, int dequeues, int deoptimizations) {
39+
this.queued = queued;
40+
this.running = running;
41+
this.finished = finished;
42+
this.failures = failures;
43+
this.dequeues = dequeues;
44+
this.deoptimizations = deoptimizations;
45+
}
46+
47+
@Override
48+
public int getQueued() {
49+
return queued;
50+
}
51+
52+
@Override
53+
public int getRunning() {
54+
return running;
55+
}
56+
57+
@Override
58+
public int getFinished() {
59+
return finished;
60+
}
61+
62+
@Override
63+
public int getFailed() {
64+
return failures;
65+
}
66+
67+
@Override
68+
public int getDequeued() {
69+
return dequeues;
70+
}
71+
72+
@Override
73+
public int getDeoptimizations() {
74+
return deoptimizations;
75+
}
76+
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.compiler.truffle.runtime.debug;
26+
27+
import com.oracle.truffle.api.frame.Frame;
28+
import com.oracle.truffle.api.instrumentation.CompilationState;
29+
import com.oracle.truffle.api.instrumentation.CompilationStateBackdoor;
30+
import org.graalvm.compiler.truffle.common.TruffleCompilerListener.CompilationResultInfo;
31+
import org.graalvm.compiler.truffle.common.TruffleCompilerListener.GraphInfo;
32+
import org.graalvm.compiler.truffle.runtime.*;
33+
34+
import java.util.concurrent.atomic.AtomicInteger;
35+
36+
public final class CompilationStatusListener extends AbstractGraalTruffleRuntimeListener {
37+
38+
private AtomicInteger finished = new AtomicInteger();
39+
private AtomicInteger failures = new AtomicInteger();
40+
private AtomicInteger dequeues = new AtomicInteger();
41+
private AtomicInteger deoptimizations = new AtomicInteger();
42+
43+
private CompilationStatusListener(GraalTruffleRuntime runtime) {
44+
super(runtime);
45+
CompilationStateBackdoor.ACCESSOR = this::sampleCompilationState;
46+
}
47+
48+
public static void install(GraalTruffleRuntime runtime) {
49+
runtime.addListener(new CompilationStatusListener(runtime));
50+
}
51+
52+
@Override
53+
public synchronized void onCompilationDequeued(OptimizedCallTarget target, Object source, CharSequence reason) {
54+
dequeues.getAndIncrement();
55+
}
56+
57+
@Override
58+
public synchronized void onCompilationInvalidated(OptimizedCallTarget target, Object source, CharSequence reason) {
59+
// We count invalidations as deoptimizations
60+
deoptimizations.getAndIncrement();
61+
}
62+
63+
@Override
64+
public synchronized void onCompilationSuccess(OptimizedCallTarget target, TruffleInlining inliningDecision, GraphInfo graph, CompilationResultInfo result) {
65+
finished.getAndIncrement();
66+
}
67+
68+
@Override
69+
public synchronized void onCompilationFailed(OptimizedCallTarget target, String reason, boolean bailout, boolean permanentBailout) {
70+
if (TraceCompilationListener.isPermanentFailure(bailout, permanentBailout)) {
71+
failures.getAndIncrement();
72+
}
73+
}
74+
75+
@Override
76+
public synchronized void onCompilationDeoptimized(OptimizedCallTarget target, Frame frame) {
77+
deoptimizations.getAndIncrement();
78+
}
79+
80+
public synchronized CompilationState sampleCompilationState() {
81+
return new CompilationStateImpl(
82+
runtime.getCompilationQueueSize(),
83+
runtime.getCompilationsRunning(),
84+
finished.get(),
85+
failures.get(),
86+
dequeues.get(),
87+
deoptimizations.get());
88+
}
89+
90+
}

compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/debug/TraceCompilationListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private static void addSourceInfo(Map<String, Object> properties, Object source)
187187
* @see GraalTruffleRuntimeListener#onCompilationFailed(OptimizedCallTarget, String, boolean,
188188
* boolean)
189189
*/
190-
private static boolean isPermanentFailure(boolean bailout, boolean permanentBailout) {
190+
public static boolean isPermanentFailure(boolean bailout, boolean permanentBailout) {
191191
return !bailout || permanentBailout;
192192
}
193193

tools/docs/Thermometer-Manual.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# The Truffle Optimization Thermometer Tool
2+
3+
The Truffle Optimization Thermometer Tool indicates how *warmed-up* your
4+
application is. A *cold* application is running mostly in the interpreter, still
5+
has code to compile, may still loading new code, and may be deoptimizing. A
6+
*warmed up* application is stable and running mostly in compiled code.
7+
8+
It's difficult to talk quantitively about how warm an application is, and there
9+
are many subtle factors at work here, so the tool is more of an *indication* than
10+
a *measurement*.
11+
12+
See https://arxiv.org/abs/1602.00602 for some interesting discussion about
13+
virtual machine warmup.
14+
15+
## Basic usage
16+
17+
Take this example Ruby program. It renders an ERB template. Every now and again
18+
another thread swaps the template. After a few seconds a compilation will be
19+
attempted which will fail.
20+
21+
```ruby
22+
require 'erb'
23+
24+
template = ERB.new('The time is <%= time %>')
25+
26+
Thread.new do
27+
loop do
28+
sleep 3 + rand(3)
29+
template = ERB.new("The time was #{Time.now} but is now <%= time #{' ' * 10_000} %>")
30+
end
31+
end
32+
33+
Thread.new do
34+
sleep 3 + rand(3)
35+
loop do
36+
Truffle::Graal.bailout 'demo compilation failure'
37+
end
38+
end
39+
40+
dev_null = File.open('/dev/null', 'w')
41+
42+
loop do
43+
time = Time.now
44+
dev_null.puts template.result(binding)
45+
end
46+
```
47+
48+
Run with the `--thermometer` flag (we use
49+
`--vm.Dgraal.TruffleCompilationExceptionsAreThrown=true` to stop the
50+
compilation failure being re-tried).
51+
52+
```
53+
% ruby --vm.Dgraal.TruffleCompilationExceptionsAreThrown=true --thermometer demo.rb
54+
```
55+
56+
You'll see log lines like this:
57+
58+
```
59+
[thermometer] INFO: 5.34s 🥶 39° 0.77 MB 25 ▶ 2 ▶ 17 ( 2, 28 ) 0 ▼
60+
[thermometer] INFO: 5.67s 🥶 44° 0.77 MB 25 ▶ 2 ▶ 17 ( 2, 28 ) 0 ▼
61+
[thermometer] INFO: 6.00s 🤔 73° 0.77 MB 0 ▶ 0 ▶ 20 ( 2, 43 ) 0 ▼
62+
[thermometer] INFO: 6.34s 😊 97° 0.77 MB 0 ▶ 0 ▶ 20 ( 2, 43 ) 0 ▼
63+
[thermometer] INFO: 6.67s 😊 100° 0.77 MB 0 ▶ 0 ▶ 20 ( 2, 43 ) 0 ▼
64+
[thermometer] INFO: 7.00s 🤮 52° 0.84 MB 33 ▶ 2 ▶ 26 ( 2, 45 ) 4 ▼
65+
[thermometer] INFO: 7.33s 🥶 48° 0.84 MB 40 ▶ 2 ▶ 26 ( 2, 45 ) 4 ▼
66+
[thermometer] INFO: 7.67s 🤔 70° 0.84 MB 25 ▶ 2 ▶ 27 ( 2, 77 ) 4 ▼
67+
[thermometer] INFO: 8.00s 🤔 85° 0.84 MB 4 ▶ 2 ▶ 29 ( 2, 88 ) 4 ▼
68+
[thermometer] INFO: 8.33s 😊 94° 0.84 MB 0 ▶ 1 ▶ 30 ( 2, 92 ) 4 ▼
69+
[thermometer] INFO: 8.67s 😊 100° 0.84 MB 0 ▶ 1 ▶ 30 ( 2, 92 ) 4 ▼
70+
```
71+
72+
* `6.34s` is how long the application has been running by wall clock
73+
* `🥶🤔😊` indicate very broadly whether the application is very cold, warming up, or warmed up
74+
* `73°` indicates the *temperature* - how much of the application is compiled - see *Mechanism* below for details of how this is calculated
75+
* `0.84 MB` is how much code has been loaded
76+
* The next three numbers are the current compilation backlog, running compilations, and finished compilations
77+
* The numbers in brackets are failures and dequeued compilations
78+
* The final number is deoptimizations and invalidations
79+
80+
## Monitoring performance
81+
82+
`--thermometer.IterationPoint=test.rb:24` will install an iterations-per-second
83+
counter on any statements at this location. You should ensure there is just one
84+
statement at this location as each statement run will count as an iteration.
85+
86+
```
87+
[thermometer] INFO: 6.67s 🤮 79° 0.167 M i/s 0.81 MB 5 ▶ 2 ▶ 24 ( 3, 45 ) 3 ▼
88+
```
89+
90+
## Advanced usage
91+
92+
* `--thermometer.SamplingPeriod=10` sets the sampling period in ms.
93+
94+
* `--thermometer.ReportingPeriod=300` sets the reporting period in ms.
95+
96+
## Mechanism
97+
98+
The *temperature* is the percentage of samples taken during the period where the
99+
top-most Truffle method activation is running in compiled code. Method preludes
100+
set a per-thread flag to indicate whether they're compiled or not. A separate
101+
high-priority timer thread samples this flag.
102+
103+
The indicator is set to `🥶` for a temperature `< 0.5`, `🤔` for `< 0.9`, and
104+
`😊` otherwise. If there was a deoptimization in the period, it is instead set
105+
to `🤮`. If there was a compilation error in the period, it is instead set to
106+
`😡`.
107+
108+
The indicator isn't set higher than `🤔` if new code was loaded in the period.
109+
110+
## Overhead
111+
112+
Setting the flag is a volatile write. There is some method indirection in the
113+
interpreter. In compiled code the flag is set with these machine instructions.
114+
Note that the flag is set at the root of each logical method, not at the root of
115+
each compilation unit.
116+
117+
```
118+
movabs $counter,%rax
119+
movl $0x1,field(%rax)
120+
lock addl $0x0,(%rsp)
121+
```
122+
123+
## Issues
124+
125+
There is a single flag, so the thermometer works best with applications with a
126+
single thread running most of the time.
127+
128+
The sample flag is set for each guest-language method root, so a compilation
129+
unit may set it multiple times increasing overhead.
130+
131+
Counters are `int` so may overflow during a very long running process.
132+
133+
(The `#{' ' * 10_000}` in the demo is there so code size can be seen to
134+
grow more easily.)

tools/mx.tools/mx_tools.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ def _tools_gate_runner(args, tasks):
208208
include_by_default=True,
209209
))
210210

211+
mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVmTool(
212+
suite=_suite,
213+
name='GraalVM Thermometer',
214+
short_name='temp',
215+
dir_name='thermometer',
216+
license_files=[],
217+
third_party_license_files=[],
218+
dependencies=['Truffle'],
219+
truffle_jars=['tools:TRUFFLE_THERMOMETER'],
220+
support_distributions=['tools:TRUFFLE_THERMOMETER_GRAALVM_SUPPORT'],
221+
include_by_default=True,
222+
))
223+
211224
mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVmJdkComponent(
212225
suite=_suite,
213226
name='VisualVM',

0 commit comments

Comments
 (0)