1/*
2 * Copyright (c) 2016, 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 "prims/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      ASSERT_EQ(0, init_jvm(_argc, _argv, false)) << "Could not initialize the JVM";
115      _is_initialized = true;
116    }
117  }
118};
119
120static char* get_java_home_arg(int argc, char** argv) {
121  for (int i = 0; i < argc; i++) {
122    if (strncmp(argv[i], "-jdk", strlen(argv[i])) == 0) {
123      return argv[i+1];
124    }
125    if (is_prefix("--jdk=", argv[i])) {
126      return argv[i] + strlen("--jdk=");
127    }
128    if (is_prefix("-jdk:", argv[i])) {
129      return argv[i] + strlen("-jdk:");
130    }
131  }
132  return NULL;
133}
134
135static bool get_spawn_new_main_thread_arg(int argc, char** argv) {
136  // -new-thread[=(true|false)]
137  for (int i = 0; i < argc; i++) {
138    if (is_prefix("-new-thread", argv[i])) {
139      const char* v = argv[i] + strlen("-new-thread");
140      if (strlen(v) == 0) {
141        return true;
142      } else {
143        if (strcmp(v, "=true") == 0) {
144          return true;
145        } else if (strcmp(v, "=false") == 0) {
146          return false;
147        } else {
148          fprintf(stderr, "Invalid value for -new-thread (%s)", v);
149        }
150      }
151    }
152  }
153  return DEFAULT_SPAWN_IN_NEW_THREAD;
154}
155
156static int num_args_to_skip(char* arg) {
157  if (strcmp(arg, "-jdk") == 0) {
158    return 2; // skip the argument after -jdk as well
159  }
160  if (is_prefix("--jdk=", arg)) {
161    return 1;
162  }
163  if (is_prefix("-jdk:", arg)) {
164    return 1;
165  }
166  if (is_prefix("-new-thread", arg)) {
167    return 1;
168  }
169  return 0;
170}
171
172static char** remove_test_runner_arguments(int* argcp, char **argv) {
173  int argc = *argcp;
174  char** new_argv = (char**) malloc(sizeof(char*) * argc);
175  int new_argc = 0;
176
177  int i = 0;
178  while (i < argc) {
179    int args_to_skip = num_args_to_skip(argv[i]);
180    if (args_to_skip == 0) {
181      new_argv[new_argc] = argv[i];
182      i++;
183      new_argc++;
184    } else {
185      i += num_args_to_skip(argv[i]);
186    }
187  }
188
189  *argcp = new_argc;
190  return new_argv;
191}
192
193static void runUnitTestsInner(int argc, char** argv) {
194  ::testing::InitGoogleTest(&argc, argv);
195  ::testing::GTEST_FLAG(death_test_style) = "threadsafe";
196
197  bool is_vmassert_test = false;
198  bool is_othervm_test = false;
199  // death tests facility is used for both regular death tests, other vm and vmassert tests
200  if (::testing::internal::GTEST_FLAG(internal_run_death_test).length() > 0) {
201    // when we execute death test, filter value equals to test name
202    const char* test_name = ::testing::GTEST_FLAG(filter).c_str();
203    const char* const othervm_suffix = "_other_vm_test"; // TEST_OTHER_VM
204    const char* const vmassert_suffix = "_vm_assert_test"; // TEST_VM_ASSERT(_MSG)
205    if (is_suffix(othervm_suffix, test_name)) {
206      is_othervm_test = true;
207    } else if (is_suffix(vmassert_suffix, test_name)) {
208      is_vmassert_test = true;
209    }
210  }
211
212  char* java_home = get_java_home_arg(argc, argv);
213  if (java_home == NULL) {
214    fprintf(stderr, "ERROR: You must specify a JDK to use for running the unit tests.\n");
215    exit(1);
216  }
217#ifndef _WIN32
218  int overwrite = 1; // overwrite an eventual existing value for JAVA_HOME
219  setenv("JAVA_HOME", java_home, overwrite);
220
221// workaround for JDK-7131356
222#ifdef __APPLE__
223  size_t len = strlen(java_home) + strlen("/lib/jli/libjli.dylib") + 1;
224  char* path = new char[len];
225  snprintf(path, len, "%s/lib/jli/libjli.dylib", java_home);
226  dlopen(path, RTLD_NOW | RTLD_GLOBAL);
227#endif // __APPLE__
228
229#else  // _WIN32
230  char* java_home_var = "_ALT_JAVA_HOME_DIR";
231  size_t len = strlen(java_home) + strlen(java_home_var) + 2;
232  char * envString = new char[len];
233  sprintf_s(envString, len, "%s=%s", java_home_var, java_home);
234  _putenv(envString);
235#endif // _WIN32
236  argv = remove_test_runner_arguments(&argc, argv);
237
238  if (is_vmassert_test || is_othervm_test) {
239    // both vmassert and other vm tests require inited jvm
240    // but only vmassert tests disable hs_err and core file generation
241    if (init_jvm(argc, argv, is_vmassert_test) != 0) {
242      abort();
243    }
244  } else {
245    ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners();
246    listeners.Append(new JVMInitializerListener(argc, argv));
247  }
248
249  int result = RUN_ALL_TESTS();
250  if (result != 0) {
251    fprintf(stderr, "ERROR: RUN_ALL_TESTS() failed. Error %d\n", result);
252    exit(2);
253  }
254}
255
256// Thread support for -new-thread option
257
258struct args_t {
259  int argc; char** argv;
260};
261
262#define STACK_SIZE 0x200000
263
264#ifdef _WIN32
265
266static DWORD WINAPI thread_wrapper(void* p) {
267  const args_t* const p_args = (const args_t*) p;
268  runUnitTestsInner(p_args->argc, p_args->argv);
269  return 0;
270}
271
272static void run_in_new_thread(const args_t* args) {
273  HANDLE hdl;
274  hdl = CreateThread(NULL, STACK_SIZE, thread_wrapper, (void*)args, 0, NULL);
275  if (hdl == NULL) {
276    fprintf(stderr, "Failed to create main thread\n");
277    exit(2);
278  }
279  WaitForSingleObject(hdl, INFINITE);
280}
281
282#else
283
284extern "C" void* thread_wrapper(void* p) {
285  const args_t* const p_args = (const args_t*) p;
286  runUnitTestsInner(p_args->argc, p_args->argv);
287  return 0;
288}
289
290static void run_in_new_thread(const args_t* args) {
291  pthread_t tid;
292  pthread_attr_t attr;
293
294  pthread_attr_init(&attr);
295  pthread_attr_setstacksize(&attr, STACK_SIZE);
296
297  if (pthread_create(&tid, &attr, thread_wrapper, (void*)args) != 0) {
298    fprintf(stderr, "Failed to create main thread\n");
299    exit(2);
300  }
301
302  if (pthread_join(tid, NULL) != 0) {
303    fprintf(stderr, "Failed to join main thread\n");
304    exit(2);
305  }
306}
307
308#endif
309
310extern "C"
311JNIEXPORT void JNICALL runUnitTests(int argc, char** argv) {
312  const bool spawn_new_main_thread = get_spawn_new_main_thread_arg(argc, argv);
313  if (spawn_new_main_thread) {
314    args_t args;
315    args.argc = argc;
316    args.argv = argv;
317    run_in_new_thread(&args);
318  } else {
319    runUnitTestsInner(argc, argv);
320  }
321}
322
323