/*------------------------------------------------------------------------- * drawElements Quality Program Execution Server * --------------------------------------------- * * 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 TestProcess implementation for Unix-like systems. *//*--------------------------------------------------------------------*/ #include "xsPosixTestProcess.hpp" #include "deFilePath.hpp" #include "deClock.h" #include #include using std::string; using std::vector; namespace xs { namespace posix { CaseListWriter::CaseListWriter (void) : m_file (DE_NULL) , m_run (false) { } CaseListWriter::~CaseListWriter (void) { } void CaseListWriter::start (const char* caseList, deFile* dst) { DE_ASSERT(!isStarted()); m_file = dst; m_run = true; int caseListSize = (int)strlen(caseList)+1; m_caseList.resize(caseListSize); std::copy(caseList, caseList+caseListSize, m_caseList.begin()); // Set to non-blocking mode. if (!deFile_setFlags(m_file, DE_FILE_NONBLOCKING)) XS_FAIL("Failed to set non-blocking mode"); de::Thread::start(); } void CaseListWriter::run (void) { deInt64 pos = 0; while (m_run && pos < (deInt64)m_caseList.size()) { deInt64 numWritten = 0; deFileResult result = deFile_write(m_file, &m_caseList[0] + pos, m_caseList.size()-pos, &numWritten); if (result == DE_FILERESULT_SUCCESS) pos += numWritten; else if (result == DE_FILERESULT_WOULD_BLOCK) deSleep(1); // Yield. else break; // Error. } } void CaseListWriter::stop (void) { if (!isStarted()) return; // Nothing to do. m_run = false; // Join thread. join(); m_file = DE_NULL; } PipeReader::PipeReader (ThreadedByteBuffer* dst) : m_file (DE_NULL) , m_buf (dst) { } PipeReader::~PipeReader (void) { } void PipeReader::start (deFile* file) { DE_ASSERT(!isStarted()); // Set to non-blocking mode. if (!deFile_setFlags(file, DE_FILE_NONBLOCKING)) XS_FAIL("Failed to set non-blocking mode"); m_file = file; de::Thread::start(); } void PipeReader::run (void) { std::vector tmpBuf (FILEREADER_TMP_BUFFER_SIZE); deInt64 numRead = 0; while (!m_buf->isCanceled()) { deFileResult result = deFile_read(m_file, &tmpBuf[0], (deInt64)tmpBuf.size(), &numRead); if (result == DE_FILERESULT_SUCCESS) { // Write to buffer. try { m_buf->write((int)numRead, &tmpBuf[0]); m_buf->flush(); } catch (const ThreadedByteBuffer::CanceledException&) { // Canceled. break; } } else if (result == DE_FILERESULT_END_OF_FILE || result == DE_FILERESULT_WOULD_BLOCK) { // Wait for more data. deSleep(FILEREADER_IDLE_SLEEP); } else break; // Error. } } void PipeReader::stop (void) { if (!isStarted()) return; // Nothing to do. // Buffer must be in canceled state or otherwise stopping reader might block. DE_ASSERT(m_buf->isCanceled()); // Join thread. join(); m_file = DE_NULL; } } // unix PosixTestProcess::PosixTestProcess (void) : m_process (DE_NULL) , m_processStartTime (0) , m_infoBuffer (INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS) , m_stdOutReader (&m_infoBuffer) , m_stdErrReader (&m_infoBuffer) , m_logReader (LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS) { } PosixTestProcess::~PosixTestProcess (void) { delete m_process; } void PosixTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList) { bool hasCaseList = strlen(caseList) > 0; XS_CHECK(!m_process); de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa"); m_logFileName = logFilePath.getPath(); // Remove old file if such exists. if (deFileExists(m_logFileName.c_str())) { if (!deDeleteFile(m_logFileName.c_str()) || deFileExists(m_logFileName.c_str())) throw TestProcessException(string("Failed to remove '") + m_logFileName + "'"); } // Construct command line. string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).getPath(); cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName(); if (hasCaseList) cmdLine += " --deqp-stdin-caselist"; if (strlen(params) > 0) cmdLine += string(" ") + params; DE_ASSERT(!m_process); m_process = new de::Process(); try { m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL); } catch (const de::ProcessError& e) { delete m_process; m_process = DE_NULL; throw TestProcessException(e.what()); } m_processStartTime = deGetMicroseconds(); // Create stdout & stderr readers. if (m_process->getStdOut()) m_stdOutReader.start(m_process->getStdOut()); if (m_process->getStdErr()) m_stdErrReader.start(m_process->getStdErr()); // Start case list writer. if (hasCaseList) { deFile* dst = m_process->getStdIn(); if (dst) m_caseListWriter.start(caseList, dst); else { cleanup(); throw TestProcessException("Failed to write case list"); } } } void PosixTestProcess::terminate (void) { if (m_process) { try { m_process->kill(); } catch (const std::exception& e) { printf("PosixTestProcess::terminate(): Failed to kill process: %s\n", e.what()); } } } void PosixTestProcess::cleanup (void) { m_caseListWriter.stop(); m_logReader.stop(); // \note Info buffer must be canceled before stopping pipe readers. m_infoBuffer.cancel(); m_stdErrReader.stop(); m_stdOutReader.stop(); // Reset info buffer. m_infoBuffer.clear(); if (m_process) { try { if (m_process->isRunning()) { m_process->kill(); m_process->waitForFinish(); } } catch (const de::ProcessError& e) { printf("PosixTestProcess::stop(): Failed to kill process: %s\n", e.what()); } delete m_process; m_process = DE_NULL; } } bool PosixTestProcess::isRunning (void) { if (m_process) return m_process->isRunning(); else return false; } int PosixTestProcess::getExitCode (void) const { if (m_process) return m_process->getExitCode(); else return -1; } int PosixTestProcess::readTestLog (deUint8* dst, int numBytes) { if (!m_logReader.isRunning()) { if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000) { // Timeout, kill process. terminate(); return 0; // \todo [2013-08-13 pyry] Throw exception? } if (!deFileExists(m_logFileName.c_str())) return 0; // Start reader. m_logReader.start(m_logFileName.c_str()); } DE_ASSERT(m_logReader.isRunning()); return m_logReader.read(dst, numBytes); } } // xs