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 Win32. 22 *//*--------------------------------------------------------------------*/ 23 24#include "xsWin32TestProcess.hpp" 25#include "deFilePath.hpp" 26#include "deString.h" 27#include "deMemory.h" 28#include "deClock.h" 29#include "deFile.h" 30 31#include <sstream> 32#include <string.h> 33 34using std::string; 35using std::vector; 36 37namespace xs 38{ 39 40enum 41{ 42 MAX_OLD_LOGFILE_DELETE_ATTEMPTS = 20, //!< How many times execserver tries to delete old log file 43 LOGFILE_DELETE_SLEEP_MS = 50 //!< Sleep time (in ms) between log file delete attempts 44}; 45 46namespace win32 47{ 48 49// Error 50 51static std::string formatErrMsg (DWORD error, const char* msg) 52{ 53 std::ostringstream str; 54 LPSTR msgBuf; 55 56#if defined(UNICODE) 57# error Unicode not supported. 58#endif 59 60 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 61 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msgBuf, 0, DE_NULL) > 0) 62 str << msg << ", error " << error << ": " << msgBuf; 63 else 64 str << msg << ", error " << error; 65 66 return str.str(); 67} 68 69Error::Error (DWORD error, const char* msg) 70 : std::runtime_error(formatErrMsg(error, msg)) 71 , m_error (error) 72{ 73} 74 75// Event 76 77Event::Event (bool manualReset, bool initialState) 78 : m_handle(0) 79{ 80 m_handle = CreateEvent(NULL, manualReset ? TRUE : FALSE, initialState ? TRUE : FALSE, NULL); 81 if (!m_handle) 82 throw Error(GetLastError(), "CreateEvent() failed"); 83} 84 85Event::~Event (void) 86{ 87 CloseHandle(m_handle); 88} 89 90void Event::setSignaled (void) 91{ 92 if (!SetEvent(m_handle)) 93 throw Error(GetLastError(), "SetEvent() failed"); 94} 95 96void Event::reset (void) 97{ 98 if (!ResetEvent(m_handle)) 99 throw Error(GetLastError(), "ResetEvent() failed"); 100} 101 102// CaseListWriter 103 104CaseListWriter::CaseListWriter (void) 105 : m_dst (INVALID_HANDLE_VALUE) 106 , m_cancelEvent (true, false) 107{ 108} 109 110CaseListWriter::~CaseListWriter (void) 111{ 112} 113 114void CaseListWriter::start (const char* caseList, HANDLE dst) 115{ 116 DE_ASSERT(!isStarted()); 117 118 m_dst = dst; 119 120 int caseListSize = (int)strlen(caseList)+1; 121 m_caseList.resize(caseListSize); 122 std::copy(caseList, caseList+caseListSize, m_caseList.begin()); 123 124 de::Thread::start(); 125} 126 127void CaseListWriter::run (void) 128{ 129 try 130 { 131 Event ioEvent (true, false); // Manual reset, non-signaled state. 132 HANDLE waitHandles[] = { ioEvent.getHandle(), m_cancelEvent.getHandle() }; 133 OVERLAPPED overlapped; 134 int curPos = 0; 135 136 deMemset(&overlapped, 0, sizeof(overlapped)); 137 overlapped.hEvent = ioEvent.getHandle(); 138 139 while (curPos < (int)m_caseList.size()) 140 { 141 const int maxWriteSize = 4096; 142 const int numToWrite = de::min(maxWriteSize, (int)m_caseList.size() - curPos); 143 DWORD waitRes = 0; 144 145 if (!WriteFile(m_dst, &m_caseList[curPos], (DWORD)numToWrite, NULL, &overlapped)) 146 { 147 DWORD err = GetLastError(); 148 if (err != ERROR_IO_PENDING) 149 throw Error(err, "WriteFile() failed"); 150 } 151 152 waitRes = WaitForMultipleObjects(DE_LENGTH_OF_ARRAY(waitHandles), &waitHandles[0], FALSE, INFINITE); 153 154 if (waitRes == WAIT_OBJECT_0) 155 { 156 DWORD numBytesWritten = 0; 157 158 // \note GetOverlappedResult() will fail with ERROR_IO_INCOMPLETE if IO event is not complete (should be). 159 if (!GetOverlappedResult(m_dst, &overlapped, &numBytesWritten, FALSE)) 160 throw Error(GetLastError(), "GetOverlappedResult() failed"); 161 162 if (numBytesWritten == 0) 163 throw Error(GetLastError(), "Writing to pipe failed (pipe closed?)"); 164 165 curPos += (int)numBytesWritten; 166 } 167 else if (waitRes == WAIT_OBJECT_0 + 1) 168 { 169 // Cancel. 170 if (!CancelIo(m_dst)) 171 throw Error(GetLastError(), "CancelIo() failed"); 172 break; 173 } 174 else 175 throw Error(GetLastError(), "WaitForMultipleObjects() failed"); 176 } 177 } 178 catch (const std::exception& e) 179 { 180 // \todo [2013-08-13 pyry] What to do about this? 181 printf("win32::CaseListWriter::run(): %s\n", e.what()); 182 } 183} 184 185void CaseListWriter::stop (void) 186{ 187 if (!isStarted()) 188 return; // Nothing to do. 189 190 m_cancelEvent.setSignaled(); 191 192 // Join thread. 193 join(); 194 195 m_cancelEvent.reset(); 196 197 m_dst = INVALID_HANDLE_VALUE; 198} 199 200// FileReader 201 202FileReader::FileReader (ThreadedByteBuffer* dst) 203 : m_dstBuf (dst) 204 , m_handle (INVALID_HANDLE_VALUE) 205 , m_cancelEvent (false, false) 206{ 207} 208 209FileReader::~FileReader (void) 210{ 211} 212 213void FileReader::start (HANDLE file) 214{ 215 DE_ASSERT(!isStarted()); 216 217 m_handle = file; 218 219 de::Thread::start(); 220} 221 222void FileReader::run (void) 223{ 224 try 225 { 226 Event ioEvent (true, false); // Manual reset, not signaled state. 227 HANDLE waitHandles[] = { ioEvent.getHandle(), m_cancelEvent.getHandle() }; 228 OVERLAPPED overlapped; 229 std::vector<deUint8> tmpBuf (FILEREADER_TMP_BUFFER_SIZE); 230 deUint64 offset = 0; // Overlapped IO requires manual offset keeping. 231 232 deMemset(&overlapped, 0, sizeof(overlapped)); 233 overlapped.hEvent = ioEvent.getHandle(); 234 235 for (;;) 236 { 237 DWORD numBytesRead = 0; 238 DWORD waitRes; 239 240 overlapped.Offset = (DWORD)(offset & 0xffffffffu); 241 overlapped.OffsetHigh = (DWORD)(offset >> 32); 242 243 if (!ReadFile(m_handle, &tmpBuf[0], (DWORD)tmpBuf.size(), NULL, &overlapped)) 244 { 245 DWORD err = GetLastError(); 246 247 if (err == ERROR_BROKEN_PIPE) 248 break; 249 else if (err == ERROR_HANDLE_EOF) 250 { 251 if (m_dstBuf->isCanceled()) 252 break; 253 254 deSleep(FILEREADER_IDLE_SLEEP); 255 256 if (m_dstBuf->isCanceled()) 257 break; 258 else 259 continue; 260 } 261 else if (err != ERROR_IO_PENDING) 262 throw Error(err, "ReadFile() failed"); 263 } 264 265 waitRes = WaitForMultipleObjects(DE_LENGTH_OF_ARRAY(waitHandles), &waitHandles[0], FALSE, INFINITE); 266 267 if (waitRes == WAIT_OBJECT_0) 268 { 269 // \note GetOverlappedResult() will fail with ERROR_IO_INCOMPLETE if IO event is not complete (should be). 270 if (!GetOverlappedResult(m_handle, &overlapped, &numBytesRead, FALSE)) 271 { 272 DWORD err = GetLastError(); 273 274 if (err == ERROR_HANDLE_EOF) 275 { 276 // End of file - for now. 277 // \note Should check for end of buffer here, or otherwise may end up in infinite loop. 278 if (m_dstBuf->isCanceled()) 279 break; 280 281 deSleep(FILEREADER_IDLE_SLEEP); 282 283 if (m_dstBuf->isCanceled()) 284 break; 285 else 286 continue; 287 } 288 else if (err == ERROR_BROKEN_PIPE) 289 break; 290 else 291 throw Error(err, "GetOverlappedResult() failed"); 292 } 293 294 if (numBytesRead == 0) 295 throw Error(GetLastError(), "Reading from file failed"); 296 else 297 offset += (deUint64)numBytesRead; 298 } 299 else if (waitRes == WAIT_OBJECT_0 + 1) 300 { 301 // Cancel. 302 if (!CancelIo(m_handle)) 303 throw Error(GetLastError(), "CancelIo() failed"); 304 break; 305 } 306 else 307 throw Error(GetLastError(), "WaitForMultipleObjects() failed"); 308 309 try 310 { 311 m_dstBuf->write((int)numBytesRead, &tmpBuf[0]); 312 m_dstBuf->flush(); 313 } 314 catch (const ThreadedByteBuffer::CanceledException&) 315 { 316 // Canceled. 317 break; 318 } 319 } 320 } 321 catch (const std::exception& e) 322 { 323 // \todo [2013-08-13 pyry] What to do? 324 printf("win32::FileReader::run(): %s\n", e.what()); 325 } 326} 327 328void FileReader::stop (void) 329{ 330 if (!isStarted()) 331 return; // Nothing to do. 332 333 m_cancelEvent.setSignaled(); 334 335 // Join thread. 336 join(); 337 338 m_cancelEvent.reset(); 339 340 m_handle = INVALID_HANDLE_VALUE; 341} 342 343// TestLogReader 344 345TestLogReader::TestLogReader (void) 346 : m_logBuffer (LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS) 347 , m_logFile (INVALID_HANDLE_VALUE) 348 , m_reader (&m_logBuffer) 349{ 350} 351 352TestLogReader::~TestLogReader (void) 353{ 354 if (m_logFile != INVALID_HANDLE_VALUE) 355 CloseHandle(m_logFile); 356} 357 358void TestLogReader::start (const char* filename) 359{ 360 DE_ASSERT(m_logFile == INVALID_HANDLE_VALUE && !m_reader.isStarted()); 361 362 m_logFile = CreateFile(filename, 363 GENERIC_READ, 364 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, 365 DE_NULL, 366 OPEN_EXISTING, 367 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 368 DE_NULL); 369 370 if (m_logFile == INVALID_HANDLE_VALUE) 371 throw Error(GetLastError(), "Failed to open log file"); 372 373 m_reader.start(m_logFile); 374} 375 376void TestLogReader::stop (void) 377{ 378 if (!m_reader.isStarted()) 379 return; // Nothing to do. 380 381 m_logBuffer.cancel(); 382 m_reader.stop(); 383 384 CloseHandle(m_logFile); 385 m_logFile = INVALID_HANDLE_VALUE; 386 387 m_logBuffer.clear(); 388} 389 390// Process 391 392Process::Process (void) 393 : m_state (STATE_NOT_STARTED) 394 , m_exitCode (0) 395 , m_standardIn (INVALID_HANDLE_VALUE) 396 , m_standardOut (INVALID_HANDLE_VALUE) 397 , m_standardErr (INVALID_HANDLE_VALUE) 398{ 399 deMemset(&m_procInfo, 0, sizeof(m_procInfo)); 400} 401 402Process::~Process (void) 403{ 404 try 405 { 406 if (isRunning()) 407 { 408 kill(); 409 waitForFinish(); 410 } 411 } 412 catch (...) 413 { 414 } 415 416 cleanupHandles(); 417} 418 419void Process::cleanupHandles (void) 420{ 421 DE_ASSERT(!isRunning()); 422 423 if (m_standardErr != INVALID_HANDLE_VALUE) 424 CloseHandle(m_standardErr); 425 426 if (m_standardOut != INVALID_HANDLE_VALUE) 427 CloseHandle(m_standardOut); 428 429 if (m_standardIn != INVALID_HANDLE_VALUE) 430 CloseHandle(m_standardIn); 431 432 if (m_procInfo.hProcess) 433 CloseHandle(m_procInfo.hProcess); 434 435 if (m_procInfo.hThread) 436 CloseHandle(m_procInfo.hThread); 437 438 m_standardErr = INVALID_HANDLE_VALUE; 439 m_standardOut = INVALID_HANDLE_VALUE; 440 m_standardIn = INVALID_HANDLE_VALUE; 441 442 deMemset(&m_procInfo, 0, sizeof(m_procInfo)); 443} 444 445__declspec(thread) static int t_pipeNdx = 0; 446 447static void createPipeWithOverlappedIO (HANDLE* readHandleOut, HANDLE* writeHandleOut, deUint32 readMode, deUint32 writeMode, SECURITY_ATTRIBUTES* securityAttr) 448{ 449 const int defaultBufSize = 4096; 450 char pipeName[128]; 451 HANDLE readHandle; 452 HANDLE writeHandle; 453 454 DE_ASSERT(((readMode | writeMode) & ~FILE_FLAG_OVERLAPPED) == 0); 455 456 deSprintf(pipeName, sizeof(pipeName), "\\\\.\\Pipe\\dEQP-ExecServer-%08x-%08x-%08x", 457 GetCurrentProcessId(), 458 GetCurrentThreadId(), 459 t_pipeNdx++); 460 461 readHandle = CreateNamedPipe(pipeName, /* Pipe name. */ 462 PIPE_ACCESS_INBOUND|readMode, /* Open mode. */ 463 PIPE_TYPE_BYTE|PIPE_WAIT, /* Pipe flags. */ 464 1, /* Max number of instances. */ 465 defaultBufSize, /* Output buffer size. */ 466 defaultBufSize, /* Input buffer size. */ 467 0, /* Use default timeout. */ 468 securityAttr); 469 470 if (readHandle == INVALID_HANDLE_VALUE) 471 throw Error(GetLastError(), "CreateNamedPipe() failed"); 472 473 writeHandle = CreateFile(pipeName, 474 GENERIC_WRITE, /* Access mode. */ 475 0, /* No sharing. */ 476 securityAttr, 477 OPEN_EXISTING, /* Assume existing object. */ 478 FILE_ATTRIBUTE_NORMAL|writeMode, /* Open mode / flags. */ 479 DE_NULL /* Template file. */); 480 481 if (writeHandle == INVALID_HANDLE_VALUE) 482 { 483 DWORD openErr = GetLastError(); 484 CloseHandle(readHandle); 485 throw Error(openErr, "Failed to open created pipe, CreateFile() failed"); 486 } 487 488 *readHandleOut = readHandle; 489 *writeHandleOut = writeHandle; 490} 491 492void Process::start (const char* commandLine, const char* workingDirectory) 493{ 494 // Pipes. 495 HANDLE stdInRead = INVALID_HANDLE_VALUE; 496 HANDLE stdInWrite = INVALID_HANDLE_VALUE; 497 HANDLE stdOutRead = INVALID_HANDLE_VALUE; 498 HANDLE stdOutWrite = INVALID_HANDLE_VALUE; 499 HANDLE stdErrRead = INVALID_HANDLE_VALUE; 500 HANDLE stdErrWrite = INVALID_HANDLE_VALUE; 501 502 if (m_state == STATE_RUNNING) 503 throw std::runtime_error("Process already running"); 504 else if (m_state == STATE_FINISHED) 505 { 506 // Process finished, clean up old cruft. 507 cleanupHandles(); 508 m_state = STATE_NOT_STARTED; 509 } 510 511 // Create pipes 512 try 513 { 514 SECURITY_ATTRIBUTES securityAttr; 515 STARTUPINFO startInfo; 516 517 deMemset(&startInfo, 0, sizeof(startInfo)); 518 deMemset(&securityAttr, 0, sizeof(securityAttr)); 519 520 // Security attributes for inheriting handle. 521 securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 522 securityAttr.bInheritHandle = TRUE; 523 securityAttr.lpSecurityDescriptor = DE_NULL; 524 525 createPipeWithOverlappedIO(&stdInRead, &stdInWrite, 0, FILE_FLAG_OVERLAPPED, &securityAttr); 526 createPipeWithOverlappedIO(&stdOutRead, &stdOutWrite, FILE_FLAG_OVERLAPPED, 0, &securityAttr); 527 createPipeWithOverlappedIO(&stdErrRead, &stdErrWrite, FILE_FLAG_OVERLAPPED, 0, &securityAttr); 528 529 if (!SetHandleInformation(stdInWrite, HANDLE_FLAG_INHERIT, 0) || 530 !SetHandleInformation(stdOutRead, HANDLE_FLAG_INHERIT, 0) || 531 !SetHandleInformation(stdErrRead, HANDLE_FLAG_INHERIT, 0)) 532 throw Error(GetLastError(), "SetHandleInformation() failed"); 533 534 // Startup info for process. 535 startInfo.cb = sizeof(startInfo); 536 startInfo.hStdError = stdErrWrite; 537 startInfo.hStdOutput = stdOutWrite; 538 startInfo.hStdInput = stdInRead; 539 startInfo.dwFlags |= STARTF_USESTDHANDLES; 540 541 if (!CreateProcess(DE_NULL, (LPTSTR)commandLine, DE_NULL, DE_NULL, TRUE /* inherit handles */, 0, DE_NULL, workingDirectory, &startInfo, &m_procInfo)) 542 throw Error(GetLastError(), "CreateProcess() failed"); 543 } 544 catch (...) 545 { 546 if (stdInRead != INVALID_HANDLE_VALUE) CloseHandle(stdInRead); 547 if (stdInWrite != INVALID_HANDLE_VALUE) CloseHandle(stdInWrite); 548 if (stdOutRead != INVALID_HANDLE_VALUE) CloseHandle(stdOutRead); 549 if (stdOutWrite != INVALID_HANDLE_VALUE) CloseHandle(stdOutWrite); 550 if (stdErrRead != INVALID_HANDLE_VALUE) CloseHandle(stdErrRead); 551 if (stdErrWrite != INVALID_HANDLE_VALUE) CloseHandle(stdErrWrite); 552 throw; 553 } 554 555 // Store handles to be kept. 556 m_standardIn = stdInWrite; 557 m_standardOut = stdOutRead; 558 m_standardErr = stdErrRead; 559 560 // Close other ends of handles. 561 CloseHandle(stdErrWrite); 562 CloseHandle(stdOutWrite); 563 CloseHandle(stdInRead); 564 565 m_state = STATE_RUNNING; 566} 567 568bool Process::isRunning (void) 569{ 570 if (m_state == STATE_RUNNING) 571 { 572 int exitCode; 573 BOOL result = GetExitCodeProcess(m_procInfo.hProcess, (LPDWORD)&exitCode); 574 575 if (result != TRUE) 576 throw Error(GetLastError(), "GetExitCodeProcess() failed"); 577 578 if (exitCode == STILL_ACTIVE) 579 return true; 580 else 581 { 582 // Done. 583 m_exitCode = exitCode; 584 m_state = STATE_FINISHED; 585 return false; 586 } 587 } 588 else 589 return false; 590} 591 592void Process::waitForFinish (void) 593{ 594 if (m_state == STATE_RUNNING) 595 { 596 if (WaitForSingleObject(m_procInfo.hProcess, INFINITE) != WAIT_OBJECT_0) 597 throw Error(GetLastError(), "Waiting for process failed, WaitForSingleObject() failed"); 598 599 if (isRunning()) 600 throw std::runtime_error("Process is still alive"); 601 } 602 else 603 throw std::runtime_error("Process is not running"); 604} 605 606void Process::stopProcess (bool kill) 607{ 608 if (m_state == STATE_RUNNING) 609 { 610 if (!TerminateProcess(m_procInfo.hProcess, kill ? -1 : 0)) 611 throw Error(GetLastError(), "TerminateProcess() failed"); 612 } 613 else 614 throw std::runtime_error("Process is not running"); 615} 616 617void Process::terminate (void) 618{ 619 stopProcess(false); 620} 621 622void Process::kill (void) 623{ 624 stopProcess(true); 625} 626 627} // win32 628 629Win32TestProcess::Win32TestProcess (void) 630 : m_process (DE_NULL) 631 , m_processStartTime (0) 632 , m_infoBuffer (INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS) 633 , m_stdOutReader (&m_infoBuffer) 634 , m_stdErrReader (&m_infoBuffer) 635{ 636} 637 638Win32TestProcess::~Win32TestProcess (void) 639{ 640 delete m_process; 641} 642 643void Win32TestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList) 644{ 645 bool hasCaseList = strlen(caseList) > 0; 646 647 XS_CHECK(!m_process); 648 649 de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa"); 650 m_logFileName = logFilePath.getPath(); 651 652 // Remove old file if such exists. 653 // \note Sometimes on Windows the test process dies slowly and may not release handle to log file 654 // until a bit later. 655 // \todo [2013-07-15 pyry] This should be solved by improving deProcess and killing all child processes as well. 656 { 657 int tryNdx = 0; 658 while (tryNdx < MAX_OLD_LOGFILE_DELETE_ATTEMPTS && deFileExists(m_logFileName.c_str())) 659 { 660 if (deDeleteFile(m_logFileName.c_str())) 661 break; 662 deSleep(LOGFILE_DELETE_SLEEP_MS); 663 tryNdx += 1; 664 } 665 666 if (deFileExists(m_logFileName.c_str())) 667 throw TestProcessException(string("Failed to remove '") + m_logFileName + "'"); 668 } 669 670 // Construct command line. 671 string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).normalize().getPath(); 672 cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName(); 673 674 if (hasCaseList) 675 cmdLine += " --deqp-stdin-caselist"; 676 677 if (strlen(params) > 0) 678 cmdLine += string(" ") + params; 679 680 DE_ASSERT(!m_process); 681 m_process = new win32::Process(); 682 683 try 684 { 685 m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL); 686 } 687 catch (const std::exception& e) 688 { 689 delete m_process; 690 m_process = DE_NULL; 691 throw TestProcessException(e.what()); 692 } 693 694 m_processStartTime = deGetMicroseconds(); 695 696 // Create stdout & stderr readers. 697 m_stdOutReader.start(m_process->getStdOut()); 698 m_stdErrReader.start(m_process->getStdErr()); 699 700 // Start case list writer. 701 if (hasCaseList) 702 m_caseListWriter.start(caseList, m_process->getStdIn()); 703} 704 705void Win32TestProcess::terminate (void) 706{ 707 if (m_process) 708 { 709 try 710 { 711 m_process->kill(); 712 } 713 catch (const std::exception& e) 714 { 715 printf("Win32TestProcess::terminate(): Failed to kill process: %s\n", e.what()); 716 } 717 } 718} 719 720void Win32TestProcess::cleanup (void) 721{ 722 m_caseListWriter.stop(); 723 724 // \note Buffers must be canceled before stopping readers. 725 m_infoBuffer.cancel(); 726 727 m_stdErrReader.stop(); 728 m_stdOutReader.stop(); 729 m_testLogReader.stop(); 730 731 // Reset buffers. 732 m_infoBuffer.clear(); 733 734 if (m_process) 735 { 736 try 737 { 738 if (m_process->isRunning()) 739 { 740 m_process->kill(); 741 m_process->waitForFinish(); 742 } 743 } 744 catch (const std::exception& e) 745 { 746 printf("Win32TestProcess::cleanup(): Failed to kill process: %s\n", e.what()); 747 } 748 749 delete m_process; 750 m_process = DE_NULL; 751 } 752} 753 754int Win32TestProcess::readTestLog (deUint8* dst, int numBytes) 755{ 756 if (!m_testLogReader.isRunning()) 757 { 758 if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000) 759 { 760 // Timeout, kill process. 761 terminate(); 762 return 0; // \todo [2013-08-13 pyry] Throw exception? 763 } 764 765 if (!deFileExists(m_logFileName.c_str())) 766 return 0; 767 768 // Start reader. 769 m_testLogReader.start(m_logFileName.c_str()); 770 } 771 772 DE_ASSERT(m_testLogReader.isRunning()); 773 return m_testLogReader.read(dst, numBytes); 774} 775 776bool Win32TestProcess::isRunning (void) 777{ 778 if (m_process) 779 return m_process->isRunning(); 780 else 781 return false; 782} 783 784int Win32TestProcess::getExitCode (void) const 785{ 786 if (m_process) 787 return m_process->getExitCode(); 788 else 789 return -1; 790} 791 792} // xs 793