// Copyright 2021 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // angle_native_test: // Contains native implementation for com.android.angle.test.AngleNativeTest. #include #include #include #include #include #include #include #include "common/angleutils.h" #include "common/string_utils.h" // The main function of the program to be wrapped as a test apk. extern int main(int argc, char **argv); namespace { const char kLogTag[] = "chromium"; const char kCrashedMarker[] = "[ CRASHED ]\n"; // The list of signals which are considered to be crashes. const int kExceptionSignals[] = {SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1}; struct sigaction g_old_sa[NSIG]; class ANGLE_NO_DISCARD ScopedMainEntryLogger { public: ScopedMainEntryLogger() { printf(">>ScopedMainEntryLogger\n"); } ~ScopedMainEntryLogger() { printf("<GetStringLength(str); if (!length) { return ""; } // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so // instead get the String in UTF16. As the input is ASCII, drop the higher // bytes. const jchar *jchars = env->GetStringChars(str, NULL); const char16_t *chars = reinterpret_cast(jchars); std::string out(chars, chars + length); env->ReleaseStringChars(str, jchars); return out; } size_t ArgsToArgv(const std::vector &args, std::vector *argv) { // We need to pass in a non-const char**. size_t argc = args.size(); argv->resize(argc + 1); for (size_t i = 0; i < argc; ++i) { (*argv)[i] = const_cast(args[i].c_str()); } (*argv)[argc] = NULL; // argv must be NULL terminated. return argc; } void InstallExceptionHandlers() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = SignalHandler; sa.sa_flags = SA_SIGINFO; for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) { sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]); } } void AndroidLog(int priority, const char *format, ...) { va_list args; va_start(args, format); __android_log_vprint(priority, kLogTag, format, args); va_end(args); } } // anonymous namespace extern "C" JNIEXPORT void JNICALL Java_com_android_angle_test_AngleNativeTest_nativeRunTests(JNIEnv *env, jclass clazz, jstring jcommandLineFlags, jstring jcommandLineFilePath, jstring jstdoutFilePath) { InstallExceptionHandlers(); const std::string commandLineFlags(ASCIIJavaStringToUTF8(env, jcommandLineFlags)); const std::string commandLineFilePath(ASCIIJavaStringToUTF8(env, jcommandLineFilePath)); const std::string stdoutFilePath(ASCIIJavaStringToUTF8(env, jstdoutFilePath)); std::vector args; if (commandLineFilePath.empty()) { args.push_back("_"); } else { std::string commandLineString; if (angle::ReadFileToString(commandLineFilePath, &commandLineString)) { angle::SplitStringAlongWhitespace(commandLineString, &args); } } angle::SplitStringAlongWhitespace(commandLineFlags, &args); // A few options, such "--gtest_list_tests", will just use printf directly // Always redirect stdout to a known file. FILE *stdoutFile = fopen(stdoutFilePath.c_str(), "a+"); if (stdoutFile == NULL) { AndroidLog(ANDROID_LOG_ERROR, "Failed to open stdout file: %s: %s\n", stdoutFilePath.c_str(), strerror(errno)); exit(EXIT_FAILURE); } int oldStdout = dup(STDOUT_FILENO); if (oldStdout == -1) { AndroidLog(ANDROID_LOG_ERROR, "Failed to dup stdout: %d\n", errno); fclose(stdoutFile); exit(EXIT_FAILURE); } int retVal = dup2(fileno(stdoutFile), STDOUT_FILENO); if (retVal == -1) { AndroidLog(ANDROID_LOG_ERROR, "Failed to dup2 stdout to file: %d\n", errno); fclose(stdoutFile); close(oldStdout); exit(EXIT_FAILURE); } dup2(STDOUT_FILENO, STDERR_FILENO); std::vector argv; size_t argc = ArgsToArgv(args, &argv); { ScopedMainEntryLogger scoped_main_entry_logger; main(static_cast(argc), &argv[0]); } fclose(stdoutFile); dup2(oldStdout, STDOUT_FILENO); close(oldStdout); }