Skip to content

Commit 17dc30c

Browse files
committed
8352414: JFR: JavaMonitorDeflateEvent crashes when deflated monitor object is dead
Reviewed-by: dholmes, mgronlun
1 parent 6bc4803 commit 17dc30c

File tree

4 files changed

+174
-8
lines changed

4 files changed

+174
-8
lines changed

src/hotspot/share/jfr/metadata/metadata.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@
123123
</Event>
124124

125125
<Event name="JavaMonitorDeflate" category="Java Application" label="Java Monitor Deflated">
126-
<Field type="Class" name="monitorClass" label="Monitor Class" />
127-
<Field type="ulong" contentType="address" name="address" label="Monitor Address" relation="JavaMonitorAddress" />
126+
<Field type="Class" name="monitorClass" label="Monitor Class" description="Class of the object deflated. If null or N/A, the object has been garbage collected." />
127+
<Field type="ulong" contentType="address" name="address" label="Monitor Address" relation="JavaMonitorAddress" description="Address of the object deflated. If null or N/A, the object has been garbage collected."/>
128128
</Event>
129129

130130
<Event name="JavaMonitorStatistics" category="Java Application, Statistics" label="Java Monitor Statistics" period="everyChunk">

src/hotspot/share/runtime/objectMonitor.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -737,12 +737,19 @@ bool ObjectMonitor::try_lock_or_add_to_entry_list(JavaThread* current, ObjectWai
737737
static void post_monitor_deflate_event(EventJavaMonitorDeflate* event,
738738
const oop obj) {
739739
assert(event != nullptr, "invariant");
740-
const Klass* monitor_klass = obj->klass();
741-
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
742-
return;
740+
if (obj == nullptr) {
741+
// Accept the case when obj was already garbage-collected.
742+
// Emit the event anyway, but without details.
743+
event->set_monitorClass(nullptr);
744+
event->set_address(0);
745+
} else {
746+
const Klass* monitor_klass = obj->klass();
747+
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
748+
return;
749+
}
750+
event->set_monitorClass(monitor_klass);
751+
event->set_address((uintptr_t)(void*)obj);
743752
}
744-
event->set_monitorClass(monitor_klass);
745-
event->set_address((uintptr_t)(void*)obj);
746753
event->commit();
747754
}
748755

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright Amazon.com Inc. 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package jdk.jfr.event.runtime;
25+
26+
import static jdk.test.lib.Asserts.assertTrue;
27+
28+
import java.util.ArrayList;
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Set;
33+
import java.util.concurrent.atomic.AtomicLong;
34+
import java.util.concurrent.TimeUnit;
35+
import java.util.concurrent.ThreadLocalRandom;
36+
37+
import jdk.jfr.consumer.RecordingStream;
38+
import jdk.jfr.consumer.RecordedClass;
39+
import jdk.jfr.consumer.RecordedEvent;
40+
import jdk.test.lib.jfr.EventNames;
41+
import jdk.test.lib.jfr.Events;
42+
import jdk.test.lib.thread.TestThread;
43+
import jdk.test.lib.thread.XRun;
44+
45+
/**
46+
* @test StressJavaMonitorEvents
47+
* @summary Tests that VM does not crash when monitor-related JFR events are enabled
48+
* @requires vm.flagless
49+
* @requires vm.hasJFR
50+
* @library /test/lib
51+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedAsyncDeflationInterval=100 jdk.jfr.event.runtime.StressJavaMonitorEvents
52+
*/
53+
public class StressJavaMonitorEvents {
54+
55+
static final int RUN_TIME_MS = 10000;
56+
static final int GC_EVERY_MS = 500;
57+
static final int THREADS = 4;
58+
static final int NUM_LOCKS = 1024;
59+
60+
static final List<String> CAPTURE_EVENTS = List.of(
61+
EventNames.JavaMonitorEnter,
62+
EventNames.JavaMonitorWait,
63+
EventNames.JavaMonitorNotify,
64+
EventNames.JavaMonitorInflate,
65+
EventNames.JavaMonitorDeflate,
66+
EventNames.JavaMonitorStatistics
67+
);
68+
69+
static final Set<String> CAN_BE_ZERO_EVENTS = Set.of(
70+
// Only when lock actually gets contended, not guaranteed.
71+
EventNames.JavaMonitorEnter,
72+
// Only when there are waiters on the lock, not guaranteed.
73+
EventNames.JavaMonitorNotify
74+
);
75+
76+
static final Object[] LOCKS = new Object[NUM_LOCKS];
77+
78+
public static TestThread startThread(final long deadline) {
79+
TestThread t = new TestThread(new XRun() {
80+
@Override
81+
public void xrun() throws Throwable {
82+
ThreadLocalRandom r = ThreadLocalRandom.current();
83+
while (System.nanoTime() < deadline) {
84+
// Overwrite random lock, making the old one dead
85+
LOCKS[r.nextInt(NUM_LOCKS)] = new Object();
86+
87+
// Wait on random lock, inflating it
88+
Object waitLock = LOCKS[r.nextInt(NUM_LOCKS)];
89+
if (waitLock != null) {
90+
synchronized (waitLock) {
91+
waitLock.wait(1);
92+
}
93+
}
94+
95+
// Notify a random lock
96+
Object notifyLock = LOCKS[r.nextInt(NUM_LOCKS)];
97+
if (notifyLock != null) {
98+
synchronized (notifyLock) {
99+
notifyLock.notify();
100+
}
101+
}
102+
103+
// Notify all on a random lock
104+
Object notifyAllLock = LOCKS[r.nextInt(NUM_LOCKS)];
105+
if (notifyAllLock != null) {
106+
synchronized (notifyAllLock) {
107+
notifyAllLock.notifyAll();
108+
}
109+
}
110+
}
111+
}
112+
});
113+
t.start();
114+
return t;
115+
}
116+
117+
public static void main(String[] args) throws Exception {
118+
Map<String, AtomicLong> counters = new HashMap<>();
119+
120+
try (RecordingStream rs = new RecordingStream()) {
121+
// Setup all interesting events, and start recording.
122+
for (String ev : CAPTURE_EVENTS) {
123+
rs.enable(ev).withoutThreshold();
124+
AtomicLong counter = new AtomicLong();
125+
rs.onEvent(ev, e -> counter.incrementAndGet());
126+
counters.put(ev, counter);
127+
}
128+
rs.startAsync();
129+
130+
long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(RUN_TIME_MS);
131+
List<TestThread> threads = new ArrayList<>();
132+
for (int t = 0; t < THREADS; t++) {
133+
threads.add(startThread(deadline));
134+
}
135+
136+
// Trigger GCs periodically to yield dead objects with inflated monitors.
137+
while (System.nanoTime() < deadline) {
138+
Thread.sleep(GC_EVERY_MS);
139+
System.gc();
140+
}
141+
142+
// Wait for all threads to exit and close the recording.
143+
for (TestThread t : threads) {
144+
t.join();
145+
}
146+
rs.close();
147+
148+
// Print stats and check event counts.
149+
for (String ev : CAPTURE_EVENTS) {
150+
long count = counters.get(ev).get();
151+
System.out.println(ev + ": " + count);
152+
assertTrue(CAN_BE_ZERO_EVENTS.contains(ev) || (count > 0));
153+
}
154+
}
155+
}
156+
}

test/jdk/jdk/jfr/event/runtime/TestJavaMonitorDeflateEvent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ public class TestJavaMonitorDeflateEvent {
5656
static class Lock {
5757
}
5858

59+
// Make sure the object stays reachable.
60+
// This guarantees the fields are fully set up on deflation.
61+
static final Lock lock = new Lock();
62+
5963
public static void main(String[] args) throws Exception {
60-
final Lock lock = new Lock();
6164
final String lockClassName = lock.getClass().getName().replace('.', '/');
6265

6366
List<RecordedEvent> events = new CopyOnWriteArrayList<>();

0 commit comments

Comments
 (0)