blob: 0766b38e799057ebb49d52af9d344847bd118d90 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Android ExecServer.
*//*--------------------------------------------------------------------*/
#include "tcuAndroidExecService.hpp"
#include "deFile.h"
#include "deClock.h"
#if 0
# define DBG_PRINT(ARGS) print ARGS
#else
# define DBG_PRINT(ARGS)
#endif
namespace tcu
{
namespace Android
{
static const char* LOG_FILE_NAME = "/sdcard/dEQP-log.qpa";
enum
{
PROCESS_START_TIMEOUT = 5000*1000, //!< Timeout in usec.
PROCESS_QUERY_INTERVAL = 1000*1000 //!< Running query interval limit in usec.
};
static void checkJniException (JNIEnv* env, const char* file, int line)
{
if (env->ExceptionOccurred())
{
env->ExceptionDescribe();
env->ExceptionClear();
throw InternalError("JNI Exception", DE_NULL, file, line);
}
}
#define JNI_CHECK(EXPR) do { checkJniException(env, __FILE__, __LINE__); TCU_CHECK_INTERNAL(EXPR); } while (deGetFalse())
// TestProcess
TestProcess::TestProcess (JavaVM* vm, jobject context)
: m_vm (vm)
, m_remoteCls (0)
, m_remote (0)
, m_start (0)
, m_kill (0)
, m_isRunning (0)
, m_launchTime (0)
, m_lastQueryTime (0)
, m_lastRunningStatus (false)
, m_logReader (xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
{
DBG_PRINT(("TestProcess::TestProcess(%p, %p)", vm, context));
JNIEnv* env = getCurrentThreadEnv();
jobject remote = 0;
jstring logFileName = 0;
try
{
jclass remoteCls = 0;
jmethodID ctorId = 0;
remoteCls = env->FindClass("com/drawelements/deqp/testercore/RemoteAPI");
JNI_CHECK(remoteCls);
// Acquire global reference to RemoteAPI class.
m_remoteCls = reinterpret_cast<jclass>(env->NewGlobalRef(remoteCls));
JNI_CHECK(m_remoteCls);
env->DeleteLocalRef(remoteCls);
remoteCls = 0;
ctorId = env->GetMethodID(m_remoteCls, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
JNI_CHECK(ctorId);
logFileName = env->NewStringUTF(LOG_FILE_NAME);
JNI_CHECK(logFileName);
// Create RemoteAPI instance.
remote = env->NewObject(m_remoteCls, ctorId, context, logFileName);
JNI_CHECK(remote);
env->DeleteLocalRef(logFileName);
logFileName = 0;
// Acquire global reference to remote.
m_remote = env->NewGlobalRef(remote);
JNI_CHECK(m_remote);
env->DeleteLocalRef(remote);
remote = 0;
m_start = env->GetMethodID(m_remoteCls, "start", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
JNI_CHECK(m_start);
m_kill = env->GetMethodID(m_remoteCls, "kill", "()Z");
JNI_CHECK(m_kill);
m_isRunning = env->GetMethodID(m_remoteCls, "isRunning", "()Z");
JNI_CHECK(m_isRunning);
}
catch (...)
{
if (logFileName)
env->DeleteLocalRef(logFileName);
if (remote)
env->DeleteLocalRef(remote);
if (m_remoteCls)
env->DeleteGlobalRef(reinterpret_cast<jobject>(m_remoteCls));
if (m_remote)
env->DeleteGlobalRef(m_remote);
throw;
}
}
TestProcess::~TestProcess (void)
{
DBG_PRINT(("TestProcess::~TestProcess()"));
try
{
JNIEnv* env = getCurrentThreadEnv();
env->DeleteGlobalRef(m_remote);
env->DeleteGlobalRef(m_remoteCls);
}
catch (...)
{
}
}
void TestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
{
DBG_PRINT(("TestProcess::start(%s, %s, %s, ...)", name, params, workingDir));
JNIEnv* env = getCurrentThreadEnv();
jstring nameStr = 0;
jstring paramsStr = 0;
jstring caseListStr = 0;
DE_UNREF(workingDir);
// Remove old log file if such exists.
if (deFileExists(LOG_FILE_NAME))
{
if (!deDeleteFile(LOG_FILE_NAME) || deFileExists(LOG_FILE_NAME))
throw xs::TestProcessException(std::string("Failed to remove '") + LOG_FILE_NAME + "'");
}
try
{
nameStr = env->NewStringUTF(name);
JNI_CHECK(nameStr);
paramsStr = env->NewStringUTF(params);
JNI_CHECK(paramsStr);
caseListStr = env->NewStringUTF(caseList);
JNI_CHECK(caseListStr);
jboolean res = env->CallBooleanMethod(m_remote, m_start, nameStr, paramsStr, caseListStr);
checkJniException(env, __FILE__, __LINE__);
if (res == JNI_FALSE)
throw xs::TestProcessException("Failed to launch activity");
m_launchTime = deGetMicroseconds();
m_lastQueryTime = m_launchTime;
m_lastRunningStatus = true;
}
catch (...)
{
if (nameStr)
env->DeleteLocalRef(nameStr);
if (paramsStr)
env->DeleteLocalRef(paramsStr);
if (caseListStr)
env->DeleteLocalRef(caseListStr);
throw;
}
env->DeleteLocalRef(nameStr);
env->DeleteLocalRef(paramsStr);
env->DeleteLocalRef(caseListStr);
}
void TestProcess::terminate (void)
{
DBG_PRINT(("TestProcess::terminate()"));
JNIEnv* env = getCurrentThreadEnv();
jboolean res = env->CallBooleanMethod(m_remote, m_kill);
checkJniException(env, __FILE__, __LINE__);
DE_UNREF(res); // Failure to kill process is ignored.
}
void TestProcess::cleanup (void)
{
DBG_PRINT(("TestProcess::cleanup()"));
terminate();
m_logReader.stop();
}
bool TestProcess::isRunning (void)
{
deUint64 curTime = deGetMicroseconds();
// On Android process launch is asynchronous so we don't want to poll for process until after some time.
if (curTime-m_launchTime < PROCESS_START_TIMEOUT ||
curTime-m_lastQueryTime < PROCESS_QUERY_INTERVAL)
return m_lastRunningStatus;
JNIEnv* env = getCurrentThreadEnv();
jboolean res = env->CallBooleanMethod(m_remote, m_isRunning);
checkJniException(env, __FILE__, __LINE__);
DBG_PRINT(("TestProcess::isRunning(): %s", res == JNI_TRUE ? "true" : "false"));
m_lastQueryTime = curTime;
m_lastRunningStatus = res == JNI_TRUE;
return m_lastRunningStatus;
}
JNIEnv* TestProcess::getCurrentThreadEnv (void)
{
JNIEnv* env = DE_NULL;
jint ret = m_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (ret == JNI_OK)
return env;
else
throw InternalError("GetEnv() failed");
}
int TestProcess::readTestLog (deUint8* dst, int numBytes)
{
if (!m_logReader.isRunning())
{
if (deGetMicroseconds() - m_launchTime > xs::LOG_FILE_TIMEOUT*1000)
{
// Timeout, kill process.
terminate();
DBG_PRINT(("TestProcess:readTestLog(): Log file timeout occurred!"));
return 0; // \todo [2013-08-13 pyry] Throw exception?
}
if (!deFileExists(LOG_FILE_NAME))
return 0;
// Start reader.
m_logReader.start(LOG_FILE_NAME);
}
DE_ASSERT(m_logReader.isRunning());
return m_logReader.read(dst, numBytes);
}
int TestProcess::getExitCode (void) const
{
return 0;
}
int TestProcess::readInfoLog (deUint8* dst, int numBytes)
{
// \todo [2012-11-12 pyry] Read device log.
DE_UNREF(dst && numBytes);
return 0;
}
// ExecutionServer
ExecutionServer::ExecutionServer (JavaVM* vm, xs::TestProcess* testProcess, deSocketFamily family, int port, RunMode runMode)
: xs::ExecutionServer (testProcess, family, port, runMode)
, m_vm (vm)
{
}
xs::ConnectionHandler* ExecutionServer::createHandler (de::Socket* socket, const de::SocketAddress& clientAddress)
{
DE_UNREF(clientAddress);
return new ConnectionHandler(m_vm, this, socket);
}
// ConnectionHandler
ConnectionHandler::ConnectionHandler (JavaVM* vm, xs::ExecutionServer* server, de::Socket* socket)
: xs::ExecutionRequestHandler (server, socket)
, m_vm (vm)
{
}
void ConnectionHandler::run (void)
{
JNIEnv* env = DE_NULL;
if (m_vm->AttachCurrentThread(&env, DE_NULL) != 0)
{
print("AttachCurrentThread() failed");
return;
}
xs::ExecutionRequestHandler::run();
if (m_vm->DetachCurrentThread() != 0)
print("DetachCurrentThread() failed");
}
// ServerThread
ServerThread::ServerThread (JavaVM* vm, xs::TestProcess* process, deSocketFamily family, int port)
: m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
{
}
void ServerThread::run (void)
{
try
{
m_server.runServer();
}
catch (const std::exception& e)
{
die("ServerThread::run(): %s", e.what());
}
}
void ServerThread::stop (void)
{
m_server.stopServer();
join();
}
// ExecService
ExecService::ExecService (JavaVM* vm, jobject context, int port, deSocketFamily family)
: m_process (vm, context)
, m_thread (vm, &m_process, family, port)
{
}
ExecService::~ExecService (void)
{
}
void ExecService::start (void)
{
m_thread.start();
}
void ExecService::stop (void)
{
m_thread.stop();
}
} // Android
} // tcu