/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jni/jni_internal.h"
#include <pthread.h>
#include "common_runtime_test.h"
#include "gc/heap.h"
#include "java_vm_ext.h"
#include "runtime.h"
namespace art {
class JavaVmExtTest : public CommonRuntimeTest {
protected:
void SetUp() override {
CommonRuntimeTest::SetUp();
vm_ = Runtime::Current()->GetJavaVM();
}
void TearDown() override {
CommonRuntimeTest::TearDown();
}
JavaVMExt* vm_;
};
TEST_F(JavaVmExtTest, JNI_GetDefaultJavaVMInitArgs) {
jint err = JNI_GetDefaultJavaVMInitArgs(nullptr);
EXPECT_EQ(JNI_ERR, err);
}
TEST_F(JavaVmExtTest, JNI_GetCreatedJavaVMs) {
JavaVM* vms_buf[1];
jsize num_vms;
jint ok = JNI_GetCreatedJavaVMs(vms_buf, arraysize(vms_buf), &num_vms);
EXPECT_EQ(JNI_OK, ok);
EXPECT_EQ(1, num_vms);
EXPECT_EQ(vms_buf[0], vm_);
}
static bool gSmallStack = false;
static bool gAsDaemon = false;
static void* attach_current_thread_callback(void* arg ATTRIBUTE_UNUSED) {
JavaVM* vms_buf[1];
jsize num_vms;
JNIEnv* env;
jint ok = JNI_GetCreatedJavaVMs(vms_buf, arraysize(vms_buf), &num_vms);
EXPECT_EQ(JNI_OK, ok);
if (ok == JNI_OK) {
if (!gAsDaemon) {
ok = vms_buf[0]->AttachCurrentThread(&env, nullptr);
} else {
ok = vms_buf[0]->AttachCurrentThreadAsDaemon(&env, nullptr);
}
// TODO: Find a way to test with exact SMALL_STACK value, for which we would bail. The pthreads
// spec says that the stack size argument is a lower bound, and bionic currently gives us
// a chunk more on arm64.
if (!gSmallStack) {
EXPECT_EQ(JNI_OK, ok);
}
if (ok == JNI_OK) {
ok = vms_buf[0]->DetachCurrentThread();
EXPECT_EQ(JNI_OK, ok);
}
}
return nullptr;
}
TEST_F(JavaVmExtTest, AttachCurrentThread) {
pthread_t pthread;
const char* reason = __PRETTY_FUNCTION__;
gSmallStack = false;
gAsDaemon = false;
CHECK_PTHREAD_CALL(pthread_create, (&pthread, nullptr, attach_current_thread_callback,
nullptr), reason);
void* ret_val;
CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
EXPECT_EQ(ret_val, nullptr);
}
TEST_F(JavaVmExtTest, AttachCurrentThreadAsDaemon) {
pthread_t pthread;
const char* reason = __PRETTY_FUNCTION__;
gSmallStack = false;
gAsDaemon = true;
CHECK_PTHREAD_CALL(pthread_create, (&pthread, nullptr, attach_current_thread_callback,
nullptr), reason);
void* ret_val;
CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
EXPECT_EQ(ret_val, nullptr);
}
TEST_F(JavaVmExtTest, AttachCurrentThread_SmallStack) {
TEST_DISABLED_FOR_MEMORY_TOOL(); // b/123500163
pthread_t pthread;
pthread_attr_t attr;
const char* reason = __PRETTY_FUNCTION__;
gSmallStack = true;
gAsDaemon = false;
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, PTHREAD_STACK_MIN), reason);
CHECK_PTHREAD_CALL(pthread_create, (&pthread, &attr, attach_current_thread_callback,
nullptr), reason);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
void* ret_val;
CHECK_PTHREAD_CALL(pthread_join, (pthread, &ret_val), reason);
EXPECT_EQ(ret_val, nullptr);
}
TEST_F(JavaVmExtTest, DetachCurrentThread) {
JNIEnv* env;
jint ok = vm_->AttachCurrentThread(&env, nullptr);
ASSERT_EQ(JNI_OK, ok);
ok = vm_->DetachCurrentThread();
EXPECT_EQ(JNI_OK, ok);
jint err = vm_->DetachCurrentThread();
EXPECT_EQ(JNI_ERR, err);
}
class JavaVmExtStackTraceTest : public JavaVmExtTest {
protected:
void SetUpRuntimeOptions(RuntimeOptions* options) override {
options->emplace_back("-XX:GlobalRefAllocStackTraceLimit=50000", nullptr);
}
};
TEST_F(JavaVmExtStackTraceTest, TestEnableDisable) {
ASSERT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
JNIEnv* env;
jint ok = vm_->AttachCurrentThread(&env, nullptr);
ASSERT_EQ(JNI_OK, ok);
std::vector<jobject> global_refs_;
jobject local_ref = env->NewStringUTF("Dummy");
for (size_t i = 0; i < 2000; ++i) {
global_refs_.push_back(env->NewGlobalRef(local_ref));
}
EXPECT_TRUE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
for (jobject global_ref : global_refs_) {
env->DeleteGlobalRef(global_ref);
}
EXPECT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
global_refs_.clear();
for (size_t i = 0; i < 2000; ++i) {
global_refs_.push_back(env->NewGlobalRef(local_ref));
}
EXPECT_TRUE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
for (jobject global_ref : global_refs_) {
env->DeleteGlobalRef(global_ref);
}
EXPECT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
ok = vm_->DetachCurrentThread();
EXPECT_EQ(JNI_OK, ok);
}
} // namespace art