1 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // angle_native_test:
6 // Contains native implementation for com.android.angle.test.AngleNativeTest.
7
8 #include <jni.h>
9 #include <vector>
10
11 #include <android/log.h>
12 #include <errno.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #include "common/angleutils.h"
18 #include "common/string_utils.h"
19
20 // The main function of the program to be wrapped as a test apk.
21 extern int main(int argc, char **argv);
22
23 namespace
24 {
25
26 const char kLogTag[] = "chromium";
27 const char kCrashedMarker[] = "[ CRASHED ]\n";
28
29 // The list of signals which are considered to be crashes.
30 const int kExceptionSignals[] = {SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1};
31
32 struct sigaction g_old_sa[NSIG];
33
34 class [[nodiscard]] ScopedMainEntryLogger
35 {
36 public:
ScopedMainEntryLogger()37 ScopedMainEntryLogger() { printf(">>ScopedMainEntryLogger\n"); }
38
~ScopedMainEntryLogger()39 ~ScopedMainEntryLogger()
40 {
41 printf("<<ScopedMainEntryLogger\n");
42 fflush(stdout);
43 fflush(stderr);
44 }
45 };
46
47 // This function runs in a compromised context. It should not allocate memory.
SignalHandler(int sig,siginfo_t * info,void * reserved)48 void SignalHandler(int sig, siginfo_t *info, void *reserved)
49 {
50 // Output the crash marker.
51 write(STDOUT_FILENO, kCrashedMarker, sizeof(kCrashedMarker) - 1);
52 g_old_sa[sig].sa_sigaction(sig, info, reserved);
53 }
54
ASCIIJavaStringToUTF8(JNIEnv * env,jstring str)55 std::string ASCIIJavaStringToUTF8(JNIEnv *env, jstring str)
56 {
57 if (!str)
58 {
59 return "";
60 }
61
62 const jsize length = env->GetStringLength(str);
63 if (!length)
64 {
65 return "";
66 }
67
68 // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so
69 // instead get the String in UTF16. As the input is ASCII, drop the higher
70 // bytes.
71 const jchar *jchars = env->GetStringChars(str, NULL);
72 const char16_t *chars = reinterpret_cast<const char16_t *>(jchars);
73 std::string out(chars, chars + length);
74 env->ReleaseStringChars(str, jchars);
75 return out;
76 }
77
ArgsToArgv(const std::vector<std::string> & args,std::vector<char * > * argv)78 size_t ArgsToArgv(const std::vector<std::string> &args, std::vector<char *> *argv)
79 {
80 // We need to pass in a non-const char**.
81 size_t argc = args.size();
82
83 argv->resize(argc + 1);
84 for (size_t i = 0; i < argc; ++i)
85 {
86 (*argv)[i] = const_cast<char *>(args[i].c_str());
87 }
88 (*argv)[argc] = NULL; // argv must be NULL terminated.
89
90 return argc;
91 }
92
InstallExceptionHandlers()93 void InstallExceptionHandlers()
94 {
95 struct sigaction sa;
96 memset(&sa, 0, sizeof(sa));
97
98 sa.sa_sigaction = SignalHandler;
99 sa.sa_flags = SA_SIGINFO;
100
101 for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i)
102 {
103 sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]);
104 }
105 }
106
AndroidLog(int priority,const char * format,...)107 void AndroidLog(int priority, const char *format, ...)
108 {
109 va_list args;
110 va_start(args, format);
111 __android_log_vprint(priority, kLogTag, format, args);
112 va_end(args);
113 }
114
115 } // anonymous namespace
116
117 extern "C" JNIEXPORT void JNICALL
Java_com_android_angle_test_AngleNativeTest_nativeRunTests(JNIEnv * env,jclass clazz,jstring jcommandLineFlags,jstring jcommandLineFilePath,jstring jstdoutFilePath)118 Java_com_android_angle_test_AngleNativeTest_nativeRunTests(JNIEnv *env,
119 jclass clazz,
120 jstring jcommandLineFlags,
121 jstring jcommandLineFilePath,
122 jstring jstdoutFilePath)
123 {
124 InstallExceptionHandlers();
125
126 const std::string commandLineFlags(ASCIIJavaStringToUTF8(env, jcommandLineFlags));
127 const std::string commandLineFilePath(ASCIIJavaStringToUTF8(env, jcommandLineFilePath));
128 const std::string stdoutFilePath(ASCIIJavaStringToUTF8(env, jstdoutFilePath));
129
130 std::vector<std::string> args;
131 if (commandLineFilePath.empty())
132 {
133 args.push_back("_");
134 }
135 else
136 {
137 std::string commandLineString;
138 if (angle::ReadFileToString(commandLineFilePath, &commandLineString))
139 {
140 angle::SplitStringAlongWhitespace(commandLineString, &args);
141 }
142 }
143 angle::SplitStringAlongWhitespace(commandLineFlags, &args);
144
145 // A few options, such "--gtest_list_tests", will just use printf directly
146 // Always redirect stdout to a known file.
147 FILE *stdoutFile = fopen(stdoutFilePath.c_str(), "a+");
148 if (stdoutFile == NULL)
149 {
150 AndroidLog(ANDROID_LOG_ERROR, "Failed to open stdout file: %s: %s\n",
151 stdoutFilePath.c_str(), strerror(errno));
152 exit(EXIT_FAILURE);
153 }
154
155 int oldStdout = dup(STDOUT_FILENO);
156 if (oldStdout == -1)
157 {
158 AndroidLog(ANDROID_LOG_ERROR, "Failed to dup stdout: %d\n", errno);
159 fclose(stdoutFile);
160 exit(EXIT_FAILURE);
161 }
162
163 int retVal = dup2(fileno(stdoutFile), STDOUT_FILENO);
164 if (retVal == -1)
165 {
166 AndroidLog(ANDROID_LOG_ERROR, "Failed to dup2 stdout to file: %d\n", errno);
167 fclose(stdoutFile);
168 close(oldStdout);
169 exit(EXIT_FAILURE);
170 }
171
172 dup2(STDOUT_FILENO, STDERR_FILENO);
173
174 // When using a temp path on `/data`, performance is good enough we can line buffer
175 // stdout/stderr. This makes e.g. FATAL() << "message"; show up in the logs in CI
176 // or local runs. Do *not* enable this on /sdcard/ (see https://crrev.com/c/3615081)
177 if (stdoutFilePath.rfind("/data/", 0) == 0)
178 {
179 setlinebuf(stdout);
180 setlinebuf(stderr);
181 }
182
183 std::vector<char *> argv;
184 size_t argc = ArgsToArgv(args, &argv);
185
186 {
187 ScopedMainEntryLogger scoped_main_entry_logger;
188 main(static_cast<int>(argc), &argv[0]);
189 }
190
191 fclose(stdoutFile);
192 dup2(oldStdout, STDOUT_FILENO);
193 close(oldStdout);
194 }
195