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