blob: 8e0d4fcb6aeafed72bbb7841c7c1e207055069d0 [file] [log] [blame]
// 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 <jni.h>
#include <vector>
#include <android/log.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.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 ScopedMainEntryLogger
{
public:
ScopedMainEntryLogger() { printf(">>ScopedMainEntryLogger\n"); }
~ScopedMainEntryLogger()
{
printf("<<ScopedMainEntryLogger\n");
fflush(stdout);
fflush(stderr);
}
};
// This function runs in a compromised context. It should not allocate memory.
void SignalHandler(int sig, siginfo_t *info, void *reserved)
{
// Output the crash marker.
write(STDOUT_FILENO, kCrashedMarker, sizeof(kCrashedMarker) - 1);
g_old_sa[sig].sa_sigaction(sig, info, reserved);
}
std::string ASCIIJavaStringToUTF8(JNIEnv *env, jstring str)
{
if (!str)
{
return "";
}
const jsize length = env->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<const char16_t *>(jchars);
std::string out(chars, chars + length);
env->ReleaseStringChars(str, jchars);
return out;
}
size_t ArgsToArgv(const std::vector<std::string> &args, std::vector<char *> *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<char *>(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<std::string> 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<char *> argv;
size_t argc = ArgsToArgv(args, &argv);
{
ScopedMainEntryLogger scoped_main_entry_logger;
main(static_cast<int>(argc), &argv[0]);
}
fclose(stdoutFile);
dup2(oldStdout, STDOUT_FILENO);
close(oldStdout);
}