1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Execution Server 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 TestProcess implementation for Unix-like systems. 22 *//*--------------------------------------------------------------------*/ 23 24#include "xsPosixTestProcess.hpp" 25#include "deFilePath.hpp" 26#include "deClock.h" 27 28#include <string.h> 29#include <stdio.h> 30 31using std::string; 32using std::vector; 33 34namespace xs 35{ 36 37namespace posix 38{ 39 40CaseListWriter::CaseListWriter (void) 41 : m_file (DE_NULL) 42 , m_run (false) 43{ 44} 45 46CaseListWriter::~CaseListWriter (void) 47{ 48} 49 50void CaseListWriter::start (const char* caseList, deFile* dst) 51{ 52 DE_ASSERT(!isStarted()); 53 m_file = dst; 54 m_run = true; 55 56 int caseListSize = (int)strlen(caseList)+1; 57 m_caseList.resize(caseListSize); 58 std::copy(caseList, caseList+caseListSize, m_caseList.begin()); 59 60 // Set to non-blocking mode. 61 if (!deFile_setFlags(m_file, DE_FILE_NONBLOCKING)) 62 XS_FAIL("Failed to set non-blocking mode"); 63 64 de::Thread::start(); 65} 66 67void CaseListWriter::run (void) 68{ 69 deInt64 pos = 0; 70 71 while (m_run && pos < (deInt64)m_caseList.size()) 72 { 73 deInt64 numWritten = 0; 74 deFileResult result = deFile_write(m_file, &m_caseList[0] + pos, m_caseList.size()-pos, &numWritten); 75 76 if (result == DE_FILERESULT_SUCCESS) 77 pos += numWritten; 78 else if (result == DE_FILERESULT_WOULD_BLOCK) 79 deSleep(1); // Yield. 80 else 81 break; // Error. 82 } 83} 84 85void CaseListWriter::stop (void) 86{ 87 if (!isStarted()) 88 return; // Nothing to do. 89 90 m_run = false; 91 92 // Join thread. 93 join(); 94 95 m_file = DE_NULL; 96} 97 98PipeReader::PipeReader (ThreadedByteBuffer* dst) 99 : m_file (DE_NULL) 100 , m_buf (dst) 101{ 102} 103 104PipeReader::~PipeReader (void) 105{ 106} 107 108void PipeReader::start (deFile* file) 109{ 110 DE_ASSERT(!isStarted()); 111 112 // Set to non-blocking mode. 113 if (!deFile_setFlags(file, DE_FILE_NONBLOCKING)) 114 XS_FAIL("Failed to set non-blocking mode"); 115 116 m_file = file; 117 118 de::Thread::start(); 119} 120 121void PipeReader::run (void) 122{ 123 std::vector<deUint8> tmpBuf (FILEREADER_TMP_BUFFER_SIZE); 124 deInt64 numRead = 0; 125 126 while (!m_buf->isCanceled()) 127 { 128 deFileResult result = deFile_read(m_file, &tmpBuf[0], (deInt64)tmpBuf.size(), &numRead); 129 130 if (result == DE_FILERESULT_SUCCESS) 131 { 132 // Write to buffer. 133 try 134 { 135 m_buf->write((int)numRead, &tmpBuf[0]); 136 m_buf->flush(); 137 } 138 catch (const ThreadedByteBuffer::CanceledException&) 139 { 140 // Canceled. 141 break; 142 } 143 } 144 else if (result == DE_FILERESULT_END_OF_FILE || 145 result == DE_FILERESULT_WOULD_BLOCK) 146 { 147 // Wait for more data. 148 deSleep(FILEREADER_IDLE_SLEEP); 149 } 150 else 151 break; // Error. 152 } 153} 154 155void PipeReader::stop (void) 156{ 157 if (!isStarted()) 158 return; // Nothing to do. 159 160 // Buffer must be in canceled state or otherwise stopping reader might block. 161 DE_ASSERT(m_buf->isCanceled()); 162 163 // Join thread. 164 join(); 165 166 m_file = DE_NULL; 167} 168 169} // unix 170 171PosixTestProcess::PosixTestProcess (void) 172 : m_process (DE_NULL) 173 , m_processStartTime (0) 174 , m_infoBuffer (INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS) 175 , m_stdOutReader (&m_infoBuffer) 176 , m_stdErrReader (&m_infoBuffer) 177 , m_logReader (LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS) 178{ 179} 180 181PosixTestProcess::~PosixTestProcess (void) 182{ 183 delete m_process; 184} 185 186void PosixTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList) 187{ 188 bool hasCaseList = strlen(caseList) > 0; 189 190 XS_CHECK(!m_process); 191 192 de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa"); 193 m_logFileName = logFilePath.getPath(); 194 195 // Remove old file if such exists. 196 if (deFileExists(m_logFileName.c_str())) 197 { 198 if (!deDeleteFile(m_logFileName.c_str()) || deFileExists(m_logFileName.c_str())) 199 throw TestProcessException(string("Failed to remove '") + m_logFileName + "'"); 200 } 201 202 // Construct command line. 203 string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).normalize().getPath(); 204 cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName(); 205 206 if (hasCaseList) 207 cmdLine += " --deqp-stdin-caselist"; 208 209 if (strlen(params) > 0) 210 cmdLine += string(" ") + params; 211 212 DE_ASSERT(!m_process); 213 m_process = new de::Process(); 214 215 try 216 { 217 m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL); 218 } 219 catch (const de::ProcessError& e) 220 { 221 delete m_process; 222 m_process = DE_NULL; 223 throw TestProcessException(e.what()); 224 } 225 226 m_processStartTime = deGetMicroseconds(); 227 228 // Create stdout & stderr readers. 229 if (m_process->getStdOut()) 230 m_stdOutReader.start(m_process->getStdOut()); 231 232 if (m_process->getStdErr()) 233 m_stdErrReader.start(m_process->getStdErr()); 234 235 // Start case list writer. 236 if (hasCaseList) 237 { 238 deFile* dst = m_process->getStdIn(); 239 if (dst) 240 m_caseListWriter.start(caseList, dst); 241 else 242 { 243 cleanup(); 244 throw TestProcessException("Failed to write case list"); 245 } 246 } 247} 248 249void PosixTestProcess::terminate (void) 250{ 251 if (m_process) 252 { 253 try 254 { 255 m_process->kill(); 256 } 257 catch (const std::exception& e) 258 { 259 printf("PosixTestProcess::terminate(): Failed to kill process: %s\n", e.what()); 260 } 261 } 262} 263 264void PosixTestProcess::cleanup (void) 265{ 266 m_caseListWriter.stop(); 267 m_logReader.stop(); 268 269 // \note Info buffer must be canceled before stopping pipe readers. 270 m_infoBuffer.cancel(); 271 272 m_stdErrReader.stop(); 273 m_stdOutReader.stop(); 274 275 // Reset info buffer. 276 m_infoBuffer.clear(); 277 278 if (m_process) 279 { 280 try 281 { 282 if (m_process->isRunning()) 283 { 284 m_process->kill(); 285 m_process->waitForFinish(); 286 } 287 } 288 catch (const de::ProcessError& e) 289 { 290 printf("PosixTestProcess::stop(): Failed to kill process: %s\n", e.what()); 291 } 292 293 delete m_process; 294 m_process = DE_NULL; 295 } 296} 297 298bool PosixTestProcess::isRunning (void) 299{ 300 if (m_process) 301 return m_process->isRunning(); 302 else 303 return false; 304} 305 306int PosixTestProcess::getExitCode (void) const 307{ 308 if (m_process) 309 return m_process->getExitCode(); 310 else 311 return -1; 312} 313 314int PosixTestProcess::readTestLog (deUint8* dst, int numBytes) 315{ 316 if (!m_logReader.isRunning()) 317 { 318 if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000) 319 { 320 // Timeout, kill process. 321 terminate(); 322 return 0; // \todo [2013-08-13 pyry] Throw exception? 323 } 324 325 if (!deFileExists(m_logFileName.c_str())) 326 return 0; 327 328 // Start reader. 329 m_logReader.start(m_logFileName.c_str()); 330 } 331 332 DE_ASSERT(m_logReader.isRunning()); 333 return m_logReader.read(dst, numBytes); 334} 335 336} // xs 337