• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
41 	: m_file	(DE_NULL)
42 	, m_run		(false)
43 {
44 }
45 
~CaseListWriter(void)46 CaseListWriter::~CaseListWriter (void)
47 {
48 }
49 
start(const char * caseList,deFile * dst)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 
run(void)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 
stop(void)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 
PipeReader(ThreadedByteBuffer * dst)98 PipeReader::PipeReader (ThreadedByteBuffer* dst)
99 	: m_file	(DE_NULL)
100 	, m_buf		(dst)
101 {
102 }
103 
~PipeReader(void)104 PipeReader::~PipeReader (void)
105 {
106 }
107 
start(deFile * file)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 
run(void)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 
stop(void)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 
PosixTestProcess(void)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 
~PosixTestProcess(void)181 PosixTestProcess::~PosixTestProcess (void)
182 {
183 	delete m_process;
184 }
185 
start(const char * name,const char * params,const char * workingDir,const char * caseList)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).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 
terminate(void)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 
cleanup(void)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 
isRunning(void)298 bool PosixTestProcess::isRunning (void)
299 {
300 	if (m_process)
301 		return m_process->isRunning();
302 	else
303 		return false;
304 }
305 
getExitCode(void) const306 int PosixTestProcess::getExitCode (void) const
307 {
308 	if (m_process)
309 		return m_process->getExitCode();
310 	else
311 		return -1;
312 }
313 
readTestLog(deUint8 * dst,int numBytes)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