1 /*
2  * Copyright (c) 2020, 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 #include "precompiled.hpp"
26 #include "jni.h"
27 #include "jvm.h"
28 #include "classfile/vmSymbols.hpp"
29 #include "oops/access.inline.hpp"
30 #include "oops/oop.inline.hpp"
31 #include "runtime/jniHandles.inline.hpp"
32 #include "runtime/interfaceSupport.inline.hpp"
33 #include "runtime/sharedRuntime.hpp"
34 #include "runtime/vframe.inline.hpp"
35 #include "runtime/deoptimization.hpp"
36 #include "prims/stackwalk.hpp"
37 
38 
39 class CloseScopedMemoryFindOopClosure : public OopClosure {
40   oop _deopt;
41   bool _found;
42 
43 public:
CloseScopedMemoryFindOopClosure(jobject deopt)44   CloseScopedMemoryFindOopClosure(jobject deopt) :
45       _deopt(JNIHandles::resolve(deopt)),
46       _found(false) {}
47 
48   template <typename T>
do_oop_work(T * p)49   void do_oop_work(T* p) {
50     if (_found) {
51       return;
52     }
53     if (RawAccess<>::oop_load(p) == _deopt) {
54       _found = true;
55     }
56   }
57 
do_oop(oop * p)58   virtual void do_oop(oop* p) {
59     do_oop_work(p);
60   }
61 
do_oop(narrowOop * p)62   virtual void do_oop(narrowOop* p) {
63     do_oop_work(p);
64   }
65 
found()66   bool found() {
67     return _found;
68   }
69 };
70 
71 class CloseScopedMemoryClosure : public HandshakeClosure {
72   jobject _deopt;
73   jobject _exception;
74 
75 public:
76   jboolean _found;
77 
CloseScopedMemoryClosure(jobject deopt,jobject exception)78   CloseScopedMemoryClosure(jobject deopt, jobject exception)
79     : HandshakeClosure("CloseScopedMemory")
80     , _deopt(deopt)
81     , _exception(exception)
82     , _found(false) {}
83 
do_thread(Thread * thread)84   void do_thread(Thread* thread) {
85 
86     JavaThread* jt = (JavaThread*)thread;
87 
88     if (!jt->has_last_Java_frame()) {
89       return;
90     }
91 
92     frame last_frame = jt->last_frame();
93     RegisterMap register_map(jt, true);
94 
95     if (last_frame.is_safepoint_blob_frame()) {
96       last_frame = last_frame.sender(&register_map);
97     }
98 
99     ResourceMark rm;
100     if (_deopt != NULL && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) {
101       CloseScopedMemoryFindOopClosure cl(_deopt);
102       CompiledMethod* cm = last_frame.cb()->as_compiled_method();
103 
104       /* FIXME: this doesn't work if reachability fences are violated by C2
105       last_frame.oops_do(&cl, NULL, &register_map);
106       if (cl.found()) {
107            //Found the deopt oop in a compiled method; deoptimize.
108            Deoptimization::deoptimize(jt, last_frame);
109       }
110       so... we unconditionally deoptimize, for now: */
111       Deoptimization::deoptimize(jt, last_frame);
112     }
113 
114     const int max_critical_stack_depth = 10;
115     int depth = 0;
116     for (vframeStream stream(jt); !stream.at_end(); stream.next()) {
117       Method* m = stream.method();
118       if (m->is_scoped()) {
119         StackValueCollection* locals = stream.asJavaVFrame()->locals();
120         for (int i = 0; i < locals->size(); i++) {
121           StackValue* var = locals->at(i);
122           if (var->type() == T_OBJECT) {
123             if (var->get_obj() == JNIHandles::resolve(_deopt)) {
124               assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth);
125               _found = true;
126               return;
127             }
128           }
129         }
130         break;
131       }
132       depth++;
133 #ifndef ASSERT
134       if (depth >= max_critical_stack_depth) {
135         break;
136       }
137 #endif
138     }
139   }
140 };
141 
142 /*
143  * This function issues a global handshake operation with all
144  * Java threads. This is useful for implementing asymmetric
145  * dekker synchronization schemes, where expensive synchronization
146  * in performance sensitive common paths, may be shifted to
147  * a less common slow path instead.
148  * Top frames containg obj will be deoptimized.
149  */
150 JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception))
151   CloseScopedMemoryClosure cl(deopt, exception);
152   Handshake::execute(&cl);
153   return !cl._found;
154 JVM_END
155 
156 /// JVM_RegisterUnsafeMethods
157 
158 #define PKG "Ljdk/internal/misc/"
159 
160 #define MEMACCESS "ScopedMemoryAccess"
161 #define SCOPE PKG MEMACCESS "$Scope;"
162 #define SCOPED_ERR PKG MEMACCESS "$Scope$ScopedAccessError;"
163 
164 #define CC (char*)  /*cast a literal from (const char*)*/
165 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
166 
167 static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = {
168     {CC "closeScope0",   CC "(" SCOPE SCOPED_ERR ")Z",           FN_PTR(ScopedMemoryAccess_closeScope)},
169 };
170 
171 #undef CC
172 #undef FN_PTR
173 
174 #undef PKG
175 #undef MEMACCESS
176 #undef SCOPE
177 #undef SCOPED_EXC
178 
179 // This function is exported, used by NativeLookup.
180 
181 JVM_ENTRY(void, JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass))
182   ThreadToNativeFromVM ttnfv(thread);
183 
184   int ok = env->RegisterNatives(scopedMemoryAccessClass, jdk_internal_misc_ScopedMemoryAccess_methods, sizeof(jdk_internal_misc_ScopedMemoryAccess_methods)/sizeof(JNINativeMethod));
185   guarantee(ok == 0, "register jdk.internal.misc.ScopedMemoryAccess natives");
186 JVM_END
187