You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
8.7 KiB
375 lines
8.7 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|