• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/android/jni_android.h"
6 
7 #include <stddef.h>
8 #include <sys/prctl.h>
9 
10 #include "base/android/java_exception_reporter.h"
11 #include "base/android/jni_string.h"
12 #include "base/android/jni_utils.h"
13 #include "base/base_jni_headers/PiiElider_jni.h"
14 #include "base/debug/debugging_buildflags.h"
15 #include "base/logging.h"
16 #include "build/build_config.h"
17 #include "third_party/abseil-cpp/absl/base/attributes.h"
18 
19 namespace base {
20 namespace android {
21 namespace {
22 
23 JavaVM* g_jvm = nullptr;
24 jobject g_class_loader = nullptr;
25 jmethodID g_class_loader_load_class_method_id = 0;
26 
27 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
28 ABSL_CONST_INIT thread_local void* stack_frame_pointer = nullptr;
29 #endif
30 
31 bool g_fatal_exception_occurred = false;
32 
GetClassInternal(JNIEnv * env,const char * class_name,jobject class_loader)33 ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
34                                             const char* class_name,
35                                             jobject class_loader) {
36   jclass clazz;
37   if (class_loader != nullptr) {
38     // ClassLoader.loadClass expects a classname with components separated by
39     // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
40     // generator generates names with slashes, so we have to replace them here.
41     // TODO(torne): move to an approach where we always use ClassLoader except
42     // for the special case of base::android::GetClassLoader(), and change the
43     // JNI generator to generate dot-separated names. http://crbug.com/461773
44     size_t bufsize = strlen(class_name) + 1;
45     char dotted_name[bufsize];
46     memmove(dotted_name, class_name, bufsize);
47     for (size_t i = 0; i < bufsize; ++i) {
48       if (dotted_name[i] == '/') {
49         dotted_name[i] = '.';
50       }
51     }
52 
53     clazz = static_cast<jclass>(
54         env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
55                               ConvertUTF8ToJavaString(env, dotted_name).obj()));
56   } else {
57     clazz = env->FindClass(class_name);
58   }
59   if (ClearException(env) || !clazz) {
60     LOG(FATAL) << "Failed to find class " << class_name;
61   }
62   return ScopedJavaLocalRef<jclass>(env, clazz);
63 }
64 
65 }  // namespace
66 
AttachCurrentThread()67 JNIEnv* AttachCurrentThread() {
68   DCHECK(g_jvm);
69   JNIEnv* env = nullptr;
70   jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
71   if (ret == JNI_EDETACHED || !env) {
72     JavaVMAttachArgs args;
73     args.version = JNI_VERSION_1_2;
74     args.group = nullptr;
75 
76     // 16 is the maximum size for thread names on Android.
77     char thread_name[16];
78     int err = prctl(PR_GET_NAME, thread_name);
79     if (err < 0) {
80       DPLOG(ERROR) << "prctl(PR_GET_NAME)";
81       args.name = nullptr;
82     } else {
83       args.name = thread_name;
84     }
85 
86 #if BUILDFLAG(IS_ANDROID)
87     ret = g_jvm->AttachCurrentThread(&env, &args);
88 #else
89     ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
90 #endif
91     CHECK_EQ(JNI_OK, ret);
92   }
93   return env;
94 }
95 
AttachCurrentThreadWithName(const std::string & thread_name)96 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
97   DCHECK(g_jvm);
98   JavaVMAttachArgs args;
99   args.version = JNI_VERSION_1_2;
100   args.name = const_cast<char*>(thread_name.c_str());
101   args.group = nullptr;
102   JNIEnv* env = nullptr;
103 #if BUILDFLAG(IS_ANDROID)
104   jint ret = g_jvm->AttachCurrentThread(&env, &args);
105 #else
106   jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
107 #endif
108   CHECK_EQ(JNI_OK, ret);
109   return env;
110 }
111 
DetachFromVM()112 void DetachFromVM() {
113   // Ignore the return value, if the thread is not attached, DetachCurrentThread
114   // will fail. But it is ok as the native thread may never be attached.
115   if (g_jvm)
116     g_jvm->DetachCurrentThread();
117 }
118 
InitVM(JavaVM * vm)119 void InitVM(JavaVM* vm) {
120   DCHECK(!g_jvm || g_jvm == vm);
121   g_jvm = vm;
122 }
123 
IsVMInitialized()124 bool IsVMInitialized() {
125   return g_jvm != nullptr;
126 }
127 
GetVM()128 JavaVM* GetVM() {
129   return g_jvm;
130 }
131 
InitGlobalClassLoader(JNIEnv * env)132 void InitGlobalClassLoader(JNIEnv* env) {
133   DCHECK(g_class_loader == nullptr);
134 
135   ScopedJavaLocalRef<jclass> class_loader_clazz =
136       GetClass(env, "java/lang/ClassLoader");
137   CHECK(!ClearException(env));
138   g_class_loader_load_class_method_id =
139       env->GetMethodID(class_loader_clazz.obj(),
140                        "loadClass",
141                        "(Ljava/lang/String;)Ljava/lang/Class;");
142   CHECK(!ClearException(env));
143 
144   // GetClassLoader() caches the reference, so we do not need to wrap it in a
145   // smart pointer as well.
146   g_class_loader = GetClassLoader(env);
147 }
148 
GetClass(JNIEnv * env,const char * class_name,const char * split_name)149 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
150                                     const char* class_name,
151                                     const char* split_name) {
152   return GetClassInternal(env, class_name,
153                           GetSplitClassLoader(env, split_name));
154 }
155 
GetClass(JNIEnv * env,const char * class_name)156 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
157   return GetClassInternal(env, class_name, g_class_loader);
158 }
159 
160 // This is duplicated with LazyGetClass below because these are performance
161 // sensitive.
LazyGetClass(JNIEnv * env,const char * class_name,const char * split_name,std::atomic<jclass> * atomic_class_id)162 jclass LazyGetClass(JNIEnv* env,
163                     const char* class_name,
164                     const char* split_name,
165                     std::atomic<jclass>* atomic_class_id) {
166   const jclass value = atomic_class_id->load(std::memory_order_acquire);
167   if (value)
168     return value;
169   ScopedJavaGlobalRef<jclass> clazz;
170   clazz.Reset(GetClass(env, class_name, split_name));
171   jclass cas_result = nullptr;
172   if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
173                                                std::memory_order_acq_rel)) {
174     // We intentionally leak the global ref since we now storing it as a raw
175     // pointer in |atomic_class_id|.
176     return clazz.Release();
177   } else {
178     return cas_result;
179   }
180 }
181 
182 // This is duplicated with LazyGetClass above because these are performance
183 // sensitive.
LazyGetClass(JNIEnv * env,const char * class_name,std::atomic<jclass> * atomic_class_id)184 jclass LazyGetClass(JNIEnv* env,
185                     const char* class_name,
186                     std::atomic<jclass>* atomic_class_id) {
187   const jclass value = atomic_class_id->load(std::memory_order_acquire);
188   if (value)
189     return value;
190   ScopedJavaGlobalRef<jclass> clazz;
191   clazz.Reset(GetClass(env, class_name));
192   jclass cas_result = nullptr;
193   if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
194                                                std::memory_order_acq_rel)) {
195     // We intentionally leak the global ref since we now storing it as a raw
196     // pointer in |atomic_class_id|.
197     return clazz.Release();
198   } else {
199     return cas_result;
200   }
201 }
202 
203 template<MethodID::Type type>
Get(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature)204 jmethodID MethodID::Get(JNIEnv* env,
205                         jclass clazz,
206                         const char* method_name,
207                         const char* jni_signature) {
208   auto get_method_ptr = type == MethodID::TYPE_STATIC ?
209       &JNIEnv::GetStaticMethodID :
210       &JNIEnv::GetMethodID;
211   jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
212   if (base::android::ClearException(env) || !id) {
213     LOG(FATAL) << "Failed to find " <<
214         (type == TYPE_STATIC ? "static " : "") <<
215         "method " << method_name << " " << jni_signature;
216   }
217   return id;
218 }
219 
220 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
221 // into ::Get() above. If there's a race, it's ok since the values are the same
222 // (and the duplicated effort will happen only once).
223 template <MethodID::Type type>
LazyGet(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature,std::atomic<jmethodID> * atomic_method_id)224 jmethodID MethodID::LazyGet(JNIEnv* env,
225                             jclass clazz,
226                             const char* method_name,
227                             const char* jni_signature,
228                             std::atomic<jmethodID>* atomic_method_id) {
229   const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
230   if (value)
231     return value;
232   jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
233   atomic_method_id->store(id, std::memory_order_release);
234   return id;
235 }
236 
237 // Various template instantiations.
238 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
239     JNIEnv* env, jclass clazz, const char* method_name,
240     const char* jni_signature);
241 
242 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
243     JNIEnv* env, jclass clazz, const char* method_name,
244     const char* jni_signature);
245 
246 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
247     JNIEnv* env, jclass clazz, const char* method_name,
248     const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
249 
250 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
251     JNIEnv* env, jclass clazz, const char* method_name,
252     const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
253 
HasException(JNIEnv * env)254 bool HasException(JNIEnv* env) {
255   return env->ExceptionCheck() != JNI_FALSE;
256 }
257 
ClearException(JNIEnv * env)258 bool ClearException(JNIEnv* env) {
259   if (!HasException(env))
260     return false;
261   env->ExceptionDescribe();
262   env->ExceptionClear();
263   return true;
264 }
265 
CheckException(JNIEnv * env)266 void CheckException(JNIEnv* env) {
267   if (!HasException(env))
268     return;
269 
270   jthrowable java_throwable = env->ExceptionOccurred();
271   if (java_throwable) {
272     // Clear the pending exception, since a local reference is now held.
273     env->ExceptionDescribe();
274     env->ExceptionClear();
275 
276     if (g_fatal_exception_occurred) {
277       // Another exception (probably OOM) occurred during GetJavaExceptionInfo.
278       base::android::SetJavaException(
279           "Java OOM'ed in exception handling, check logcat");
280     } else {
281       g_fatal_exception_occurred = true;
282       // RVO should avoid any extra copies of the exception string.
283       base::android::SetJavaException(
284           GetJavaExceptionInfo(env, java_throwable).c_str());
285     }
286   }
287 
288   // Now, feel good about it and die.
289   LOG(FATAL) << "Please include Java exception stack in crash report";
290 }
291 
GetJavaExceptionInfo(JNIEnv * env,jthrowable java_throwable)292 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
293   ScopedJavaLocalRef<jstring> sanitized_exception_string =
294       Java_PiiElider_getSanitizedStacktrace(
295           env, ScopedJavaLocalRef(env, java_throwable));
296 
297   return ConvertJavaStringToUTF8(sanitized_exception_string);
298 }
299 
300 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
301 
JNIStackFrameSaver(void * current_fp)302 JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp)
303     : resetter_(&stack_frame_pointer, current_fp) {}
304 
305 JNIStackFrameSaver::~JNIStackFrameSaver() = default;
306 
SavedFrame()307 void* JNIStackFrameSaver::SavedFrame() {
308   return stack_frame_pointer;
309 }
310 
311 #endif  // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
312 
313 }  // namespace android
314 }  // namespace base
315