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 31 using std::string; 32 using std::vector; 33 34 namespace xs 35 { 36 37 namespace posix 38 { 39 40 CaseListWriter::CaseListWriter (void) 41 : m_file (DE_NULL) 42 , m_run (false) 43 { 44 } 45 46 CaseListWriter::~CaseListWriter (void) 47 { 48 } 49 50 void 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 67 void 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 85 void 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 98 PipeReader::PipeReader (ThreadedByteBuffer* dst) 99 : m_file (DE_NULL) 100 , m_buf (dst) 101 { 102 } 103 104 PipeReader::~PipeReader (void) 105 { 106 } 107 108 void 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 121 void 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 155 void 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 171 PosixTestProcess::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 181 PosixTestProcess::~PosixTestProcess (void) 182 { 183 delete m_process; 184 } 185 186 void 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 249 void 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 264 void 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 298 bool PosixTestProcess::isRunning (void) 299 { 300 if (m_process) 301 return m_process->isRunning(); 302 else 303 return false; 304 } 305 306 int PosixTestProcess::getExitCode (void) const 307 { 308 if (m_process) 309 return m_process->getExitCode(); 310 else 311 return -1; 312 } 313 314 int 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