• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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