2
0

rlregr.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. // rlregr.c
  6. //
  7. // assembly regression worker for rl.c
  8. #include "rl.h"
  9. #define TMP_PREFIX "di" // 2 characters
  10. // Currently not overridable
  11. #define REGR_PERL "perl"
  12. #define DIR_START_CMD "startdir.cmd"
  13. #define DIR_END_CMD "enddir.cmd"
  14. // <rl> ... </rl> directives:
  15. //
  16. // PassFo
  17. // Doesn't add -FoNUL. to the command line; allows passing of -Fo
  18. // explicitly.
  19. //
  20. // NoAsm
  21. // Disables asm file generation.
  22. #define PASS_FO_CMD "PassFo"
  23. #define NO_ASM_CMD "NoAsm"
  24. //
  25. // Global variables set before worker threads start, and only accessed
  26. // (not set) by the worker threads.
  27. //
  28. static BOOL NoMasterCompare;
  29. LOCAL int
  30. DoCommand(
  31. const char *path, // full path of directory to run command in
  32. const char *cmdbuf,
  33. bool displayError = true
  34. )
  35. {
  36. int rtncode;
  37. rtncode = ExecuteCommand(path, cmdbuf);
  38. if (rtncode && displayError) {
  39. LogOut("ERROR: Command had a return code of %d:", rtncode);
  40. LogOut(" %s", cmdbuf);
  41. }
  42. return rtncode;
  43. }
  44. LOCAL void __cdecl
  45. RegrCleanUp(void)
  46. {
  47. }
  48. // One-time assembly regression initialization.
  49. void
  50. RegrInit(
  51. void
  52. )
  53. {
  54. atexit(RegrCleanUp);
  55. if (getenv_unsafe("REGR_NOMASTERCMP") != NULL) {
  56. if (FBaseline) {
  57. Fatal("-baseline conflicts with environment variable REGR_NOMASTERCMP.");
  58. }
  59. NoMasterCompare = TRUE;
  60. }
  61. else {
  62. NoMasterCompare = FALSE;
  63. }
  64. }
  65. // Called at the start of each directory.
  66. BOOL
  67. RegrStartDir(
  68. char *path
  69. )
  70. {
  71. char full[MAX_PATH];
  72. // Execute directory setup command script if present.
  73. sprintf_s(full, "%s\\%s", path, DIR_START_CMD);
  74. if (GetFileAttributes(full) != INVALID_FILE_ATTRIBUTES) {
  75. Message(""); // newline
  76. Message("Executing %s", DIR_START_CMD);
  77. if (DoCommand(path, DIR_START_CMD))
  78. return FALSE;
  79. Message(""); // newline
  80. }
  81. return TRUE;
  82. }
  83. // Called at the end of each directory.
  84. BOOL
  85. RegrEndDir(
  86. char* path
  87. )
  88. {
  89. char full[MAX_PATH];
  90. // Execute directory shutdown command script if present.
  91. sprintf_s(full, "%s\\%s", path, DIR_END_CMD);
  92. if (GetFileAttributes(full) != INVALID_FILE_ATTRIBUTES) {
  93. Message(""); // newline
  94. Message("Executing %s", DIR_END_CMD);
  95. if (DoCommand(path, DIR_END_CMD))
  96. return FALSE;
  97. Message("");
  98. }
  99. return TRUE;
  100. }
  101. // Called to perform assembly regressions on one file.
  102. // Returns -1 for failure, 1 for diff, 0 for okay.
  103. int
  104. RegrFile(
  105. CDirectory* pDir,
  106. Test * pTest,
  107. TestVariant * pTestVariant
  108. )
  109. {
  110. FILE *fp;
  111. char *p;
  112. const char *opts;
  113. int x;
  114. int rc;
  115. int retval;
  116. char full[MAX_PATH]; // temporary place for full paths
  117. char basename[MAX_PATH]; // base filename
  118. const char *asmdir; // dir of generated asm file
  119. char masterasmbuf[MAX_PATH]; // name of master asm file
  120. char asmbuf[MAX_PATH]; // name of generated asm file
  121. char diffbuf[MAX_PATH]; // name of generated diff file
  122. char objbuf[MAX_PATH]; // name of generated obj file
  123. char fullmasterasmbuf[MAX_PATH]; // path of master asm file
  124. char fullasmbuf[MAX_PATH]; // path of generated asm file
  125. char fulldiffbuf[MAX_PATH]; // path of generated diff file
  126. char fullobjbuf[MAX_PATH]; // path of generated obj file
  127. char standardoptbuf[BUFFER_SIZE]; // standard compilation options (-Fa..)
  128. char cmdbuf[BUFFER_SIZE]; // compilation command
  129. char compilerbuf[BUFFER_SIZE]; // compiler name buffer
  130. char buf[BUFFER_SIZE];
  131. char tmp_file1[BUFFER_SIZE];
  132. char tmp_file2[BUFFER_SIZE];
  133. char tmp_file3[BUFFER_SIZE];
  134. BOOL passFo = FALSE;
  135. BOOL noAsm = FALSE;
  136. // We don't allow non-target conditions for ASM currently, so we should
  137. // never see a conditionNodeList.
  138. ASSERTNR(pTest->conditionNodeList == NULL);
  139. // Check the directives.
  140. if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM,
  141. PASS_FO_CMD))
  142. {
  143. passFo = TRUE;
  144. }
  145. if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM,
  146. NO_ASM_CMD))
  147. {
  148. noAsm = TRUE;
  149. }
  150. // Base file name (name.* -> name)
  151. strcpy_s(basename, pTest->name);
  152. for(p = basename; *p != '.' && *p != '\0'; p++);
  153. *p = '\0';
  154. // Determine the standard options.
  155. asmdir = pDir->IsBaseline() ? MASTER_DIR : DIFF_DIR;
  156. sprintf_s(standardoptbuf, "-AsmDumpMode:%s\\%s.asm",
  157. asmdir, basename);
  158. switch (TargetMachine) {
  159. case TM_WVM:
  160. case TM_WVMX86:
  161. case TM_WVM64:
  162. strcat_s(standardoptbuf, " /BC ");
  163. break;
  164. }
  165. if (BaseCompiler || DiffCompiler) {
  166. char *b2;
  167. if (pDir->IsBaseline())
  168. b2 = BaseCompiler;
  169. else
  170. b2 = DiffCompiler;
  171. ASSERTNR(b2);
  172. sprintf_s(compilerbuf, " -B2 %s", b2);
  173. }
  174. else
  175. {
  176. *compilerbuf = '\0';
  177. }
  178. // Try to open the source file.
  179. sprintf_s(full, "%s\\%s", pDir->GetDirectoryPath(), pTest->name);
  180. fp = fopen_unsafe(full, "r");
  181. if (fp == NULL) {
  182. LogError("ERROR: Could not open '%s' with error '%s'", pTest->name, strerror_unsafe(errno));
  183. return -1;
  184. }
  185. fclose(fp);
  186. ASSERTNR(pTestVariant->optFlags == NULL);
  187. ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL);
  188. opts = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS];
  189. if (opts == NULL)
  190. {
  191. opts = "";
  192. }
  193. // Compute the object name if passing -Fo through.
  194. if (passFo)
  195. sprintf_s(objbuf, "%s.obj", basename);
  196. else
  197. sprintf_s(objbuf, "%s\\%s.obj", asmdir, basename);
  198. sprintf_s(fullobjbuf, "%s\\%s", pDir->GetDirectoryPath(), objbuf);
  199. // Compute the names for the assembly and diff files.
  200. sprintf_s(masterasmbuf, "%s\\%s.asm", MASTER_DIR, basename);
  201. sprintf_s(asmbuf, "%s\\%s.asm", asmdir, basename);
  202. sprintf_s(diffbuf, "%s\\%s.d", DIFF_DIR, basename);
  203. sprintf_s(fullmasterasmbuf, "%s\\%s", pDir->GetDirectoryPath(), masterasmbuf);
  204. sprintf_s(fullasmbuf, "%s\\%s", pDir->GetDirectoryPath(), asmbuf);
  205. sprintf_s(fulldiffbuf, "%s\\%s", pDir->GetDirectoryPath(), diffbuf);
  206. // There could be a diff file from previous regressions. Remove it now.
  207. if (!pDir->IsBaseline())
  208. DeleteFileIfFound(fulldiffbuf);
  209. // Create the compiler command and do the compile.
  210. sprintf_s(cmdbuf, "%s%s %s %s %s %s 2>nul 1>nul",
  211. JCBinary, compilerbuf, standardoptbuf, opts, EXTRA_CC_FLAGS,
  212. pTest->name);
  213. if (FVerbose) {
  214. strcpy_s(buf, cmdbuf);
  215. }
  216. else {
  217. // For non-verbose, don't display the standard options
  218. sprintf_s(buf, "%s%s %s %s %s",
  219. JCBinary, compilerbuf, opts, EXTRA_CC_FLAGS, pTest->name);
  220. }
  221. Message("%s", buf);
  222. if (FTest)
  223. {
  224. return 0;
  225. }
  226. // There could be a diff file from previous regressions. Remove it now.
  227. if (!pDir->IsBaseline())
  228. {
  229. DeleteFileIfFound(fulldiffbuf);
  230. }
  231. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf))
  232. {
  233. DeleteFileIfFound(fullasmbuf);
  234. return -1;
  235. }
  236. // If we are passing -Fo through to the compiler, remove the generated
  237. // object file.
  238. if (passFo)
  239. DeleteFileRetryMsg(fullobjbuf);
  240. // See if we generated an assembly file.
  241. if (GetFileAttributes(fullasmbuf) == INVALID_FILE_ATTRIBUTES) {
  242. LogOut("ERROR: Assembly file %s not generated", asmbuf);
  243. return -1;
  244. }
  245. // Create the assembler command and do the asm if necessary.
  246. if (REGR_ASM && !noAsm) {
  247. sprintf_s(cmdbuf, "%s %s", REGR_ASM, asmbuf);
  248. Message("%s", cmdbuf);
  249. // ignore the REGR_ASM return code; we still want to do diffs or
  250. // update the master with the generated asm file.
  251. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  252. retval = -1;
  253. }
  254. else {
  255. retval = 0;
  256. DeleteFileRetryMsg(fullobjbuf);
  257. }
  258. } else {
  259. retval = 0;
  260. }
  261. // Exit if not comparing to master or doing baselines.
  262. if (NoMasterCompare || pDir->IsBaseline())
  263. return retval;
  264. // FYI: Up to this point we haven't used fullmasterasmbuf.
  265. ASSERTNR(!strcmp(asmdir, DIFF_DIR));
  266. // If the master doesn't exist, consider this a failure.
  267. if (GetFileAttributes(fullmasterasmbuf) == INVALID_FILE_ATTRIBUTES) {
  268. LogOut("ERROR: %s doesn't exist", fullmasterasmbuf);
  269. return -1;
  270. }
  271. x = DoCompare(fullasmbuf, fullmasterasmbuf, pTestVariant->testInfo.hasData[TIK_EOL_NORMALIZATION]);
  272. if (x < 0)
  273. return -1;
  274. // If the files differ, remove potential bogus differences and try again.
  275. rc = retval;
  276. if (x) {
  277. // Remove the possible bogus diffs.
  278. // Generate some temporary file names.
  279. // Note: these are full pathnames, not relative pathnames.
  280. // NOTE: BE VERY CAREFUL TO CLEAN THESE UP!
  281. if (mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file1) == NULL ||
  282. mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file2) == NULL ||
  283. mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file3) == NULL) {
  284. Fatal("Unable to create temporary files");
  285. }
  286. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file1);
  287. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file2);
  288. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file3);
  289. sprintf_s(cmdbuf, "%s %s %s %s",
  290. REGR_SHOWD, REGR_PERL, fullasmbuf, tmp_file1);
  291. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  292. rc = -1;
  293. }
  294. else {
  295. sprintf_s(cmdbuf, "%s %s %s %s",
  296. REGR_SHOWD, REGR_PERL, fullmasterasmbuf, tmp_file2);
  297. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  298. rc = -1;
  299. }
  300. else {
  301. // Compare again.
  302. x = DoCompare(tmp_file1, tmp_file2, pTestVariant->testInfo.hasData[TIK_EOL_NORMALIZATION]);
  303. if (x < 0) {
  304. rc = -1;
  305. }
  306. else if (x) {
  307. // They still differ, make a diff file.
  308. sprintf_s(cmdbuf, "%s %s %s > %s",
  309. REGR_DIFF, tmp_file1, tmp_file2, tmp_file3);
  310. DoCommand(pDir->GetDirectoryPath(), cmdbuf, false);
  311. sprintf_s(cmdbuf,"%s -p -e \"s/^[<>-].*\\r?\\n//\" < %s > %s",
  312. REGR_PERL, tmp_file3, fulldiffbuf);
  313. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  314. rc = -1;
  315. }
  316. else {
  317. Message("%s and %s differ", asmbuf, masterasmbuf);
  318. WriteLog("%s has diffs", pTest->name);
  319. rc = 1;
  320. }
  321. }
  322. else {
  323. // Move the new asm into the master directory
  324. DeleteFileRetryMsg(fullmasterasmbuf);
  325. if (!MoveFile(fullasmbuf, fullmasterasmbuf)) {
  326. LogOut("ERROR: MoveFile(%s, %s) failed with error %d",
  327. fullasmbuf, fullmasterasmbuf, GetLastError());
  328. rc = -1;
  329. }
  330. }
  331. }
  332. }
  333. ThreadInfo[ThreadId].DeleteTmpFileList();
  334. }
  335. else {
  336. // They don't differ, so delete the assembly file we just created.
  337. DeleteFileRetryMsg(fullasmbuf);
  338. }
  339. return rc;
  340. }