blob: c560f8062a77deffeb40df0da59c7fb9369ef20e [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 (false)
// 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)
{
uint64_t 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(uint8_t *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(uint8_t *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();
}
} // namespace Android
} // namespace tcu