Skip to content

Commit 761774a

Browse files
author
Serguei Spitsyn
committedJan 10, 2025
8346143: add ClearAllFramePops function to speedup debugger single stepping in some cases
Reviewed-by: cjplummer, amenkov
1 parent 6f1f2f2 commit 761774a

File tree

11 files changed

+386
-36
lines changed

11 files changed

+386
-36
lines changed
 

‎src/hotspot/share/prims/jvmti.xml

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="ISO-8859-1"?>
22
<?xml-stylesheet type="text/xsl" href="jvmti.xsl"?>
33
<!--
4-
Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
4+
Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
55
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
66

77
This code is free software; you can redistribute it and/or modify it
@@ -3090,6 +3090,34 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
30903090
</errors>
30913091
</function>
30923092

3093+
<function id="ClearAllFramePops" num="67" since="25">
3094+
<synopsis>Clear Frame Pop</synopsis>
3095+
<description>
3096+
Clear all frame pop requests so that a <eventlink id="FramePop"></eventlink>
3097+
event will not be generated for any frames.
3098+
See the <eventlink id="FramePop"></eventlink> event for details.
3099+
<p/>
3100+
The specified thread must be suspended or must be the current thread.
3101+
</description>
3102+
<origin>new</origin>
3103+
<capabilities>
3104+
<required id="can_generate_frame_pop_events"></required>
3105+
</capabilities>
3106+
<parameters>
3107+
<param id="thread">
3108+
<jthread null="current" impl="noconvert"/>
3109+
<description>
3110+
The thread for which all the frame pop events will be cleared.
3111+
</description>
3112+
</param>
3113+
</parameters>
3114+
<errors>
3115+
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
3116+
Thread was not suspended and was not the current thread.
3117+
</error>
3118+
</errors>
3119+
</function>
3120+
30933121
</category>
30943122

30953123
<category id="ForceEarlyReturn" label="Force Early Return">
@@ -15481,6 +15509,9 @@ typedef void (JNICALL *jvmtiEventVMInit)
1548115509
Virtual threads finalized to be a permanent feature.
1548215510
Agent start-up in the live phase now specified to print a warning.
1548315511
</change>
15512+
<change date="10 January 2025" version="25.0.0">
15513+
Add new function ClearAllFramePops. Needed to speedup debugger single stepping.
15514+
</change>
1548415515
</changehistory>
1548515516

1548615517
</specification>

‎src/hotspot/share/prims/jvmtiEnv.cpp

+28-1
Original file line numberDiff line numberDiff line change
@@ -1804,12 +1804,39 @@ JvmtiEnv::NotifyFramePop(jthread thread, jint depth) {
18041804
return JVMTI_ERROR_THREAD_NOT_ALIVE;
18051805
}
18061806

1807-
SetFramePopClosure op(this, state, depth);
1807+
SetOrClearFramePopClosure op(this, state, true /* set */, depth);
18081808
MutexLocker mu(current, JvmtiThreadState_lock);
18091809
JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle);
18101810
return op.result();
18111811
} /* end NotifyFramePop */
18121812

1813+
// Threads_lock NOT held, java_thread not protected by lock
1814+
jvmtiError
1815+
JvmtiEnv::ClearAllFramePops(jthread thread) {
1816+
ResourceMark rm;
1817+
JvmtiVTMSTransitionDisabler disabler(thread);
1818+
JavaThread* current = JavaThread::current();
1819+
ThreadsListHandle tlh(current);
1820+
1821+
JavaThread* java_thread = nullptr;
1822+
oop thread_obj = nullptr;
1823+
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, current, &java_thread, &thread_obj);
1824+
if (err != JVMTI_ERROR_NONE) {
1825+
return err;
1826+
}
1827+
1828+
HandleMark hm(current);
1829+
Handle thread_handle(current, thread_obj);
1830+
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread, thread_handle);
1831+
if (state == nullptr) {
1832+
return JVMTI_ERROR_THREAD_NOT_ALIVE;
1833+
}
1834+
1835+
SetOrClearFramePopClosure op(this, state, false /* clear all frame pops*/);
1836+
MutexLocker mu(current, JvmtiThreadState_lock);
1837+
JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle);
1838+
return op.result();
1839+
} /* end ClearAllFramePops */
18131840

18141841
//
18151842
// Force Early Return functions

‎src/hotspot/share/prims/jvmtiEnvBase.cpp

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1367,6 +1367,13 @@ JvmtiEnvBase::set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth
13671367
return JVMTI_ERROR_NONE;
13681368
}
13691369

1370+
jvmtiError
1371+
JvmtiEnvBase::clear_all_frame_pops(JvmtiThreadState* state) {
1372+
JvmtiEnvThreadState* ets = state->env_thread_state(this);
1373+
ets->clear_all_frame_pops();
1374+
return JVMTI_ERROR_NONE;
1375+
}
1376+
13701377
bool
13711378
JvmtiEnvBase::is_cthread_with_mounted_vthread(JavaThread* jt) {
13721379
oop thread_oop = jt->threadObj();
@@ -2482,7 +2489,7 @@ UpdateForPopTopFrameClosure::doit(Thread *target) {
24822489
}
24832490

24842491
void
2485-
SetFramePopClosure::do_thread(Thread *target) {
2492+
SetOrClearFramePopClosure::do_thread(Thread *target) {
24862493
Thread* current = Thread::current();
24872494
ResourceMark rm(current); // vframes are resource allocated
24882495
JavaThread* java_thread = JavaThread::cast(target);
@@ -2495,6 +2502,10 @@ SetFramePopClosure::do_thread(Thread *target) {
24952502
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
24962503
return;
24972504
}
2505+
if (!_set) { // ClearAllFramePops
2506+
_result = _env->clear_all_frame_pops(_state);
2507+
return;
2508+
}
24982509
if (!java_thread->has_last_Java_frame()) {
24992510
_result = JVMTI_ERROR_NO_MORE_FRAMES;
25002511
return;
@@ -2506,20 +2517,24 @@ SetFramePopClosure::do_thread(Thread *target) {
25062517
RegisterMap::ProcessFrames::skip,
25072518
RegisterMap::WalkContinuation::include);
25082519
javaVFrame* jvf = JvmtiEnvBase::get_cthread_last_java_vframe(java_thread, &reg_map);
2509-
_result = ((JvmtiEnvBase*)_env)->set_frame_pop(_state, jvf, _depth);
2520+
_result = _env->set_frame_pop(_state, jvf, _depth);
25102521
}
25112522

25122523
void
2513-
SetFramePopClosure::do_vthread(Handle target_h) {
2524+
SetOrClearFramePopClosure::do_vthread(Handle target_h) {
25142525
Thread* current = Thread::current();
25152526
ResourceMark rm(current); // vframes are resource allocated
25162527

25172528
if (!_self && !JvmtiVTSuspender::is_vthread_suspended(target_h())) {
25182529
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
25192530
return;
25202531
}
2532+
if (!_set) { // ClearAllFramePops
2533+
_result = _env->clear_all_frame_pops(_state);
2534+
return;
2535+
}
25212536
javaVFrame *jvf = JvmtiEnvBase::get_vthread_jvf(target_h());
2522-
_result = ((JvmtiEnvBase*)_env)->set_frame_pop(_state, jvf, _depth);
2537+
_result = _env->set_frame_pop(_state, jvf, _depth);
25232538
}
25242539

25252540
void

‎src/hotspot/share/prims/jvmtiEnvBase.hpp

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -410,6 +410,7 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
410410
jvmtiError get_frame_location(oop vthread_oop, jint depth,
411411
jmethodID* method_ptr, jlocation* location_ptr);
412412
jvmtiError set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth);
413+
jvmtiError clear_all_frame_pops(JvmtiThreadState* state);
413414
jvmtiError get_object_monitor_usage(JavaThread* calling_thread,
414415
jobject object, jvmtiMonitorUsage* info_ptr);
415416
jvmtiError get_stack_trace(javaVFrame* jvf,
@@ -534,17 +535,19 @@ class UpdateForPopTopFrameClosure : public JvmtiUnitedHandshakeClosure {
534535
};
535536

536537
// HandshakeClosure to set frame pop.
537-
class SetFramePopClosure : public JvmtiUnitedHandshakeClosure {
538+
class SetOrClearFramePopClosure : public JvmtiUnitedHandshakeClosure {
538539
private:
539-
JvmtiEnv *_env;
540+
JvmtiEnvBase *_env;
540541
JvmtiThreadState* _state;
541-
jint _depth;
542+
bool _set;
543+
jint _depth; // used for NotiftyFramePop only
542544

543545
public:
544-
SetFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, jint depth)
545-
: JvmtiUnitedHandshakeClosure("SetFramePopClosure"),
546-
_env(env),
546+
SetOrClearFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, bool set, jint depth = 0)
547+
: JvmtiUnitedHandshakeClosure("SetOrClearFramePopClosure"),
548+
_env((JvmtiEnvBase*)env),
547549
_state(state),
550+
_set(set),
548551
_depth(depth) {}
549552
void do_thread(Thread *target);
550553
void do_vthread(Handle target_h);

‎src/hotspot/share/prims/jvmtiEnvThreadState.cpp

+14-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -71,6 +71,10 @@ JvmtiFramePops::clear(JvmtiFramePop& fp) {
7171
_pops->remove(fp.frame_number());
7272
}
7373

74+
void
75+
JvmtiFramePops::clear_all() {
76+
_pops->clear();
77+
}
7478

7579
int
7680
JvmtiFramePops::clear_to(JvmtiFramePop& fp) {
@@ -212,10 +216,7 @@ void JvmtiEnvThreadState::compare_and_set_current_location(Method* new_method,
212216

213217

214218
JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
215-
#ifdef ASSERT
216-
Thread *current = Thread::current();
217-
#endif
218-
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
219+
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
219220
"frame pop data only accessible from same or detached thread or direct handshake");
220221
if (_frame_pops == nullptr) {
221222
_frame_pops = new JvmtiFramePops();
@@ -230,32 +231,28 @@ bool JvmtiEnvThreadState::has_frame_pops() {
230231
}
231232

232233
void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
233-
#ifdef ASSERT
234-
Thread *current = Thread::current();
235-
#endif
236-
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
234+
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
237235
"frame pop data only accessible from same or detached thread or direct handshake");
238236
JvmtiFramePop fpop(frame_number);
239237
JvmtiEventController::set_frame_pop(this, fpop);
240238
}
241239

242240

243241
void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
244-
#ifdef ASSERT
245-
Thread *current = Thread::current();
246-
#endif
247-
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
242+
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
248243
"frame pop data only accessible from same or detached thread or direct handshake");
249244
JvmtiFramePop fpop(frame_number);
250245
JvmtiEventController::clear_frame_pop(this, fpop);
251246
}
252247

248+
void JvmtiEnvThreadState::clear_all_frame_pops() {
249+
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
250+
"frame pop data only accessible from same or detached thread or direct handshake");
251+
JvmtiEventController::clear_all_frame_pops(this);
252+
}
253253

254254
bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
255-
#ifdef ASSERT
256-
Thread *current = Thread::current();
257-
#endif
258-
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
255+
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
259256
"frame pop data only accessible from same or detached thread or direct handshake");
260257
if (!jvmti_thread_state()->is_interp_only_mode() || _frame_pops == nullptr) {
261258
return false;

‎src/hotspot/share/prims/jvmtiEnvThreadState.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -86,6 +86,7 @@ class JvmtiFramePops : public CHeapObj<mtInternal> {
8686
friend class JvmtiEventControllerPrivate;
8787
void set(JvmtiFramePop& fp);
8888
void clear(JvmtiFramePop& fp);
89+
void clear_all();
8990
int clear_to(JvmtiFramePop& fp);
9091

9192
public:
@@ -184,6 +185,7 @@ class JvmtiEnvThreadState : public CHeapObj<mtInternal> {
184185

185186
void set_frame_pop(int frame_number);
186187
void clear_frame_pop(int frame_number);
188+
void clear_all_frame_pops();
187189

188190
};
189191

‎src/hotspot/share/prims/jvmtiEventController.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -312,6 +312,7 @@ class JvmtiEventControllerPrivate : public AllStatic {
312312

313313
static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
314314
static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
315+
static void clear_all_frame_pops(JvmtiEnvThreadState *env_thread);
315316
static void clear_to_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
316317
static void change_field_watch(jvmtiEvent event_type, bool added);
317318

@@ -946,6 +947,15 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram
946947
recompute_thread_enabled(ets->jvmti_thread_state());
947948
}
948949

950+
void
951+
JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) {
952+
EC_TRACE(("[%s] # clear all frame pops",
953+
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved())
954+
));
955+
956+
ets->get_frame_pops()->clear_all();
957+
recompute_thread_enabled(ets->jvmti_thread_state());
958+
}
949959

950960
void
951961
JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
@@ -1125,6 +1135,12 @@ JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fp
11251135
JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop);
11261136
}
11271137

1138+
void
1139+
JvmtiEventController::clear_all_frame_pops(JvmtiEnvThreadState *ets) {
1140+
assert(JvmtiThreadState_lock->is_locked(), "Must be locked.");
1141+
JvmtiEventControllerPrivate::clear_all_frame_pops(ets);
1142+
}
1143+
11281144
void
11291145
JvmtiEventController::change_field_watch(jvmtiEvent event_type, bool added) {
11301146
MutexLocker mu(JvmtiThreadState_lock);

‎src/hotspot/share/prims/jvmtiEventController.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -231,6 +231,7 @@ class JvmtiEventController : AllStatic {
231231
static void enter_interp_only_mode(JvmtiThreadState* state);
232232
static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
233233
static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
234+
static void clear_all_frame_pops(JvmtiEnvThreadState *env_thread);
234235

235236
static void change_field_watch(jvmtiEvent event_type, bool added);
236237

‎src/hotspot/share/prims/jvmtiExport.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1969,7 +1969,10 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread,
19691969
// remove the frame's entry
19701970
{
19711971
MutexLocker mu(JvmtiThreadState_lock);
1972-
ets->clear_frame_pop(cur_frame_number);
1972+
// Need to recheck the condition as the JVMTI ClearAllFramePops can do its work at a safepoint.
1973+
if (ets->is_frame_pop(cur_frame_number)) {
1974+
ets->clear_frame_pop(cur_frame_number);
1975+
}
19731976
}
19741977
}
19751978
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2025, 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.
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+
/*
25+
* @test id=platform
26+
* @summary Verifies JVMTI ClearAllFramePops clears all FramePop requests
27+
* @library /test/lib
28+
* @run main/othervm/native -agentlib:ClearAllFramePops ClearAllFramePops platform
29+
*/
30+
/*
31+
* @test id=virtual
32+
* @summary Verifies JVMTI ClearAllFramePops clears all FramePop requests
33+
* @library /test/lib
34+
* @run main/othervm/native -agentlib:ClearAllFramePops ClearAllFramePops virtual
35+
*/
36+
37+
public class ClearAllFramePops {
38+
39+
final static int MAX_THREADS_LIMIT = 10;
40+
final static int NESTING_DEPTH = 5;
41+
final static String TEST_THREAD_NAME_BASE = "Test Thread #";
42+
43+
native static void clearAllFramePops();
44+
native static void getReady();
45+
native static void check();
46+
47+
public static void main(String args[]) {
48+
boolean isVirtual = args.length > 0 && args[0].equals("virtual");
49+
final int THREADS_LIMIT = Math.min(Runtime.getRuntime().availableProcessors() + 1, MAX_THREADS_LIMIT);
50+
Thread[] t = new Thread[THREADS_LIMIT];
51+
getReady();
52+
Thread.Builder builder = (isVirtual ? Thread.ofVirtual() : Thread.ofPlatform())
53+
.name(TEST_THREAD_NAME_BASE, 0);
54+
for (int i = 0; i < THREADS_LIMIT; i++) {
55+
t[i] = builder.start(new TestTask());
56+
}
57+
for (int i = 0; i < THREADS_LIMIT; i++) {
58+
try {
59+
t[i].join();
60+
} catch (InterruptedException e) {
61+
throw new Error("Unexpected: " + e);
62+
}
63+
}
64+
check();
65+
}
66+
67+
static class TestTask implements Runnable {
68+
int nestingCount = 0;
69+
70+
public void run() {
71+
if (nestingCount < NESTING_DEPTH) {
72+
nestingCount++;
73+
run();
74+
} else {
75+
clearAllFramePops();
76+
}
77+
}
78+
}
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2025, 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.
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+
#include <stdio.h>
25+
#include <stdlib.h>
26+
#include <string.h>
27+
#include <time.h>
28+
#include "jvmti.h"
29+
#include "jvmti_common.hpp"
30+
31+
32+
extern "C" {
33+
34+
static jvmtiEnv *jvmti;
35+
static jvmtiEventCallbacks callbacks;
36+
static jrawMonitorID event_lock;
37+
static jboolean watch_events = JNI_FALSE;
38+
static int pop_count;
39+
static const char* TEST_THREAD_NAME_BASE = "Test Thread";
40+
static const char* TEST_CLASS_SIG = "LClearAllFramePops$TestTask;";
41+
42+
static
43+
bool isTestThread(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr) {
44+
char* tname = get_thread_name(jvmti, jni, thr);
45+
bool result = strncmp(tname, TEST_THREAD_NAME_BASE, strlen(TEST_THREAD_NAME_BASE)) == 0;
46+
deallocate(jvmti, jni, tname);
47+
48+
return result;
49+
}
50+
51+
static
52+
void printInfo(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr, jmethodID method, int depth) {
53+
jclass cls;
54+
char *mname, *msig, *csig;
55+
char* tname = get_thread_name(jvmti, jni, thr);
56+
57+
check_jvmti_status(jni, jvmti->GetMethodDeclaringClass(method, &cls), "Error in GetMethodDeclaringClass.");
58+
check_jvmti_status(jni, jvmti->GetClassSignature(cls, &csig, nullptr), "Error in GetClassSignature.");
59+
check_jvmti_status(jni, jvmti->GetMethodName(method, &mname, &msig, nullptr), "Error in GetMethodName.");
60+
61+
LOG(" %s: %s.%s%s, depth = %d\n", tname, csig, mname, msig, depth);
62+
63+
deallocate(jvmti, jni, tname);
64+
deallocate(jvmti, jni, mname);
65+
deallocate(jvmti, jni, msig);
66+
deallocate(jvmti, jni, csig);
67+
}
68+
69+
void JNICALL MethodEntry(jvmtiEnv *jvmti, JNIEnv *jni,
70+
jthread thr, jmethodID method) {
71+
RawMonitorLocker rml(jvmti, jni, event_lock);
72+
73+
if (watch_events == JNI_FALSE) {
74+
return;
75+
}
76+
if (!isTestThread(jni, jvmti, thr)) {
77+
return; // not a tested thread
78+
}
79+
jclass cls;
80+
char *csig;
81+
82+
check_jvmti_status(jni, jvmti->GetMethodDeclaringClass(method, &cls), "Error in GetMethodDeclaringClass.");
83+
check_jvmti_status(jni, jvmti->GetClassSignature(cls, &csig, nullptr), "Error in GetClassSignature.");
84+
85+
if (strcmp(csig, TEST_CLASS_SIG) != 0 ||
86+
strcmp(get_method_name(jvmti, jni, method), "run") != 0) {
87+
return; // not a tested method
88+
}
89+
LOG("\n>>>Method entry event:");
90+
printInfo(jni, jvmti, thr, method, get_frame_count(jvmti, jni, thr));
91+
92+
check_jvmti_status(jni, jvmti->NotifyFramePop(thr, 0), "Error in NotifyFramePop.");
93+
deallocate(jvmti, jni, csig);
94+
}
95+
96+
void JNICALL FramePop(jvmtiEnv *jvmti, JNIEnv *jni,
97+
jthread thr, jmethodID method, jboolean wasPopedByException) {
98+
RawMonitorLocker rml(jvmti, jni, event_lock);
99+
100+
jint frameCount = get_frame_count(jvmti, jni, thr);
101+
102+
LOG("\n>>> Frame Pop event:");
103+
printInfo(jni, jvmti, thr, method, frameCount);
104+
pop_count++;
105+
}
106+
107+
JNIEXPORT jint JNICALL
108+
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
109+
jvmtiCapabilities caps;
110+
jvmtiError err;
111+
112+
jint res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
113+
if (res != JNI_OK || jvmti == nullptr) {
114+
LOG("Failed: Wrong result of a valid call to GetEnv!\n");
115+
return JNI_ERR;
116+
}
117+
event_lock = create_raw_monitor(jvmti, "_event_lock");
118+
119+
memset(&caps, 0, sizeof(jvmtiCapabilities));
120+
caps.can_generate_frame_pop_events = 1;
121+
caps.can_generate_method_entry_events = 1;
122+
caps.can_support_virtual_threads = 1;
123+
124+
callbacks.MethodEntry = &MethodEntry;
125+
callbacks.FramePop = &FramePop;
126+
127+
err = jvmti->AddCapabilities(&caps);
128+
if (err != JVMTI_ERROR_NONE) {
129+
LOG("(AddCapabilities) unexpected error: %s (%d)\n", TranslateError(err), err);
130+
return JNI_ERR;
131+
}
132+
err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
133+
if (err != JVMTI_ERROR_NONE) {
134+
LOG("(SetEventCallbacks) unexpected error: %s (%d)\n", TranslateError(err), err);
135+
return JNI_ERR;
136+
}
137+
err = set_event_notification_mode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr);
138+
if (err != JVMTI_ERROR_NONE) {
139+
return JNI_ERR;
140+
}
141+
return JNI_OK;
142+
}
143+
144+
JNIEXPORT void JNICALL Java_ClearAllFramePops_clearAllFramePops(JNIEnv *jni, jclass cls) {
145+
RawMonitorLocker rml(jvmti, jni, event_lock);
146+
147+
char* tname = get_thread_name(jvmti, jni, nullptr);
148+
149+
check_jvmti_status(jni, jvmti->ClearAllFramePops(nullptr), "Error in ClearAllFramePops");
150+
LOG("Called ClearAllFramePops for thread: %s\n", tname);
151+
152+
deallocate(jvmti, jni, tname);
153+
}
154+
155+
JNIEXPORT void JNICALL Java_ClearAllFramePops_getReady(JNIEnv *jni, jclass cls) {
156+
RawMonitorLocker rml(jvmti, jni, event_lock);
157+
158+
watch_events = JNI_TRUE;
159+
set_event_notification_mode(jvmti, jni, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, nullptr);
160+
set_event_notification_mode(jvmti, jni, JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, nullptr);
161+
}
162+
163+
JNIEXPORT void JNICALL Java_ClearAllFramePops_check(JNIEnv *jni, jclass cls) {
164+
RawMonitorLocker rml(jvmti, jni, event_lock);
165+
166+
watch_events = JNI_FALSE;
167+
set_event_notification_mode(jvmti, jni, JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, nullptr);
168+
set_event_notification_mode(jvmti, jni, JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, nullptr);
169+
LOG("\n>>> Total frame pops: %d\n", pop_count);
170+
171+
if (pop_count > 0) {
172+
fatal(jni, "Failed: FramePop events are not expected");
173+
}
174+
}
175+
176+
}

0 commit comments

Comments
 (0)
Please sign in to comment.