xref: /aosp_15_r20/external/deqp/execserver/xsPosixTestProcess.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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