xref: /aosp_15_r20/external/deqp/framework/platform/android/tcuAndroidExecService.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Android ExecServer.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuAndroidExecService.hpp"
25 #include "deFile.h"
26 #include "deClock.h"
27 
28 #if 0
29 #define DBG_PRINT(ARGS) print ARGS
30 #else
31 #define DBG_PRINT(ARGS)
32 #endif
33 
34 namespace tcu
35 {
36 namespace Android
37 {
38 
39 static const char *LOG_FILE_NAME = "/sdcard/dEQP-log.qpa";
40 
41 enum
42 {
43     PROCESS_START_TIMEOUT  = 5000 * 1000, //!< Timeout in usec.
44     PROCESS_QUERY_INTERVAL = 1000 * 1000  //!< Running query interval limit in usec.
45 };
46 
checkJniException(JNIEnv * env,const char * file,int line)47 static void checkJniException(JNIEnv *env, const char *file, int line)
48 {
49     if (env->ExceptionOccurred())
50     {
51         env->ExceptionDescribe();
52         env->ExceptionClear();
53         throw InternalError("JNI Exception", DE_NULL, file, line);
54     }
55 }
56 
57 #define JNI_CHECK(EXPR)                             \
58     do                                              \
59     {                                               \
60         checkJniException(env, __FILE__, __LINE__); \
61         TCU_CHECK_INTERNAL(EXPR);                   \
62     } while (false)
63 
64 // TestProcess
65 
TestProcess(JavaVM * vm,jobject context)66 TestProcess::TestProcess(JavaVM *vm, jobject context)
67     : m_vm(vm)
68     , m_remoteCls(0)
69     , m_remote(0)
70     , m_start(0)
71     , m_kill(0)
72     , m_isRunning(0)
73     , m_launchTime(0)
74     , m_lastQueryTime(0)
75     , m_lastRunningStatus(false)
76     , m_logReader(xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
77 {
78     DBG_PRINT(("TestProcess::TestProcess(%p, %p)", vm, context));
79 
80     JNIEnv *env         = getCurrentThreadEnv();
81     jobject remote      = 0;
82     jstring logFileName = 0;
83 
84     try
85     {
86         jclass remoteCls = 0;
87         jmethodID ctorId = 0;
88 
89         remoteCls = env->FindClass("com/drawelements/deqp/testercore/RemoteAPI");
90         JNI_CHECK(remoteCls);
91 
92         // Acquire global reference to RemoteAPI class.
93         m_remoteCls = reinterpret_cast<jclass>(env->NewGlobalRef(remoteCls));
94         JNI_CHECK(m_remoteCls);
95         env->DeleteLocalRef(remoteCls);
96         remoteCls = 0;
97 
98         ctorId = env->GetMethodID(m_remoteCls, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
99         JNI_CHECK(ctorId);
100 
101         logFileName = env->NewStringUTF(LOG_FILE_NAME);
102         JNI_CHECK(logFileName);
103 
104         // Create RemoteAPI instance.
105         remote = env->NewObject(m_remoteCls, ctorId, context, logFileName);
106         JNI_CHECK(remote);
107 
108         env->DeleteLocalRef(logFileName);
109         logFileName = 0;
110 
111         // Acquire global reference to remote.
112         m_remote = env->NewGlobalRef(remote);
113         JNI_CHECK(m_remote);
114         env->DeleteLocalRef(remote);
115         remote = 0;
116 
117         m_start = env->GetMethodID(m_remoteCls, "start", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
118         JNI_CHECK(m_start);
119 
120         m_kill = env->GetMethodID(m_remoteCls, "kill", "()Z");
121         JNI_CHECK(m_kill);
122 
123         m_isRunning = env->GetMethodID(m_remoteCls, "isRunning", "()Z");
124         JNI_CHECK(m_isRunning);
125     }
126     catch (...)
127     {
128         if (logFileName)
129             env->DeleteLocalRef(logFileName);
130         if (remote)
131             env->DeleteLocalRef(remote);
132         if (m_remoteCls)
133             env->DeleteGlobalRef(reinterpret_cast<jobject>(m_remoteCls));
134         if (m_remote)
135             env->DeleteGlobalRef(m_remote);
136         throw;
137     }
138 }
139 
~TestProcess(void)140 TestProcess::~TestProcess(void)
141 {
142     DBG_PRINT(("TestProcess::~TestProcess()"));
143 
144     try
145     {
146         JNIEnv *env = getCurrentThreadEnv();
147         env->DeleteGlobalRef(m_remote);
148         env->DeleteGlobalRef(m_remoteCls);
149     }
150     catch (...)
151     {
152     }
153 }
154 
start(const char * name,const char * params,const char * workingDir,const char * caseList)155 void TestProcess::start(const char *name, const char *params, const char *workingDir, const char *caseList)
156 {
157     DBG_PRINT(("TestProcess::start(%s, %s, %s, ...)", name, params, workingDir));
158 
159     JNIEnv *env         = getCurrentThreadEnv();
160     jstring nameStr     = 0;
161     jstring paramsStr   = 0;
162     jstring caseListStr = 0;
163 
164     DE_UNREF(workingDir);
165 
166     // Remove old log file if such exists.
167     if (deFileExists(LOG_FILE_NAME))
168     {
169         if (!deDeleteFile(LOG_FILE_NAME) || deFileExists(LOG_FILE_NAME))
170             throw xs::TestProcessException(std::string("Failed to remove '") + LOG_FILE_NAME + "'");
171     }
172 
173     try
174     {
175         nameStr = env->NewStringUTF(name);
176         JNI_CHECK(nameStr);
177 
178         paramsStr = env->NewStringUTF(params);
179         JNI_CHECK(paramsStr);
180 
181         caseListStr = env->NewStringUTF(caseList);
182         JNI_CHECK(caseListStr);
183 
184         jboolean res = env->CallBooleanMethod(m_remote, m_start, nameStr, paramsStr, caseListStr);
185         checkJniException(env, __FILE__, __LINE__);
186 
187         if (res == JNI_FALSE)
188             throw xs::TestProcessException("Failed to launch activity");
189 
190         m_launchTime        = deGetMicroseconds();
191         m_lastQueryTime     = m_launchTime;
192         m_lastRunningStatus = true;
193     }
194     catch (...)
195     {
196         if (nameStr)
197             env->DeleteLocalRef(nameStr);
198         if (paramsStr)
199             env->DeleteLocalRef(paramsStr);
200         if (caseListStr)
201             env->DeleteLocalRef(caseListStr);
202         throw;
203     }
204 
205     env->DeleteLocalRef(nameStr);
206     env->DeleteLocalRef(paramsStr);
207     env->DeleteLocalRef(caseListStr);
208 }
209 
terminate(void)210 void TestProcess::terminate(void)
211 {
212     DBG_PRINT(("TestProcess::terminate()"));
213 
214     JNIEnv *env  = getCurrentThreadEnv();
215     jboolean res = env->CallBooleanMethod(m_remote, m_kill);
216     checkJniException(env, __FILE__, __LINE__);
217     DE_UNREF(res); // Failure to kill process is ignored.
218 }
219 
cleanup(void)220 void TestProcess::cleanup(void)
221 {
222     DBG_PRINT(("TestProcess::cleanup()"));
223 
224     terminate();
225     m_logReader.stop();
226 }
227 
isRunning(void)228 bool TestProcess::isRunning(void)
229 {
230     uint64_t curTime = deGetMicroseconds();
231 
232     // On Android process launch is asynchronous so we don't want to poll for process until after some time.
233     if (curTime - m_launchTime < PROCESS_START_TIMEOUT || curTime - m_lastQueryTime < PROCESS_QUERY_INTERVAL)
234         return m_lastRunningStatus;
235 
236     JNIEnv *env  = getCurrentThreadEnv();
237     jboolean res = env->CallBooleanMethod(m_remote, m_isRunning);
238     checkJniException(env, __FILE__, __LINE__);
239 
240     DBG_PRINT(("TestProcess::isRunning(): %s", res == JNI_TRUE ? "true" : "false"));
241     m_lastQueryTime     = curTime;
242     m_lastRunningStatus = res == JNI_TRUE;
243 
244     return m_lastRunningStatus;
245 }
246 
getCurrentThreadEnv(void)247 JNIEnv *TestProcess::getCurrentThreadEnv(void)
248 {
249     JNIEnv *env = DE_NULL;
250     jint ret    = m_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
251 
252     if (ret == JNI_OK)
253         return env;
254     else
255         throw InternalError("GetEnv() failed");
256 }
257 
readTestLog(uint8_t * dst,int numBytes)258 int TestProcess::readTestLog(uint8_t *dst, int numBytes)
259 {
260     if (!m_logReader.isRunning())
261     {
262         if (deGetMicroseconds() - m_launchTime > xs::LOG_FILE_TIMEOUT * 1000)
263         {
264             // Timeout, kill process.
265             terminate();
266             DBG_PRINT(("TestProcess:readTestLog(): Log file timeout occurred!"));
267             return 0; // \todo [2013-08-13 pyry] Throw exception?
268         }
269 
270         if (!deFileExists(LOG_FILE_NAME))
271             return 0;
272 
273         // Start reader.
274         m_logReader.start(LOG_FILE_NAME);
275     }
276 
277     DE_ASSERT(m_logReader.isRunning());
278     return m_logReader.read(dst, numBytes);
279 }
280 
getExitCode(void) const281 int TestProcess::getExitCode(void) const
282 {
283     return 0;
284 }
285 
readInfoLog(uint8_t * dst,int numBytes)286 int TestProcess::readInfoLog(uint8_t *dst, int numBytes)
287 {
288     // \todo [2012-11-12 pyry] Read device log.
289     DE_UNREF(dst && numBytes);
290     return 0;
291 }
292 
293 // ExecutionServer
294 
ExecutionServer(JavaVM * vm,xs::TestProcess * testProcess,deSocketFamily family,int port,RunMode runMode)295 ExecutionServer::ExecutionServer(JavaVM *vm, xs::TestProcess *testProcess, deSocketFamily family, int port,
296                                  RunMode runMode)
297     : xs::ExecutionServer(testProcess, family, port, runMode)
298     , m_vm(vm)
299 {
300 }
301 
createHandler(de::Socket * socket,const de::SocketAddress & clientAddress)302 xs::ConnectionHandler *ExecutionServer::createHandler(de::Socket *socket, const de::SocketAddress &clientAddress)
303 {
304     DE_UNREF(clientAddress);
305     return new ConnectionHandler(m_vm, this, socket);
306 }
307 
308 // ConnectionHandler
309 
ConnectionHandler(JavaVM * vm,xs::ExecutionServer * server,de::Socket * socket)310 ConnectionHandler::ConnectionHandler(JavaVM *vm, xs::ExecutionServer *server, de::Socket *socket)
311     : xs::ExecutionRequestHandler(server, socket)
312     , m_vm(vm)
313 {
314 }
315 
run(void)316 void ConnectionHandler::run(void)
317 {
318     JNIEnv *env = DE_NULL;
319     if (m_vm->AttachCurrentThread(&env, DE_NULL) != 0)
320     {
321         print("AttachCurrentThread() failed");
322         return;
323     }
324 
325     xs::ExecutionRequestHandler::run();
326 
327     if (m_vm->DetachCurrentThread() != 0)
328         print("DetachCurrentThread() failed");
329 }
330 
331 // ServerThread
332 
ServerThread(JavaVM * vm,xs::TestProcess * process,deSocketFamily family,int port)333 ServerThread::ServerThread(JavaVM *vm, xs::TestProcess *process, deSocketFamily family, int port)
334     : m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
335 {
336 }
337 
run(void)338 void ServerThread::run(void)
339 {
340     try
341     {
342         m_server.runServer();
343     }
344     catch (const std::exception &e)
345     {
346         die("ServerThread::run(): %s", e.what());
347     }
348 }
349 
stop(void)350 void ServerThread::stop(void)
351 {
352     m_server.stopServer();
353     join();
354 }
355 
356 // ExecService
357 
ExecService(JavaVM * vm,jobject context,int port,deSocketFamily family)358 ExecService::ExecService(JavaVM *vm, jobject context, int port, deSocketFamily family)
359     : m_process(vm, context)
360     , m_thread(vm, &m_process, family, port)
361 {
362 }
363 
~ExecService(void)364 ExecService::~ExecService(void)
365 {
366 }
367 
start(void)368 void ExecService::start(void)
369 {
370     m_thread.start();
371 }
372 
stop(void)373 void ExecService::stop(void)
374 {
375     m_thread.stop();
376 }
377 
378 } // namespace Android
379 } // namespace tcu
380