1/*
2 * Copyright (c) 2016, 2019, 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 <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#ifdef __APPLE__
29# include <dlfcn.h>
30#endif
31
32#ifdef _WIN32
33#include <windows.h>
34#else
35#include <pthread.h>
36#endif
37
38#include "jni.h"
39#include "unittest.hpp"
40
41// Default value for -new-thread option: true on AIX because we run into
42// problems when attempting to initialize the JVM on the primordial thread.
43#ifdef _AIX
44const static bool DEFAULT_SPAWN_IN_NEW_THREAD = true;
45#else
46const static bool DEFAULT_SPAWN_IN_NEW_THREAD = false;
47#endif
48
49static bool is_prefix(const char* prefix, const char* str) {
50 return strncmp(str, prefix, strlen(prefix)) == 0;
51}
52
53static bool is_suffix(const char* suffix, const char* str) {
54 size_t suffix_len = strlen(suffix);
55 size_t str_len = strlen(str);
56 if (str_len < suffix_len) {
57 return false;
58 }
59 return strncmp(str + (str_len - suffix_len), suffix, suffix_len) == 0;
60}
61
62
63static int init_jvm(int argc, char **argv, bool disable_error_handling) {
64 // don't care about the program name
65 argc--;
66 argv++;
67
68 int extra_jvm_args = disable_error_handling ? 4 : 2;
69 int num_jvm_options = argc + extra_jvm_args;
70
71 JavaVMOption* options = new JavaVMOption[num_jvm_options];
72 options[0].optionString = (char*) "-Dsun.java.launcher.is_altjvm=true";
73 options[1].optionString = (char*) "-XX:+ExecutingUnitTests";
74
75 if (disable_error_handling) {
76 // don't create core files or hs_err files executing assert tests
77 options[2].optionString = (char*) "-XX:+SuppressFatalErrorMessage";
78 options[3].optionString = (char*) "-XX:-CreateCoredumpOnCrash";
79 }
80
81 for (int i = 0; i < argc; i++) {
82 options[extra_jvm_args + i].optionString = argv[i];
83 }
84
85 JavaVMInitArgs args;
86 args.version = JNI_VERSION_1_8;
87 args.nOptions = num_jvm_options;
88 args.options = options;
89
90 JavaVM* jvm;
91 JNIEnv* env;
92
93 return JNI_CreateJavaVM(&jvm, (void**)&env, &args);
94}
95
96class JVMInitializerListener : public ::testing::EmptyTestEventListener {
97 private:
98 int _argc;
99 char** _argv;
100 bool _is_initialized;
101
102 void initialize_jvm() {
103 }
104
105 public:
106 JVMInitializerListener(int argc, char** argv) :
107 _argc(argc), _argv(argv), _is_initialized(false) {
108 }
109
110 virtual void OnTestStart(const ::testing::TestInfo& test_info) {
111 const char* name = test_info.name();
112 if (!_is_initialized && is_suffix("_test_vm", name)) {
113 // we want to have hs_err and core files when we execute regular tests
114 int ret_val = init_jvm(_argc, _argv, false);
115 if (ret_val != 0) {
116 ADD_FAILURE() << "Could not initialize the JVM";
117 exit(1);
118 }
119 _is_initialized = true;
120 }
121 }
122};
123
124static char* get_java_home_arg(int argc, char** argv) {
125 for (int i = 0; i < argc; i++) {
126 if (strncmp(argv[i], "-jdk", strlen(argv[i])) == 0) {
127 return argv[i+1];
128 }
129 if (is_prefix("--jdk=", argv[i])) {
130 return argv[i] + strlen("--jdk=");
131 }
132 if (is_prefix("-jdk:", argv[i])) {
133 return argv[i] + strlen("-jdk:");
134 }
135 }
136 return NULL;
137}
138
139static bool get_spawn_new_main_thread_arg(int argc, char** argv) {
140 // -new-thread[=(true|false)]
141 for (int i = 0; i < argc; i++) {
142 if (is_prefix("-new-thread", argv[i])) {
143 const char* v = argv[i] + strlen("-new-thread");
144 if (strlen(v) == 0) {
145 return true;
146 } else {
147 if (strcmp(v, "=true") == 0) {
148 return true;
149 } else if (strcmp(v, "=false") == 0) {
150 return false;
151 } else {
152 fprintf(stderr, "Invalid value for -new-thread (%s)", v);
153 }
154 }
155 }
156 }
157 return DEFAULT_SPAWN_IN_NEW_THREAD;
158}
159
160static int num_args_to_skip(char* arg) {
161 if (strcmp(arg, "-jdk") == 0) {
162 return 2; // skip the argument after -jdk as well
163 }
164 if (is_prefix("--jdk=", arg)) {
165 return 1;
166 }
167 if (is_prefix("-jdk:", arg)) {
168 return 1;
169 }
170 if (is_prefix("-new-thread", arg)) {
171 return 1;
172 }
173 return 0;
174}
175
176static char** remove_test_runner_arguments(int* argcp, char **argv) {
177 int argc = *argcp;
178 char** new_argv = (char**) malloc(sizeof(char*) * argc);
179 int new_argc = 0;
180
181 int i = 0;
182 while (i < argc) {
183 int args_to_skip = num_args_to_skip(argv[i]);
184 if (args_to_skip == 0) {
185 new_argv[new_argc] = argv[i];
186 i++;
187 new_argc++;
188 } else {
189 i += num_args_to_skip(argv[i]);
190 }
191 }
192
193 *argcp = new_argc;
194 return new_argv;
195}
196
197static void runUnitTestsInner(int argc, char** argv) {
198 ::testing::InitGoogleMock(&argc, argv);
199 ::testing::GTEST_FLAG(death_test_style) = "threadsafe";
200
201 bool is_vmassert_test = false;
202 bool is_othervm_test = false;
203 // death tests facility is used for both regular death tests, other vm and vmassert tests
204 if (::testing::internal::GTEST_FLAG(internal_run_death_test).length() > 0) {
205 // when we execute death test, filter value equals to test name
206 const char* test_name = ::testing::GTEST_FLAG(filter).c_str();
207 const char* const othervm_suffix = "_other_vm_test"; // TEST_OTHER_VM
208 const char* const vmassert_suffix = "_vm_assert_test"; // TEST_VM_ASSERT(_MSG)
209 if (is_suffix(othervm_suffix, test_name)) {
210 is_othervm_test = true;
211 } else if (is_suffix(vmassert_suffix, test_name)) {
212 is_vmassert_test = true;
213 }
214 }
215
216 char* java_home = get_java_home_arg(argc, argv);
217 if (java_home == NULL) {
218 fprintf(stderr, "ERROR: You must specify a JDK to use for running the unit tests.\n");
219 exit(1);
220 }
221#ifndef _WIN32
222 int overwrite = 1; // overwrite an eventual existing value for JAVA_HOME
223 setenv("JAVA_HOME", java_home, overwrite);
224
225// workaround for JDK-7131356
226#ifdef __APPLE__
227 size_t len = strlen(java_home) + strlen("/lib/jli/libjli.dylib") + 1;
228 char* path = new char[len];
229 snprintf(path, len, "%s/lib/jli/libjli.dylib", java_home);
230 dlopen(path, RTLD_NOW | RTLD_GLOBAL);
231#endif // __APPLE__
232
233#else // _WIN32
234 const char* java_home_var = "_ALT_JAVA_HOME_DIR";
235 size_t len = strlen(java_home) + strlen(java_home_var) + 2;
236 char * envString = new char[len];
237 sprintf_s(envString, len, "%s=%s", java_home_var, java_home);
238 _putenv(envString);
239#endif // _WIN32
240 argv = remove_test_runner_arguments(&argc, argv);
241
242 if (is_vmassert_test || is_othervm_test) {
243 // both vmassert and other vm tests require inited jvm
244 // but only vmassert tests disable hs_err and core file generation
245 if (init_jvm(argc, argv, is_vmassert_test) != 0) {
246 abort();
247 }
248 } else {
249 ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners();
250 listeners.Append(new JVMInitializerListener(argc, argv));
251 }
252
253 int result = RUN_ALL_TESTS();
254 if (result != 0) {
255 fprintf(stderr, "ERROR: RUN_ALL_TESTS() failed. Error %d\n", result);
256 exit(2);
257 }
258}
259
260// Thread support for -new-thread option
261
262struct args_t {
263 int argc; char** argv;
264};
265
266#define STACK_SIZE 0x200000
267
268#ifdef _WIN32
269
270static DWORD WINAPI thread_wrapper(void* p) {
271 const args_t* const p_args = (const args_t*) p;
272 runUnitTestsInner(p_args->argc, p_args->argv);
273 return 0;
274}
275
276static void run_in_new_thread(const args_t* args) {
277 HANDLE hdl;
278 hdl = CreateThread(NULL, STACK_SIZE, thread_wrapper, (void*)args, 0, NULL);
279 if (hdl == NULL) {
280 fprintf(stderr, "Failed to create main thread\n");
281 exit(2);
282 }
283 WaitForSingleObject(hdl, INFINITE);
284}
285
286#else
287
288extern "C" void* thread_wrapper(void* p) {
289 const args_t* const p_args = (const args_t*) p;
290 runUnitTestsInner(p_args->argc, p_args->argv);
291 return 0;
292}
293
294static void run_in_new_thread(const args_t* args) {
295 pthread_t tid;
296 pthread_attr_t attr;
297
298 pthread_attr_init(&attr);
299 pthread_attr_setstacksize(&attr, STACK_SIZE);
300
301 if (pthread_create(&tid, &attr, thread_wrapper, (void*)args) != 0) {
302 fprintf(stderr, "Failed to create main thread\n");
303 exit(2);
304 }
305
306 if (pthread_join(tid, NULL) != 0) {
307 fprintf(stderr, "Failed to join main thread\n");
308 exit(2);
309 }
310}
311
312#endif
313
314extern "C"
315JNIEXPORT void JNICALL runUnitTests(int argc, char** argv) {
316 const bool spawn_new_main_thread = get_spawn_new_main_thread_arg(argc, argv);
317 if (spawn_new_main_thread) {
318 args_t args;
319 args.argc = argc;
320 args.argv = argv;
321 run_in_new_thread(&args);
322 } else {
323 runUnitTestsInner(argc, argv);
324 }
325}
326