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
  44 const static bool DEFAULT_SPAWN_IN_NEW_THREAD = true;
  45 #else
  46 const static bool DEFAULT_SPAWN_IN_NEW_THREAD = false;
  47 #endif
  48 
  49 static bool is_prefix(const char* prefix, const char* str) {
  50   return strncmp(str, prefix, strlen(prefix)) == 0;
  51 }
  52 
  53 static 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 
  63 static 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 
  96 class 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 
 120 static 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 
 135 static 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 
 156 static 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 
 172 static 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 
 193 static 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 
 258 struct args_t {
 259   int argc; char** argv;
 260 };
 261 
 262 #define STACK_SIZE 0x200000
 263 
 264 #ifdef _WIN32
 265 
 266 static DWORD 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 
 272 static 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 
 284 static 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 
 290 static 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 
 310 extern "C"
 311 JNIEXPORT 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