rlmp.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. //
  6. // rlmp.c -- Code to help implement multi-threaded support
  7. //
  8. #include "rl.h"
  9. /////////////////////////////////////////////////////////////////////////
  10. int
  11. FilterThread(
  12. FILE* ChildOutput,
  13. DWORD millisecTimeout
  14. );
  15. FILE *
  16. PipeSpawn(
  17. char* cmdstring,
  18. void* localEnvVars = NULL
  19. );
  20. int
  21. PipeSpawnClose(
  22. FILE* pstream,
  23. bool timedOut
  24. );
  25. /////////////////////////////////////////////////////////////////////////
  26. bool CDirectory::TryLock()
  27. {
  28. char full[BUFFER_SIZE];
  29. sprintf_s(full, "%s\\%s", this->GetDirectoryPath(), DIR_LOCKFILE);
  30. _dirLock = CreateFile(full, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
  31. FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE,
  32. NULL);
  33. if (_dirLock == INVALID_HANDLE_VALUE) {
  34. if (GetLastError() == ERROR_SHARING_VIOLATION) {
  35. return false;
  36. } else {
  37. LogError("Creating lock file %s - error %d\n", full, GetLastError());
  38. return false;
  39. }
  40. }
  41. else
  42. return true;
  43. }
  44. void CDirectory::WaitLock()
  45. {
  46. char full[BUFFER_SIZE];
  47. sprintf_s(full, "%s\\%s", this->GetDirectoryPath(), DIR_LOCKFILE);
  48. int sec = 0;
  49. for (;;)
  50. {
  51. _dirLock = CreateFile(full, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
  52. FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE,
  53. NULL);
  54. if (_dirLock != INVALID_HANDLE_VALUE)
  55. break;
  56. if (GetLastError() != ERROR_SHARING_VIOLATION) {
  57. LogError("Creating lock file %s - error %d\n", full, GetLastError());
  58. // Continue execution regardless
  59. break;
  60. }
  61. Message("Waiting for directory %s (%d seconds elapsed)",
  62. this->GetDirectoryPath(), sec);
  63. // Force this message to be seen immediately. This breaks our usual
  64. // rules for interleaving output, but that is probably okay in this
  65. // rare situation.
  66. FlushOutput();
  67. Sleep(1000);
  68. sec++;
  69. }
  70. }
  71. CDirectory::~CDirectory()
  72. {
  73. // Directory lock _dirLock gets released automagically by CHandle. Since
  74. // we use FILE_FLAG_DELETE_ON_CLOSE, the lock file goes away when the
  75. // handle is closed.
  76. FreeTestList(&_testList);
  77. DeleteCriticalSection(&_cs);
  78. }
  79. int CDirectory::Count()
  80. {
  81. int count = 0;
  82. Test * pTest;
  83. for (pTest = _testList.first; pTest != NULL; pTest = pTest->next)
  84. {
  85. for (TestVariant * variant = pTest->variants;
  86. variant != NULL;
  87. variant = variant->next)
  88. {
  89. ++count;
  90. }
  91. }
  92. return count;
  93. }
  94. void CDirectory::InitStats(int num)
  95. {
  96. if (num) {
  97. NumVariations = num;
  98. if (Mode == RM_EXE) {
  99. stat = RLS_EXE;
  100. ::NumVariationsTotal[RLS_EXE] += num;
  101. ::NumVariationsTotal[RLS_TOTAL] += num;
  102. }
  103. else {
  104. if (FBaseDiff || FBaseline) {
  105. stat = RLS_BASELINES; // initial stat
  106. ::NumVariationsTotal[RLS_BASELINES] += num;
  107. ::NumVariationsTotal[RLS_TOTAL] += num;
  108. }
  109. if (FBaseDiff || !FBaseline) {
  110. if (!FBaseDiff)
  111. stat = RLS_DIFFS; // initial stat
  112. ::NumVariationsTotal[RLS_DIFFS] += num;
  113. ::NumVariationsTotal[RLS_TOTAL] += num;
  114. }
  115. }
  116. }
  117. else {
  118. ASSERTNR(FBaseDiff && !IsBaseline());
  119. ASSERTNR(stat == RLS_BASELINES);
  120. ASSERTNR(NumVariationsRun == NumVariations);
  121. stat = RLS_DIFFS;
  122. }
  123. NumVariationsRun = NumFailures = NumDiffs = 0;
  124. }
  125. int32 CDirectory::IncRun(int inc)
  126. {
  127. ::NumVariationsRun[RLS_TOTAL] += inc; // overall count
  128. ::NumVariationsRun[stat] += inc; // mode stat
  129. return NumVariationsRun += inc; // per directory count
  130. }
  131. int32 CDirectory::IncFailures(int inc)
  132. {
  133. ::NumFailuresTotal[RLS_TOTAL] += inc; // overall count
  134. ::NumFailuresTotal[stat] += inc; // mode stat
  135. return NumFailures += inc; // per directory count
  136. }
  137. int32 CDirectory::IncDiffs()
  138. {
  139. ::NumDiffsTotal[RLS_TOTAL]++; // overall count
  140. ::NumDiffsTotal[stat]++; // mode stat
  141. return NumDiffs++; // per directory count
  142. }
  143. void CDirectory::TryBeginDirectory()
  144. {
  145. UpdateState();
  146. }
  147. void CDirectory::TryEndDirectory()
  148. {
  149. UpdateState();
  150. }
  151. // Notify CDirectory that run counts have been changed.
  152. // Check to see if directory is just being started or finished.
  153. // Output summary if needed and TODO: enter/end directory
  154. void CDirectory::UpdateState()
  155. {
  156. EnterCriticalSection(&_cs);
  157. if (_isDirStarted)
  158. {
  159. // After we set elapsed_dir, we won't write the finish summary again.
  160. if (NumVariationsRun == NumVariations && elapsed_dir == 0)
  161. {
  162. elapsed_dir = time(NULL) - start_dir;
  163. WriteDirectoryFinishSummary();
  164. }
  165. }
  166. else
  167. {
  168. start_dir = time(NULL);
  169. _isDirStarted = true;
  170. WriteDirectoryBeginSummary();
  171. }
  172. LeaveCriticalSection(&_cs);
  173. }
  174. void CDirectory::WriteDirectoryBeginSummary()
  175. {
  176. Message("Running %s %s in directory %s",
  177. ModeNames[Mode],
  178. (Mode == RM_ASM)
  179. ? (IsBaseline() ? "baselines" : "diffs")
  180. : "tests",
  181. GetDirectoryName());
  182. if (FSyncTest || FSyncVariation)
  183. {
  184. FlushOutput();
  185. }
  186. }
  187. void CDirectory::WriteDirectoryFinishSummary()
  188. {
  189. WriteSummary(GetDirectoryName(), IsBaseline(), NumVariations, NumDiffs, NumFailures);
  190. if (Timing & TIME_DIR)
  191. {
  192. Message("RL: Directory elapsed time (%s): %02d:%02d", GetDirectoryName(), elapsed_dir / 60, elapsed_dir % 60);
  193. }
  194. if (FSyncDir)
  195. {
  196. FlushOutput();
  197. }
  198. }
  199. #ifndef NODEBUG
  200. void CDirectory::Dump()
  201. {
  202. printf("==== Directory %s (%s)\n", _pDir->name, _pDir->fullPath);
  203. DumpTestList(&_testList);
  204. }
  205. #endif
  206. /////////////////////////////////////////////////////////////////////////
  207. CDirectoryQueue::~CDirectoryQueue()
  208. {
  209. // We must clean up the directory list rooted at _head if FSingleThreadPerDir is
  210. // false. Might as well just always clean up if there is anything here.
  211. CDirectory* prev = NULL;
  212. CDirectory* temp = _head;
  213. while (temp != NULL)
  214. {
  215. prev = temp;
  216. temp = temp->_next;
  217. delete prev;
  218. }
  219. // The CHandle objects clean up after themselves.
  220. DeleteCriticalSection(&_cs);
  221. }
  222. CDirectory* CDirectoryQueue::Pop()
  223. {
  224. bool force_wait = false;
  225. CDirectory* tmp;
  226. CDirectory* tmpPrev;
  227. EnterCriticalSection(&_cs);
  228. tmpPrev = NULL;
  229. tmp = _head;
  230. while (tmp != NULL) {
  231. if (tmp->TryLock()) {
  232. if (tmp == _head)
  233. _head = _head->_next;
  234. if (tmpPrev != NULL)
  235. tmpPrev->_next = tmp->_next; // unlink it
  236. if (tmp == _tail)
  237. _tail = tmpPrev;
  238. --_length;
  239. break;
  240. }
  241. Warning("Skipping locked directory %s (will return to it)",
  242. tmp->GetDirectoryName());
  243. tmpPrev = tmp;
  244. tmp = tmp->_next;
  245. }
  246. if (tmp == NULL && _head != NULL) {
  247. // There's at least one directory, but they're all in use by another
  248. // copy of rl. Just grab the first one and wait on it.
  249. tmp = _head;
  250. _head = _head->_next;
  251. if (tmp == _tail)
  252. _tail = NULL;
  253. --_length;
  254. // wait until it's no longer in use..... However, don't wait inside the
  255. // critical section.
  256. force_wait = true;
  257. }
  258. ReleaseSemaphore(_hMaxWorkQueueSem, 1, NULL);
  259. LeaveCriticalSection(&_cs);
  260. if (tmp != NULL) // might be empty diff queue before masters are run
  261. tmp->_next = NULL;
  262. if (force_wait)
  263. tmp->WaitLock();
  264. return tmp;
  265. }
  266. /////////////////////////////////////////////////////////////////////////
  267. CDirectoryAndTestCase* CDirectoryAndTestCaseQueue::Pop()
  268. {
  269. CDirectoryAndTestCase* tmp;
  270. EnterCriticalSection(&_cs);
  271. tmp = _head;
  272. if (tmp != NULL) {
  273. if (tmp == _head)
  274. _head = _head->_next;
  275. if (tmp == _tail)
  276. _tail = NULL;
  277. --_length;
  278. }
  279. ReleaseSemaphore(_hMaxWorkQueueSem, 1, NULL);
  280. LeaveCriticalSection(&_cs);
  281. return tmp;
  282. }
  283. /////////////////////////////////////////////////////////////////////////
  284. void CThreadInfo::Done()
  285. {
  286. // The thread is exiting, so set appropriate state
  287. EnterCriticalSection(&_cs);
  288. _isDone = true;
  289. strcpy_s(_currentTest, "done");
  290. if (FRLFE)
  291. RLFEThreadDir(NULL, (BYTE)ThreadId);
  292. LeaveCriticalSection(&_cs);
  293. }
  294. void CThreadInfo::SetCurrentTest(const char* dir, const char* test, bool isBaseline)
  295. {
  296. const char* tmp = "";
  297. EnterCriticalSection(&_cs);
  298. if (FRLFE) {
  299. if (test[0] != ' ') {
  300. sprintf_s(_currentTest, "\\%s", test);
  301. RLFEThreadStatus((BYTE)ThreadId, _currentTest);
  302. }
  303. else {
  304. RLFEThreadStatus((BYTE)ThreadId, test);
  305. }
  306. }
  307. if (FBaseDiff) { // indicate either baseline or diff compile
  308. if (isBaseline)
  309. tmp = "b:";
  310. else
  311. tmp = "d:";
  312. }
  313. sprintf_s(_currentTest, "%s%s\\%s", tmp, dir, test);
  314. LeaveCriticalSection(&_cs);
  315. }
  316. void CThreadInfo::AddToTmpFileList(char* fullPath)
  317. {
  318. EnterCriticalSection(&_cs);
  319. _head = new TmpFileList(_head, fullPath);
  320. LeaveCriticalSection(&_cs);
  321. }
  322. void CThreadInfo::ClearTmpFileList()
  323. {
  324. EnterCriticalSection(&_cs);
  325. while (_head != NULL) {
  326. TmpFileList* tmp = _head;
  327. _head = _head->_next;
  328. delete tmp;
  329. }
  330. _head = NULL;
  331. LeaveCriticalSection(&_cs);
  332. }
  333. void CThreadInfo::DeleteTmpFileList()
  334. {
  335. EnterCriticalSection(&_cs);
  336. while (_head != NULL) {
  337. DeleteFileIfFound(_head->_fullPath);
  338. _head = _head->_next;
  339. }
  340. ClearTmpFileList();
  341. LeaveCriticalSection(&_cs);
  342. }
  343. /////////////////////////////////////////////////////////////////////////
  344. #define BUFFER_CHUNK 512
  345. void COutputBuffer::Reset()
  346. {
  347. // leave buffer alone until destructed
  348. _end = _start;
  349. *_end = '\0';
  350. _textGrabbed = false;
  351. }
  352. void COutputBuffer::Flush(FILE* pfile)
  353. {
  354. if (pfile != NULL) {
  355. EnterCriticalSection(&csStdio);
  356. fprintf(pfile, "%s", _start);
  357. fflush(pfile);
  358. _textGrabbed = false;
  359. LeaveCriticalSection(&csStdio);
  360. }
  361. Reset();
  362. }
  363. COutputBuffer::COutputBuffer(const char* logfile, bool buffered)
  364. : _bufSize(512)
  365. , _buffered(buffered)
  366. , _textGrabbed(false)
  367. , _type(OUT_FILENAME)
  368. {
  369. _end = _start = new char[_bufSize];
  370. *_end = '\0';
  371. if (logfile == NULL) {
  372. _filename = NULL;
  373. } else {
  374. _filename = _strdup(logfile);
  375. }
  376. }
  377. COutputBuffer::COutputBuffer(FILE* pfile, bool buffered)
  378. : _bufSize(512)
  379. , _buffered(buffered)
  380. , _textGrabbed(false)
  381. , _type(OUT_FILE)
  382. {
  383. _end = _start = new char[_bufSize];
  384. *_end = '\0';
  385. _pfile = pfile;
  386. }
  387. COutputBuffer::~COutputBuffer()
  388. {
  389. Flush();
  390. delete[] _start;
  391. _start = _end = NULL;
  392. _bufSize = 0;
  393. if (_type == OUT_FILENAME) {
  394. free(_filename);
  395. _filename = NULL;
  396. }
  397. }
  398. // Add without doing varargs formatting (avoids local buffer size problems)
  399. void COutputBuffer::AddDirect(char* string)
  400. {
  401. ASSERTNR(!_textGrabbed);
  402. size_t len = strlen(string);
  403. while ((_end - _start) + len >= _bufSize) {
  404. char* pNew = new char[_bufSize * 2];
  405. memcpy(pNew, _start, _end - _start + 1); // copy null
  406. _end = pNew + (_end - _start);
  407. delete[] _start;
  408. _start = pNew;
  409. _bufSize *= 2;
  410. }
  411. memcpy(_end, string, len + 1); // copy null
  412. _end += len;
  413. if (!_buffered || (!FRLFE && (NumberOfThreads == 1))) {
  414. // no need to synchronize; flush immediately to get faster interaction
  415. Flush();
  416. }
  417. }
  418. void COutputBuffer::Add(const char* fmt, ...)
  419. {
  420. const int MESSAGE_MAX = 60000; // maximum allowed message size
  421. va_list arg_ptr;
  422. char tempBuf[MESSAGE_MAX];
  423. va_start(arg_ptr, fmt);
  424. vsprintf_s(tempBuf, fmt, arg_ptr);
  425. ASSERT(strlen(tempBuf) < MESSAGE_MAX); // better not have written past tempBuf
  426. AddDirect(tempBuf);
  427. }
  428. void COutputBuffer::Flush()
  429. {
  430. FILE* fp;
  431. if (_type == OUT_FILE) {
  432. Flush(_pfile);
  433. }
  434. else {
  435. ASSERTNR(_type == OUT_FILENAME);
  436. if (_filename != NULL) {
  437. fp = fopen_unsafe(_filename, "a");
  438. if (fp == NULL) {
  439. // We might not be able to open the log or full log output
  440. // files because someone is grepping or otherwise looking
  441. // through them while rl is active. In that case, we don't
  442. // want to just kill rl, so just keep accumulating output
  443. // and try again next time. Output a warning to the log so
  444. // they know it happened (but they won't see it unless the
  445. // output is flushed). We could consider having a maximum,
  446. // after which we "turn off" this output buffer, but we're
  447. // unlikely to ever have so much output that it causes a
  448. // problem.
  449. Warning("Cannot open '%s' for appending with error '%s'", _filename, strerror_unsafe(errno));
  450. }
  451. else {
  452. Flush(fp);
  453. fclose(fp);
  454. }
  455. }
  456. }
  457. }
  458. /////////////////////////////////////////////////////////////////////////
  459. //+---------------------------------------------------------------------------
  460. //
  461. // Function: ExecuteCommand
  462. //
  463. // Synopsis: Execute the given command and filter its output.
  464. //
  465. // Arguments: [path] -- directory in which to execute the command
  466. // [CommandLine] --
  467. //
  468. // Returns: ERROR_SUCCESS, ERROR_NOTENOUGHMEMORY, or return code from
  469. // PipeSpawnClose.
  470. //
  471. // Notes: On a multiprocessor machine, this will spawn a new thread
  472. // and then return, letting the thread run asynchronously. Use
  473. // WaitForParallelThreads() to ensure all threads are finished.
  474. // By default, this routine will spawn as many threads as the
  475. // machine has processors. This can be overridden with the
  476. // -threads option.
  477. //
  478. //----------------------------------------------------------------------------
  479. int
  480. ExecuteCommand(
  481. const char* path,
  482. const char* CommandLine,
  483. DWORD millisecTimeout,
  484. void* envFlags)
  485. {
  486. int rc;
  487. FILE* childOutput = NULL;
  488. char putEnvStr[BUFFER_SIZE];
  489. char ExecuteProgramCmdLine[BUFFER_SIZE];
  490. UINT prevmode;
  491. prevmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
  492. // Always flush output buffers before executing a command. Note: this
  493. // shouldn't really be necessary because all output should be buffered
  494. // by the COutputBuffer class on a per-thread basis.
  495. fflush(stdout);
  496. fflush(stderr);
  497. strcpy_s(ExecuteProgramCmdLine, "cmd.exe /c "); // for .cmd/.bat scripts
  498. strcat_s(ExecuteProgramCmdLine, CommandLine);
  499. EnterCriticalSection(&csCurrentDirectory);
  500. // If we're doing executable tests, set the TARGET_VM environment variable.
  501. // We must do this inside a critical section since the environment is
  502. // not thread specific.
  503. if ((Mode == RM_EXE) && TargetVM) {
  504. sprintf_s(putEnvStr, "TARGET_VM=%s", TargetVM);
  505. _putenv(putEnvStr);
  506. }
  507. rc = _chdir(path);
  508. if (rc == 0) {
  509. childOutput = PipeSpawn(ExecuteProgramCmdLine, envFlags);
  510. }
  511. LeaveCriticalSection(&csCurrentDirectory);
  512. if (rc != 0) {
  513. LogError("Could not change directory to '%s' - errno == %d\n", path, errno);
  514. } else if (childOutput != NULL) {
  515. rc = FilterThread(childOutput, millisecTimeout);
  516. rc = PipeSpawnClose(childOutput, rc == WAIT_TIMEOUT);
  517. }
  518. SetErrorMode(prevmode);
  519. return rc;
  520. }
  521. //+---------------------------------------------------------------------------
  522. //
  523. // Function: FilterThread
  524. //
  525. // Synopsis: Capture the output of the thread and process it.
  526. //
  527. //----------------------------------------------------------------------------
  528. // Don't malloc this up every time; use thread-local storage to keep it around.
  529. #define FILTER_READ_CHUNK 512
  530. __declspec(thread) char* StartPointer = NULL;
  531. __declspec(thread) unsigned BufSize = 512;
  532. struct FilterThreadData {
  533. int ThreadId;
  534. unsigned* pBufSize;
  535. char** pStartPointer;
  536. COutputBuffer* ThreadOut;
  537. COutputBuffer* ThreadFull;
  538. FILE* ChildOutput;
  539. };
  540. unsigned WINAPI
  541. FilterWorker(
  542. void* param
  543. );
  544. int
  545. FilterThread(
  546. FILE* ChildOutput,
  547. DWORD millisecTimeout
  548. )
  549. {
  550. FilterThreadData data = {
  551. ThreadId,
  552. &BufSize,
  553. &StartPointer,
  554. ThreadOut,
  555. ThreadFull,
  556. ChildOutput
  557. };
  558. HANDLE hThreadWorker = (HANDLE)_beginthreadex(nullptr, 0, FilterWorker, &data, 0, nullptr);
  559. if (hThreadWorker == 0) {
  560. LogError("Failed to create FilterWorker thread - error = %d", GetLastError());
  561. return -1;
  562. }
  563. DWORD waitresult = WaitForSingleObject(hThreadWorker, millisecTimeout);
  564. int rc = 0;
  565. if (waitresult == WAIT_TIMEOUT) {
  566. // Abort the worker thread by cancelling the IO operations on the pipe
  567. CancelIoEx((HANDLE)_get_osfhandle(_fileno(ChildOutput)), nullptr);
  568. WaitForSingleObject(hThreadWorker, INFINITE);
  569. rc = WAIT_TIMEOUT;
  570. }
  571. else {
  572. DWORD ec;
  573. if (!GetExitCodeThread(hThreadWorker, &ec)) {
  574. LogError("Could not get exit code from FilterWorker worker thread - error = %d", GetLastError());
  575. rc = -1;
  576. }
  577. if (ec != 0) {
  578. LogError("Pipe read failed - errno = %d\n", ec);
  579. rc = -1;
  580. }
  581. }
  582. CloseHandle(hThreadWorker);
  583. return rc;
  584. }
  585. unsigned WINAPI
  586. FilterWorker(
  587. void* param
  588. )
  589. {
  590. FilterThreadData* data = static_cast<FilterThreadData*>(param);
  591. size_t CountBytesRead;
  592. char* EndPointer;
  593. char* NewPointer;
  594. char buf[50];
  595. buf[0] = '\0';
  596. if (!FNoThreadId && data->ThreadId != 0 && NumberOfThreads > 1) {
  597. sprintf_s(buf, "%d>", data->ThreadId);
  598. }
  599. if (*data->pStartPointer == NULL)
  600. *data->pStartPointer = (char*)malloc(*data->pBufSize);
  601. while (TRUE) {
  602. EndPointer = *data->pStartPointer;
  603. do {
  604. if (*data->pBufSize - (EndPointer - *data->pStartPointer) < FILTER_READ_CHUNK) {
  605. NewPointer = (char*)malloc(*data->pBufSize*2);
  606. memcpy(NewPointer, *data->pStartPointer,
  607. EndPointer - *data->pStartPointer + 1); // copy null byte, too
  608. EndPointer = NewPointer + (EndPointer - *data->pStartPointer);
  609. free(*data->pStartPointer);
  610. *data->pStartPointer = NewPointer;
  611. *data->pBufSize *= 2;
  612. }
  613. if (NULL == fgets(EndPointer, FILTER_READ_CHUNK, data->ChildOutput)) {
  614. if (ferror(data->ChildOutput)) {
  615. return errno;
  616. }
  617. return 0;
  618. }
  619. CountBytesRead = strlen(EndPointer);
  620. EndPointer = EndPointer + CountBytesRead;
  621. } while ((CountBytesRead == FILTER_READ_CHUNK - 1)
  622. && *(EndPointer - 1) != '\n');
  623. CountBytesRead = EndPointer - *data->pStartPointer;
  624. if (CountBytesRead != 0) {
  625. data->ThreadOut->AddDirect(buf);
  626. data->ThreadOut->AddDirect(*data->pStartPointer);
  627. data->ThreadFull->AddDirect(buf);
  628. data->ThreadFull->AddDirect(*data->pStartPointer);
  629. }
  630. }
  631. return 0;
  632. }
  633. // PipeSpawn variables. We can get away with one copy per thread.
  634. __declspec(thread) HANDLE ProcHandle = INVALID_HANDLE_VALUE;
  635. //+---------------------------------------------------------------------------
  636. //
  637. // PipeSpawn. Similar to _popen, but captures both stdout and stderr
  638. //
  639. //----------------------------------------------------------------------------
  640. FILE *
  641. PipeSpawn(
  642. char* cmdstring,
  643. void* localEnvVars
  644. )
  645. {
  646. int PipeHandle[2];
  647. HANDLE WriteHandle, ErrorHandle;
  648. STARTUPINFO StartupInfo;
  649. PROCESS_INFORMATION ProcessInformation;
  650. BOOL Status;
  651. FILE* pstream;
  652. ASSERT(cmdstring != NULL);
  653. // Open the pipe where we'll collect the output.
  654. _pipe(PipeHandle, 20 * 1024, _O_TEXT|_O_NOINHERIT); // 20K bytes buffer
  655. DuplicateHandle(GetCurrentProcess(),
  656. (HANDLE)_get_osfhandle(PipeHandle[1]),
  657. GetCurrentProcess(),
  658. &WriteHandle,
  659. 0L,
  660. TRUE,
  661. DUPLICATE_SAME_ACCESS);
  662. DuplicateHandle(GetCurrentProcess(),
  663. (HANDLE)_get_osfhandle(PipeHandle[1]),
  664. GetCurrentProcess(),
  665. &ErrorHandle,
  666. 0L,
  667. TRUE,
  668. DUPLICATE_SAME_ACCESS);
  669. _close(PipeHandle[1]);
  670. pstream = _fdopen(PipeHandle[0], "rt");
  671. if (pstream == NULL) {
  672. LogError("Creation of I/O filter stream failed - error = %d\n",
  673. cmdstring, errno);
  674. CloseHandle(WriteHandle);
  675. CloseHandle(ErrorHandle);
  676. WriteHandle = INVALID_HANDLE_VALUE;
  677. ErrorHandle = INVALID_HANDLE_VALUE;
  678. _close(PipeHandle[0]);
  679. return NULL;
  680. }
  681. memset(&StartupInfo, 0, sizeof(STARTUPINFO));
  682. StartupInfo.cb = sizeof(STARTUPINFO);
  683. StartupInfo.hStdOutput = WriteHandle;
  684. StartupInfo.hStdError = ErrorHandle;
  685. StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  686. StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  687. memset(&ProcessInformation, 0, sizeof(PROCESS_INFORMATION));
  688. ProcessInformation.hThread = INVALID_HANDLE_VALUE;
  689. ProcessInformation.hProcess = INVALID_HANDLE_VALUE;
  690. // And start the process.
  691. #ifndef NODEBUG
  692. if (FDebug) {
  693. printf("Creating process '%s'\n", cmdstring);
  694. fflush(stdout);
  695. }
  696. #endif
  697. Status = CreateProcess(NULL, cmdstring, NULL, NULL, TRUE, 0, localEnvVars, NULL,
  698. &StartupInfo, &ProcessInformation);
  699. CloseHandle(WriteHandle);
  700. CloseHandle(ErrorHandle);
  701. WriteHandle = INVALID_HANDLE_VALUE;
  702. ErrorHandle = INVALID_HANDLE_VALUE;
  703. if (Status == 0) {
  704. LogError("Exec of '%s' failed - error = %d\n",
  705. cmdstring, GetLastError());
  706. fclose(pstream); // This will close the read handle
  707. pstream = NULL;
  708. ProcHandle = INVALID_HANDLE_VALUE;
  709. } else {
  710. CloseHandle(ProcessInformation.hThread);
  711. ProcessInformation.hThread = INVALID_HANDLE_VALUE;
  712. ProcHandle = ProcessInformation.hProcess;
  713. }
  714. return pstream;
  715. }
  716. //+---------------------------------------------------------------------------
  717. //
  718. // Function: PipeSpawnClose (similar to _pclose)
  719. //
  720. //----------------------------------------------------------------------------
  721. int
  722. PipeSpawnClose(
  723. FILE *pstream,
  724. bool timedOut
  725. )
  726. {
  727. DWORD retval = (DWORD) -1;
  728. if (pstream == NULL) {
  729. return -1;
  730. }
  731. fclose(pstream);
  732. pstream = NULL;
  733. if (timedOut) {
  734. retval = WAIT_TIMEOUT;
  735. // TerminateProcess doesn't kill child processes of the specified process.
  736. // Use taskkill.exe command instead using its /T option to kill child
  737. // processes as well.
  738. char cmdbuf[50];
  739. sprintf_s(cmdbuf, "taskkill.exe /PID %d /T /F", GetProcessId(ProcHandle));
  740. int rc = ExecuteCommand(REGRESS, cmdbuf);
  741. if (rc != 0) {
  742. LogError("taskkill failed - exit code == %d\n", rc);
  743. }
  744. }
  745. else {
  746. if (WaitForSingleObject(ProcHandle, INFINITE) == WAIT_OBJECT_0) {
  747. if (!GetExitCodeProcess(ProcHandle, &retval)) {
  748. LogError("Getting process exit code - error == %d\n", GetLastError());
  749. retval = (DWORD) -1;
  750. }
  751. }
  752. else {
  753. LogError("Wait for process termination failed - error == %d\n", GetLastError());
  754. retval = (DWORD) -1;
  755. }
  756. }
  757. CloseHandle(ProcHandle);
  758. ProcHandle = INVALID_HANDLE_VALUE;
  759. return (int)retval;
  760. }