• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) do { checkJniException(env, __FILE__, __LINE__); TCU_CHECK_INTERNAL(EXPR); } while (deGetFalse())
58 
59 // TestProcess
60 
TestProcess(JavaVM * vm,jobject context)61 TestProcess::TestProcess (JavaVM* vm, jobject context)
62 	: m_vm					(vm)
63 	, m_remoteCls			(0)
64 	, m_remote				(0)
65 	, m_start				(0)
66 	, m_kill				(0)
67 	, m_isRunning			(0)
68 	, m_launchTime			(0)
69 	, m_lastQueryTime		(0)
70 	, m_lastRunningStatus	(false)
71 	, m_logReader			(xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
72 {
73 	DBG_PRINT(("TestProcess::TestProcess(%p, %p)", vm, context));
74 
75 	JNIEnv* env			= getCurrentThreadEnv();
76 	jobject	remote		= 0;
77 	jstring	logFileName	= 0;
78 
79 	try
80 	{
81 		jclass		remoteCls	= 0;
82 		jmethodID	ctorId		= 0;
83 
84 		remoteCls = env->FindClass("com/drawelements/deqp/testercore/RemoteAPI");
85 		JNI_CHECK(remoteCls);
86 
87 		// Acquire global reference to RemoteAPI class.
88 		m_remoteCls = reinterpret_cast<jclass>(env->NewGlobalRef(remoteCls));
89 		JNI_CHECK(m_remoteCls);
90 		env->DeleteLocalRef(remoteCls);
91 		remoteCls = 0;
92 
93 		ctorId = env->GetMethodID(m_remoteCls, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
94 		JNI_CHECK(ctorId);
95 
96 		logFileName = env->NewStringUTF(LOG_FILE_NAME);
97 		JNI_CHECK(logFileName);
98 
99 		// Create RemoteAPI instance.
100 		remote = env->NewObject(m_remoteCls, ctorId, context, logFileName);
101 		JNI_CHECK(remote);
102 
103 		env->DeleteLocalRef(logFileName);
104 		logFileName = 0;
105 
106 		// Acquire global reference to remote.
107 		m_remote = env->NewGlobalRef(remote);
108 		JNI_CHECK(m_remote);
109 		env->DeleteLocalRef(remote);
110 		remote = 0;
111 
112 		m_start	= env->GetMethodID(m_remoteCls, "start", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
113 		JNI_CHECK(m_start);
114 
115 		m_kill = env->GetMethodID(m_remoteCls, "kill", "()Z");
116 		JNI_CHECK(m_kill);
117 
118 		m_isRunning = env->GetMethodID(m_remoteCls, "isRunning", "()Z");
119 		JNI_CHECK(m_isRunning);
120 	}
121 	catch (...)
122 	{
123 		if (logFileName)
124 			env->DeleteLocalRef(logFileName);
125 		if (remote)
126 			env->DeleteLocalRef(remote);
127 		if (m_remoteCls)
128 			env->DeleteGlobalRef(reinterpret_cast<jobject>(m_remoteCls));
129 		if (m_remote)
130 			env->DeleteGlobalRef(m_remote);
131 		throw;
132 	}
133 }
134 
~TestProcess(void)135 TestProcess::~TestProcess (void)
136 {
137 	DBG_PRINT(("TestProcess::~TestProcess()"));
138 
139 	try
140 	{
141 		JNIEnv* env = getCurrentThreadEnv();
142 		env->DeleteGlobalRef(m_remote);
143 		env->DeleteGlobalRef(m_remoteCls);
144 	}
145 	catch (...)
146 	{
147 	}
148 }
149 
start(const char * name,const char * params,const char * workingDir,const char * caseList)150 void TestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
151 {
152 	DBG_PRINT(("TestProcess::start(%s, %s, %s, ...)", name, params, workingDir));
153 
154 	JNIEnv* env			= getCurrentThreadEnv();
155 	jstring	nameStr		= 0;
156 	jstring	paramsStr	= 0;
157 	jstring caseListStr	= 0;
158 
159 	DE_UNREF(workingDir);
160 
161 	// Remove old log file if such exists.
162 	if (deFileExists(LOG_FILE_NAME))
163 	{
164 		if (!deDeleteFile(LOG_FILE_NAME) || deFileExists(LOG_FILE_NAME))
165 			throw xs::TestProcessException(std::string("Failed to remove '") + LOG_FILE_NAME + "'");
166 	}
167 
168 	try
169 	{
170 		nameStr = env->NewStringUTF(name);
171 		JNI_CHECK(nameStr);
172 
173 		paramsStr = env->NewStringUTF(params);
174 		JNI_CHECK(paramsStr);
175 
176 		caseListStr = env->NewStringUTF(caseList);
177 		JNI_CHECK(caseListStr);
178 
179 		jboolean res = env->CallBooleanMethod(m_remote, m_start, nameStr, paramsStr, caseListStr);
180 		checkJniException(env, __FILE__, __LINE__);
181 
182 		if (res == JNI_FALSE)
183 			throw xs::TestProcessException("Failed to launch activity");
184 
185 		m_launchTime		= deGetMicroseconds();
186 		m_lastQueryTime		= m_launchTime;
187 		m_lastRunningStatus	= true;
188 	}
189 	catch (...)
190 	{
191 		if (nameStr)
192 			env->DeleteLocalRef(nameStr);
193 		if (paramsStr)
194 			env->DeleteLocalRef(paramsStr);
195 		if (caseListStr)
196 			env->DeleteLocalRef(caseListStr);
197 		throw;
198 	}
199 
200 	env->DeleteLocalRef(nameStr);
201 	env->DeleteLocalRef(paramsStr);
202 	env->DeleteLocalRef(caseListStr);
203 }
204 
terminate(void)205 void TestProcess::terminate (void)
206 {
207 	DBG_PRINT(("TestProcess::terminate()"));
208 
209 	JNIEnv*		env		= getCurrentThreadEnv();
210 	jboolean	res		= env->CallBooleanMethod(m_remote, m_kill);
211 	checkJniException(env, __FILE__, __LINE__);
212 	DE_UNREF(res); // Failure to kill process is ignored.
213 }
214 
cleanup(void)215 void TestProcess::cleanup (void)
216 {
217 	DBG_PRINT(("TestProcess::cleanup()"));
218 
219 	terminate();
220 	m_logReader.stop();
221 }
222 
isRunning(void)223 bool TestProcess::isRunning (void)
224 {
225 	deUint64 curTime = deGetMicroseconds();
226 
227 	// On Android process launch is asynchronous so we don't want to poll for process until after some time.
228 	if (curTime-m_launchTime < PROCESS_START_TIMEOUT ||
229 		curTime-m_lastQueryTime < PROCESS_QUERY_INTERVAL)
230 		return m_lastRunningStatus;
231 
232 	JNIEnv*		env		= getCurrentThreadEnv();
233 	jboolean	res		= env->CallBooleanMethod(m_remote, m_isRunning);
234 	checkJniException(env, __FILE__, __LINE__);
235 
236 	DBG_PRINT(("TestProcess::isRunning(): %s", res == JNI_TRUE ? "true" : "false"));
237 	m_lastQueryTime		= curTime;
238 	m_lastRunningStatus	= res == JNI_TRUE;
239 
240 	return m_lastRunningStatus;
241 }
242 
getCurrentThreadEnv(void)243 JNIEnv* TestProcess::getCurrentThreadEnv (void)
244 {
245 	JNIEnv* env = DE_NULL;
246 	jint	ret	= m_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
247 
248 	if (ret == JNI_OK)
249 		return env;
250 	else
251 		throw InternalError("GetEnv() failed");
252 }
253 
readTestLog(deUint8 * dst,int numBytes)254 int TestProcess::readTestLog (deUint8* dst, int numBytes)
255 {
256 	if (!m_logReader.isRunning())
257 	{
258 		if (deGetMicroseconds() - m_launchTime > xs::LOG_FILE_TIMEOUT*1000)
259 		{
260 			// Timeout, kill process.
261 			terminate();
262 			DBG_PRINT(("TestProcess:readTestLog(): Log file timeout occurred!"));
263 			return 0; // \todo [2013-08-13 pyry] Throw exception?
264 		}
265 
266 		if (!deFileExists(LOG_FILE_NAME))
267 			return 0;
268 
269 		// Start reader.
270 		m_logReader.start(LOG_FILE_NAME);
271 	}
272 
273 	DE_ASSERT(m_logReader.isRunning());
274 	return m_logReader.read(dst, numBytes);
275 }
276 
getExitCode(void) const277 int	TestProcess::getExitCode (void) const
278 {
279 	return 0;
280 }
281 
readInfoLog(deUint8 * dst,int numBytes)282 int TestProcess::readInfoLog (deUint8* dst, int numBytes)
283 {
284 	// \todo [2012-11-12 pyry] Read device log.
285 	DE_UNREF(dst && numBytes);
286 	return 0;
287 }
288 
289 // ExecutionServer
290 
ExecutionServer(JavaVM * vm,xs::TestProcess * testProcess,deSocketFamily family,int port,RunMode runMode)291 ExecutionServer::ExecutionServer (JavaVM* vm, xs::TestProcess* testProcess, deSocketFamily family, int port, RunMode runMode)
292 	: xs::ExecutionServer	(testProcess, family, port, runMode)
293 	, m_vm					(vm)
294 {
295 }
296 
createHandler(de::Socket * socket,const de::SocketAddress & clientAddress)297 xs::ConnectionHandler* ExecutionServer::createHandler (de::Socket* socket, const de::SocketAddress& clientAddress)
298 {
299 	DE_UNREF(clientAddress);
300 	return new ConnectionHandler(m_vm, this, socket);
301 }
302 
303 // ConnectionHandler
304 
ConnectionHandler(JavaVM * vm,xs::ExecutionServer * server,de::Socket * socket)305 ConnectionHandler::ConnectionHandler (JavaVM* vm, xs::ExecutionServer* server, de::Socket* socket)
306 	: xs::ExecutionRequestHandler	(server, socket)
307 	, m_vm							(vm)
308 {
309 }
310 
run(void)311 void ConnectionHandler::run (void)
312 {
313 	JNIEnv* env = DE_NULL;
314 	if (m_vm->AttachCurrentThread(&env, DE_NULL) != 0)
315 	{
316 		print("AttachCurrentThread() failed");
317 		return;
318 	}
319 
320 	xs::ExecutionRequestHandler::run();
321 
322 	if (m_vm->DetachCurrentThread() != 0)
323 		print("DetachCurrentThread() failed");
324 }
325 
326 // ServerThread
327 
ServerThread(JavaVM * vm,xs::TestProcess * process,deSocketFamily family,int port)328 ServerThread::ServerThread (JavaVM* vm, xs::TestProcess* process, deSocketFamily family, int port)
329 	: m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
330 {
331 }
332 
run(void)333 void ServerThread::run (void)
334 {
335 	try
336 	{
337 		m_server.runServer();
338 	}
339 	catch (const std::exception& e)
340 	{
341 		die("ServerThread::run(): %s", e.what());
342 	}
343 }
344 
stop(void)345 void ServerThread::stop (void)
346 {
347 	m_server.stopServer();
348 	join();
349 }
350 
351 // ExecService
352 
ExecService(JavaVM * vm,jobject context,int port,deSocketFamily family)353 ExecService::ExecService (JavaVM* vm, jobject context, int port, deSocketFamily family)
354 	: m_process		(vm, context)
355 	, m_thread		(vm, &m_process, family, port)
356 {
357 }
358 
~ExecService(void)359 ExecService::~ExecService (void)
360 {
361 }
362 
start(void)363 void ExecService::start (void)
364 {
365 	m_thread.start();
366 }
367 
stop(void)368 void ExecService::stop (void)
369 {
370 	m_thread.stop();
371 }
372 
373 } // Android
374 } // tcu
375