提交 7796cbc3 编写于 作者: Y yunyao.zxl 提交者: zhengxiaolinX

[Coroutine] Bug fixes for JKU coroutine

Summary: Bug fixes for JKU coroutine
The JKU patch has several flaws:
1. When using JDI to debug coroutine, it may crash because the _switchTo intrinsic entry will change to the interpreter one, which is a SHOULD_NOT_REACH_HERE().
2. Change miscellaneous thread-local data structures to coroutine-local level during _switchTo phase.
3. Disable making zombies of _switchTo alike intrinsic things.
4. Fix multiple mem leak problems for coroutine data structures.
5. Create an empty JNIHandleBlock for the first Java call.
6. The stack layout of Coroutine doesn't align with 16 bytes on x86_64 which will cause some float point instructions to throw an sig fault.
7. etc.

Test Plan: Coroutine tests

Reviewed-by: yuleil, shiyuexw, sanhong

Issue: https://github.com/alibaba/dragonwell8/issues/113
上级 75352d45
......@@ -444,15 +444,27 @@ void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register
if (JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
Label coroutine_skip_interpret;
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
// compiled code in threads for which the event is enabled. Check here for
// interp_only_mode if these events CAN be enabled.
// interp_only is an int, on little endian it is sufficient to test the byte only
// Is a cmpl faster?
cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
jccb(Assembler::zero, run_compiled_code);
jcc(Assembler::zero, run_compiled_code);
if (EnableCoroutine) {
cmpw(Address(method, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_switchTo);
jcc(Assembler::zero, coroutine_skip_interpret);
cmpw(Address(method, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_switchToAndExit);
jcc(Assembler::zero, coroutine_skip_interpret);
cmpw(Address(method, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_switchToAndTerminate);
jcc(Assembler::zero, coroutine_skip_interpret);
}
jmp(Address(method, Method::interpreter_entry_offset()));
bind(run_compiled_code);
if (EnableCoroutine) {
bind(coroutine_skip_interpret);
}
}
jmp(Address(method, Method::from_interpreted_offset()));
......
......@@ -3636,6 +3636,10 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
__ movptr(Address(old_coroutine, Coroutine::resource_area_offset()), temp);
__ movptr(temp, Address(thread, Thread::last_handle_mark_offset()));
__ movptr(Address(old_coroutine, Coroutine::last_handle_mark_offset()), temp);
__ movptr(temp, Address(thread, Thread::active_handles_offset()));
__ movptr(Address(old_coroutine, Coroutine::active_handles_offset()), temp);
__ movptr(temp, Address(thread, Thread::metadata_handles_offset()));
__ movptr(Address(old_coroutine, Coroutine::metadata_handles_offset()), temp);
// push the current IP and frame pointer onto the stack
__ push(rbp);
......@@ -3658,7 +3662,8 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
Register temp2 = rdi;
{
Register thread = rax;
__ get_thread(rax);
__ get_thread(thread);
__ movptr(Address(thread, JavaThread::current_coroutine_offset()), target_coroutine);
// set new handle and resource areas
__ movptr(temp, Address(target_coroutine, Coroutine::handle_area_offset()));
__ movptr(Address(target_coroutine, Coroutine::handle_area_offset()), (intptr_t)NULL_WORD); // TODO is this really needed?
......@@ -3669,6 +3674,12 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
__ movptr(temp, Address(target_coroutine, Coroutine::last_handle_mark_offset()));
__ movptr(Address(target_coroutine, Coroutine::last_handle_mark_offset()), (intptr_t)NULL_WORD); // TODO is this really needed?
__ movptr(Address(thread, Thread::last_handle_mark_offset()), temp);
__ movptr(temp, Address(target_coroutine, Coroutine::active_handles_offset()));
__ movptr(Address(target_coroutine, Coroutine::active_handles_offset()), (intptr_t)NULL_WORD);
__ movptr(Address(thread, Thread::active_handles_offset()), temp);
__ movptr(temp, Address(target_coroutine, Coroutine::metadata_handles_offset()));
__ movptr(Address(target_coroutine, Coroutine::metadata_handles_offset()), (intptr_t)NULL_WORD);
__ movptr(Address(thread, Thread::metadata_handles_offset()), temp);
// update the thread's stack base and size
__ movptr(temp, Address(target_stack, CoroutineStack::stack_base_offset()));
......
......@@ -4470,6 +4470,10 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
__ movptr(Address(old_coroutine, Coroutine::resource_area_offset()), temp);
__ movptr(temp, Address(thread, Thread::last_handle_mark_offset()));
__ movptr(Address(old_coroutine, Coroutine::last_handle_mark_offset()), temp);
__ movptr(temp, Address(thread, Thread::active_handles_offset()));
__ movptr(Address(old_coroutine, Coroutine::active_handles_offset()), temp);
__ movptr(temp, Address(thread, Thread::metadata_handles_offset()));
__ movptr(Address(old_coroutine, Coroutine::metadata_handles_offset()), temp);
#ifdef ASSERT
__ movl(temp, Address(thread, JavaThread::java_call_counter_offset()));
__ movl(Address(old_coroutine, Coroutine::java_call_counter_offset()), temp);
......@@ -4492,6 +4496,7 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
Register temp2 = r9;
{
Register thread = r15;
__ movptr(Address(thread, JavaThread::current_coroutine_offset()), target_coroutine);
// set new handle and resource areas
__ movptr(temp, Address(target_coroutine, Coroutine::handle_area_offset()));
__ movptr(Address(thread, Thread::handle_area_offset()), temp);
......@@ -4499,6 +4504,10 @@ void create_switchTo_contents(MacroAssembler *masm, int start, OopMapSet* oop_ma
__ movptr(Address(thread, Thread::resource_area_offset()), temp);
__ movptr(temp, Address(target_coroutine, Coroutine::last_handle_mark_offset()));
__ movptr(Address(thread, Thread::last_handle_mark_offset()), temp);
__ movptr(temp, Address(target_coroutine, Coroutine::active_handles_offset()));
__ movptr(Address(thread, Thread::active_handles_offset()), temp);
__ movptr(temp, Address(target_coroutine, Coroutine::metadata_handles_offset()));
__ movptr(Address(thread, Thread::metadata_handles_offset()), temp);
#ifdef ASSERT
__ movl(temp, Address(target_coroutine, Coroutine::java_call_counter_offset()));
......
......@@ -1438,6 +1438,19 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
assert(!is_zombie(), "should not already be a zombie");
if (EnableCoroutine && method() != NULL) {
// do not deal with intrinsic methods of coroutine klass
assert(!is_unloaded(), "wrong state");
vmIntrinsics::ID intrinsic_id = method()->intrinsic_id();
if (intrinsic_id == vmIntrinsics::_switchTo ||
intrinsic_id == vmIntrinsics::_switchToAndExit ||
intrinsic_id == vmIntrinsics::_switchToAndTerminate) {
assert(method()->constants()->pool_holder() == SystemDictionary::java_dyn_CoroutineSupport_klass(), "wrong method!");
assert(state == not_entrant, "wrong state");
return false;
}
}
// Make sure neither the nmethod nor the method is flushed in case of a safepoint in code below.
nmethodLocker nml(this);
methodHandle the_method(method());
......
......@@ -288,6 +288,9 @@ int JvmtiThreadState::cur_stack_depth() {
if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) {
_cur_stack_depth = count_frames();
} else {
if (EnableCoroutine) {
_cur_stack_depth = count_frames(); // update debug stack depth, for switchToAndExit
}
// heavy weight assert
assert(_cur_stack_depth == count_frames(),
"cur_stack_depth out of sync");
......
......@@ -1394,7 +1394,7 @@ void CoroutineSupport_switchToAndTerminate(JNIEnv* env, jclass klass, jobject ol
} else {
CoroutineStack::free_stack(stack, THREAD);
}
Coroutine::free_coroutine(coro, THREAD);
delete coro;
}
void CoroutineSupport_switchToAndExit(JNIEnv* env, jclass klass, jobject old_coroutine, jobject target_coroutine) {
......@@ -1450,7 +1450,14 @@ jboolean CoroutineSupport_isDisposable(JNIEnv* env, jclass klass, jlong coroutin
assert(coro != NULL, "cannot free NULL coroutine");
assert(!coro->is_thread_coroutine(), "cannot free thread coroutine");
return coro->is_disposable();
jboolean is_disposable = coro->is_disposable();
if (is_disposable) {
CoroutineStack* stack = coro->stack();
stack->remove_from_list(THREAD->coroutine_stack_list());
CoroutineStack::free_stack(stack, THREAD);
delete coro;
}
return is_disposable;
}
jobject CoroutineSupport_cleanupCoroutine(JNIEnv* env, jclass klass) {
......
......@@ -77,6 +77,7 @@ void Coroutine::run(jobject coroutine) {
_thread->set_resource_area(new (mtThread) ResourceArea(32));
_thread->set_handle_area(new (mtThread) HandleArea(NULL, 32));
_thread->set_metadata_handles(new (ResourceObj::C_HEAP, mtClass) GrowableArray<Metadata*>(30, true));
{
HandleMark hm(_thread);
......@@ -105,6 +106,8 @@ Coroutine* Coroutine::create_thread_coroutine(JavaThread* thread, CoroutineStack
coro->_resource_area = NULL;
coro->_handle_area = NULL;
coro->_last_handle_mark = NULL;
coro->_active_handles = thread->active_handles();
coro->_metadata_handles = NULL;
#ifdef ASSERT
coro->_java_call_counter = 0;
#endif
......@@ -114,6 +117,12 @@ Coroutine* Coroutine::create_thread_coroutine(JavaThread* thread, CoroutineStack
return coro;
}
/**
* The initial value for corountine' active handles, which will be replaced with a real one
* when the coroutine invokes call_virtual (before running any real logic).
*/
static JNIHandleBlock* shared_empty_JNIHandleBlock = JNIHandleBlock::allocate_block();
Coroutine* Coroutine::create_coroutine(JavaThread* thread, CoroutineStack* stack, oop coroutineObj) {
Coroutine* coro = new Coroutine();
if (coro == NULL) {
......@@ -121,6 +130,8 @@ Coroutine* Coroutine::create_coroutine(JavaThread* thread, CoroutineStack* stack
}
intptr_t** d = (intptr_t**)stack->stack_base();
// Make the 16 bytes(original is 8*5=40 bytes) alignation which is required by some instructions like movaps otherwise we will incur a crash.
*(--d) = NULL;
*(--d) = NULL;
jobject obj = JNIHandles::make_global(coroutineObj);
*(--d) = (intptr_t*)obj;
......@@ -131,13 +142,15 @@ Coroutine* Coroutine::create_coroutine(JavaThread* thread, CoroutineStack* stack
stack->set_last_sp((address) d);
coro->_state = _onstack;
coro->_state = _created;
coro->_is_thread_coroutine = false;
coro->_thread = thread;
coro->_stack = stack;
coro->_resource_area = NULL;
coro->_handle_area = NULL;
coro->_last_handle_mark = NULL;
coro->_active_handles = shared_empty_JNIHandleBlock;
coro->_metadata_handles = NULL;
#ifdef ASSERT
coro->_java_call_counter = 0;
#endif
......@@ -147,13 +160,25 @@ Coroutine* Coroutine::create_coroutine(JavaThread* thread, CoroutineStack* stack
return coro;
}
void Coroutine::free_coroutine(Coroutine* coroutine, JavaThread* thread) {
coroutine->remove_from_list(thread->coroutine_list());
delete coroutine;
Coroutine::~Coroutine() {
remove_from_list(_thread->coroutine_list());
if (!_is_thread_coroutine && _state != Coroutine::_created) {
assert(_resource_area != NULL, "_resource_area is NULL");
assert(_handle_area != NULL, "_handle_area is NULL");
assert(_metadata_handles != NULL, "_metadata_handles is NULL");
delete _resource_area;
delete _handle_area;
delete _metadata_handles;
JNIHandleBlock::release_block(active_handles(), _thread);
//allocated from JavaCalls::call_virtual during coroutine's first running
}
}
void Coroutine::frames_do(FrameClosure* fc) {
switch (_state) {
case Coroutine::_created:
// the coroutine has never been run
break;
case Coroutine::_current:
// the contents of this coroutine have already been visited
break;
......@@ -182,6 +207,7 @@ void Coroutine::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) {
if (_state == _onstack &&_handle_area != NULL) {
DEBUG_CORO_ONLY(tty->print_cr("collecting handle area %08x", _handle_area));
_handle_area->oops_do(f);
_active_handles->oops_do(f);
}
}
......@@ -207,6 +233,11 @@ public:
};
void Coroutine::metadata_do(void f(Metadata*)) {
if (metadata_handles() != NULL) {
for (int i = 0; i< metadata_handles()->length(); i++) {
f(metadata_handles()->at(i));
}
}
metadata_do_Closure fc(f);
frames_do(&fc);
}
......@@ -225,7 +256,9 @@ void Coroutine::frames_do(void f(frame*, const RegisterMap* map)) {
}
bool Coroutine::is_disposable() {
return false;
//_handle_area == NULL indicates this coroutine has not been initialized,
//we should delete it directly.
return _handle_area == NULL;
}
......@@ -290,7 +323,10 @@ CoroutineStack* CoroutineStack::create_stack(JavaThread* thread, intptr_t size/*
}
void CoroutineStack::free_stack(CoroutineStack* stack, JavaThread* thread) {
guarantee(!stack->is_thread_stack(), "cannot free thread stack");
if (stack->is_thread_stack()) {
delete stack;
return;
}
ThreadLocalStorage::remove_coroutine_stack(thread, stack->stack_base(), stack->stack_size());
if (stack->_reserved_space.size() > 0) {
......
......@@ -79,6 +79,7 @@ public:
class Coroutine: public CHeapObj<mtThread>, public DoublyLinkedList<Coroutine> {
public:
enum CoroutineState {
_created = 0x00000000, // not inited
_onstack = 0x00000001,
_current = 0x00000002,
_dead = 0x00000003, // TODO is this really needed?
......@@ -96,6 +97,8 @@ private:
ResourceArea* _resource_area;
HandleArea* _handle_area;
HandleMark* _last_handle_mark;
JNIHandleBlock* _active_handles;
GrowableArray<Metadata*>* _metadata_handles;
#ifdef ASSERT
int _java_call_counter;
#endif
......@@ -106,16 +109,16 @@ private:
// objects of this type can only be created via static functions
Coroutine() { }
virtual ~Coroutine() { }
void frames_do(FrameClosure* fc);
public:
virtual ~Coroutine();
void run(jobject coroutine);
static Coroutine* create_thread_coroutine(JavaThread* thread, CoroutineStack* stack);
static Coroutine* create_coroutine(JavaThread* thread, CoroutineStack* stack, oop coroutineObj);
static void free_coroutine(Coroutine* coroutine, JavaThread* thread);
CoroutineState state() const { return _state; }
void set_state(CoroutineState x) { _state = x; }
......@@ -136,6 +139,12 @@ public:
HandleMark* last_handle_mark() const { return _last_handle_mark; }
void set_last_handle_mark(HandleMark* x){ _last_handle_mark = x; }
JNIHandleBlock* active_handles() const { return _active_handles; }
void set_active_handles(JNIHandleBlock* block) { _active_handles = block; }
GrowableArray<Metadata*>* metadata_handles() const { return _metadata_handles; }
void set_metadata_handles(GrowableArray<Metadata*>* handles){ _metadata_handles = handles; }
#ifdef ASSERT
int java_call_counter() const { return _java_call_counter; }
void set_java_call_counter(int x) { _java_call_counter = x; }
......@@ -155,6 +164,8 @@ public:
static ByteSize resource_area_offset() { return byte_offset_of(Coroutine, _resource_area); }
static ByteSize handle_area_offset() { return byte_offset_of(Coroutine, _handle_area); }
static ByteSize last_handle_mark_offset() { return byte_offset_of(Coroutine, _last_handle_mark); }
static ByteSize active_handles_offset() { return byte_offset_of(Coroutine, _active_handles); }
static ByteSize metadata_handles_offset() { return byte_offset_of(Coroutine, _metadata_handles); }
#ifdef ASSERT
static ByteSize java_call_counter_offset() { return byte_offset_of(Coroutine, _java_call_counter); }
#endif
......
......@@ -233,6 +233,7 @@ class HandleArea: public Arena {
_prev = prev;
}
// Only coroutine uses this constructor
HandleArea(HandleArea* prev, size_t init_size) : Arena(mtThread, init_size) {
assert(EnableCoroutine, "EnableCoroutine is off");
debug_only(_handle_mark_nesting = 0);
......
......@@ -1500,6 +1500,7 @@ void JavaThread::initialize() {
_coroutine_stack_cache_size = 0;
_coroutine_stack_list = NULL;
_coroutine_list = NULL;
_current_coroutine = NULL;
_thread_stat = NULL;
_thread_stat = new ThreadStatistics();
......@@ -1643,6 +1644,11 @@ JavaThread::~JavaThread() {
CoroutineStack::free_stack(stack, this);
}
while (EnableCoroutine && coroutine_list() != NULL) {
CoroutineStack::free_stack(coroutine_list()->stack(), this);
delete coroutine_list();
}
if (TraceThreadEvents) {
tty->print_cr("terminate thread %p", this);
}
......
......@@ -639,6 +639,7 @@ protected:
static ByteSize exception_file_offset() { return byte_offset_of(Thread, _exception_file ); }
static ByteSize exception_line_offset() { return byte_offset_of(Thread, _exception_line ); }
static ByteSize active_handles_offset() { return byte_offset_of(Thread, _active_handles ); }
static ByteSize metadata_handles_offset() { return byte_offset_of(Thread, _metadata_handles); }
static ByteSize stack_base_offset() { return byte_offset_of(Thread, _stack_base ); }
static ByteSize stack_size_offset() { return byte_offset_of(Thread, _stack_size ); }
......@@ -980,6 +981,7 @@ class JavaThread: public Thread {
uintx _coroutine_stack_cache_size;
CoroutineStack* _coroutine_stack_list;
Coroutine* _coroutine_list;
Coroutine* _current_coroutine;
intptr_t _coroutine_temp;
......@@ -988,9 +990,12 @@ class JavaThread: public Thread {
uintx& coroutine_stack_cache_size() { return _coroutine_stack_cache_size; }
CoroutineStack*& coroutine_stack_list() { return _coroutine_stack_list; }
Coroutine*& coroutine_list() { return _coroutine_list; }
Coroutine* current_coroutine() { return _current_coroutine; }
static ByteSize coroutine_temp_offset() { return byte_offset_of(JavaThread, _coroutine_temp); }
static ByteSize current_coroutine_offset() { return byte_offset_of(JavaThread, _current_coroutine); }
void initialize_coroutine_support();
private:
......
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* @test Issue11230146
* @library /testlibrary /testlibrary/whitebox
* @build Issue11230146
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:-Inline -XX:+EnableCoroutine -Xmx10m -Xms10m -XX:ReservedCodeCacheSize=3m -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI Issue11230146
* @summary Issue11230146, JTreg test for D181275
*/
import sun.hotspot.WhiteBox;
import java.dyn.Coroutine;
import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
public class Issue11230146 {
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100; i++) {
// trigger method state translate:
// in_use -> not_entrant -> zombie -> unload -> zombie
// ^
// |
// +-- crash happens here (described in issue11230146)
doTest();
}
}
public static void doTest() throws Exception {
WhiteBox whiteBox = WhiteBox.getWhiteBox();
MyClassloader loader = new MyClassloader();
try {
Class c = loader.loadClass("ClassTmp");
Method fooMethod = c.getMethod("foo");
Object fooObject = c.newInstance();
for (int i = 0; i < 10000; i++) {
fooMethod.invoke(fooObject);
}
} catch (Exception e) {
e.printStackTrace();
}
System.gc();
// deoptimize methods
whiteBox.deoptimizeAll();
}
/*
* class file contet:
*
* public class ClassTmp {
* public String a;
*
* public void foo() {
* if (a != null) {
* System.out.println("foo...");
* }
* }
* }
*
*/
public static String classContent =
"yv66vgAAADQAIQoABwASCQAGABMJABQAFQgAFgoAFwAYBwAZBwAaAQABYQEAEkxqYXZhL2xhbmcv" +
"U3RyaW5nOwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAA1T" +
"dGFja01hcFRhYmxlAQAKU291cmNlRmlsZQEADUNsYXNzVG1wLmphdmEMAAoACwwACAAJBwAbDAAc" +
"AB0BAAZmb28uLi4HAB4MAB8AIAEACENsYXNzVG1wAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEv" +
"bGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50" +
"U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAYABwAAAAEAAQAIAAkA" +
"AAACAAEACgALAAEADAAAAB0AAQABAAAABSq3AAGxAAAAAQANAAAABgABAAAAAQABAA4ACwABAAwA" +
"AAA5AAIAAQAAABAqtAACxgALsgADEgS2AAWxAAAAAgANAAAADgADAAAABQAHAAYADwAIAA8AAAAD" +
"AAEPAAEAEAAAAAIAEQ==";
/**
* decode Base64
*/
public static byte[] decodeBase64(String str) throws Exception {
byte[] bt = null;
sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
bt = decoder.decodeBuffer(str);
return bt;
}
public static class MyClassloader extends ClassLoader {
public Class loadClass(String name) throws ClassNotFoundException {
if ("ClassTmp".equals(name)) {
try {
byte[] bs = decodeBase64(classContent);
return defineClass(name, bs, 0, bs.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
return super.loadClass(name);
}
}
}
}
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import com.alibaba.wisp.engine.WispEngine;
public class SimpleWispTest {
public static void main(String[] args) {
WispEngine.dispatch(() -> {
});
}
}
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* @test TestAvoidDeoptCoroutineMethod
* @library /testlibrary /testlibrary/whitebox
* @build TestAvoidDeoptCoroutineMethod
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+EnableCoroutine -Xmx10m -Xms10m -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestAvoidDeoptCoroutineMethod
* @summary test avoid coroutine intrinsic method to be deoptimized
*/
import sun.hotspot.WhiteBox;
import java.dyn.Coroutine;
import java.io.*;
public class TestAvoidDeoptCoroutineMethod {
public static void main(String[] args) throws Exception {
WhiteBox whiteBox = WhiteBox.getWhiteBox();
runSomeCoroutines();
// deoptimize all
whiteBox.deoptimizeAll();
// if intrinsic methods of coroutine have been deoptimized, it will crash here
runSomeCoroutines();
}
private static void runSomeCoroutines() throws Exception {
for (int i = 0; i < 10000; i++) {
new Coroutine(() -> {});
Coroutine.yield(); // switch to new created coroutine and let it die
}
System.out.println("end of run");
}
}
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* @test
* @summary test of memory leak while creating and destroying coroutine/thread
* @run main/othervm -XX:+EnableCoroutine -Xmx10m -Xms10m TestMemLeak
*/
import java.dyn.Coroutine;
import java.io.*;
public class TestMemLeak {
private final static Runnable r = () -> {};
public static void main(String[] args) throws Exception {
testThreadCoroutineLeak();
testUserCreatedCoroutineLeak();
}
/**
* Before fix: 35128kB -> 40124kB
* After fix : 28368kB -> 28424kB
*/
private static void testThreadCoroutineLeak() throws Exception {
// occupy rss
for (int i = 0; i < 20000; i++) {
Thread t = new Thread(r);
t.start();
t.join();
}
int rss0 = getRssInKb();
System.out.println(rss0);
for (int i = 0; i < 20000; i++) {
Thread t = new Thread(r);
t.start();
t.join();
}
int rss1 = getRssInKb();
System.out.println(rss1);
if (rss1 - rss0 > 1024) { // 1M
throw new Error("thread coroutine mem leak");
}
}
/**
* Before fix: 152892kB -> 280904kB
* After fix : 25436kB -> 25572kB
*/
private static void testUserCreatedCoroutineLeak() throws Exception {
// occupy rss
for (int i = 0; i < 200000; i++) {
new Coroutine(r);
Coroutine.yield(); // switch to new created coroutine and let it die
}
int rss0 = getRssInKb();
System.out.println(rss0);
for (int i = 0; i < 200000; i++) {
new Coroutine(r);
Coroutine.yield();
}
int rss1 = getRssInKb();
System.out.println(rss1);
if (rss1 - rss0 > 1024) { // 1M
throw new Error("user created coroutine mem leak");
}
}
private static int getRssInKb() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("/proc/self/status"))) {
int rss = -1;
String line;
while ((line = br.readLine()) != null) {
//i.e. VmRSS: 360 kB
if (line.trim().startsWith("VmRSS:")) {
int numEnd = line.length() - 3;
int numBegin = line.lastIndexOf(" ", numEnd - 1) + 1;
rss = Integer.parseInt(line.substring(numBegin, numEnd));
break;
}
}
return rss;
}
}
}
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jvmti.h>
void Jvmti_Error(int errcode, const char *msg) {
if (errcode != JVMTI_ERROR_NONE) {
printf("%s, error code: [%d]\n", errcode, msg);
exit(1);
}
}
#define JVMTI_CHECK(x, str) (Jvmti_Error(x, str))
void JNICALL
SingleStepCallBack(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location)
{
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
// 1. get the jvmti env
jvmtiEnv *jvmti = NULL;
JVMTI_CHECK((*jvm)->GetEnv(jvm, (void **)&jvmti, JVMTI_VERSION_1), "env get error");
// 2. grant the ability to the jvmti: can single step
jvmtiCapabilities noryoku;
memset((void *)&noryoku, 0, sizeof(jvmtiCapabilities));
noryoku.can_generate_single_step_events = 1;
JVMTI_CHECK((*jvmti)->AddCapabilities(jvmti, &noryoku), "add capability error");
// 3. set: every single step, we can get a notification.
JVMTI_CHECK((*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, NULL), "set notification failed");
// 4. when a notification happens, we will get a callback! This callback will booooom.
jvmtiEventCallbacks callbacks;
memset((void *)&callbacks, 0, sizeof(callbacks));
callbacks.SingleStep = &SingleStepCallBack;
JVMTI_CHECK((*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)), "set callback failed");
return 0;
}
#!/bin/sh
#
# Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Alibaba designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# @test
# @library /testlibrary
# @compile SimpleWispTest.java
#
# @summary test Coroutine SwitchTo() crash problem
# @run shell coroutineBreakpointSwitchToTest.sh
#
OS=`uname -s`
case "$OS" in
AIX | Darwin | Linux | SunOS )
FS="/"
;;
Windows_* )
FS="\\"
;;
CYGWIN_* )
FS="/"
;;
* )
echo "Unrecognized system!"
exit 1;
;;
esac
JAVA=${TESTJAVA}${FS}bin${FS}java
export LD_LIBRARY_PATH=.:${TESTJAVA}${FS}jre${FS}lib${FS}amd64${FS}server:${FS}usr${FS}lib:${LD_LIBRARY_PATH}
gcc_cmd=`which gcc`
if [ "x${gcc_cmd}" = "x" ]; then
echo "WARNING: gcc not found. Cannot execute test." 2>&1
exit 0
fi
gcc -DLINUX -fPIC -shared -o libtest.so \
-I${COMPILEJAVA}/include -I${COMPILEJAVA}/include/linux \
${TESTSRC}/coroutineBreakpointSwitchToTest.c
${JAVA} -agentpath:libtest.so -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.transparentAsync=true -cp ${TESTCLASSES} SimpleWispTest
exit $?
#!/bin/sh
#
# Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Alibaba designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# @test
# @library /testlibrary
# @compile SimpleWispTest.java
#
# @summary test coroutine and -XX:+LogCompilation could work together
# @run shell logCompilationTest.sh
#
OS=`uname -s`
case "$OS" in
AIX | Darwin | Linux | SunOS )
FS="/"
;;
Windows_* )
FS="\\"
;;
CYGWIN_* )
FS="/"
;;
* )
echo "Unrecognized system!"
exit 1;
;;
esac
JAVA=${TESTJAVA}${FS}bin${FS}java
${JAVA} -XX:+UnlockDiagnosticVMOptions -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.transparentAsync=true -XX:+LogCompilation -Xcomp -cp ${TESTCLASSES} SimpleWispTest
exit $?
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册