rlregr.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  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. char *path, // full path of directory to run command in
  32. 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, *opts;
  112. int x;
  113. int rc;
  114. int retval;
  115. char full[MAX_PATH]; // temporary place for full paths
  116. char basename[MAX_PATH]; // base filename
  117. char *asmdir; // dir of generated asm file
  118. char masterasmbuf[MAX_PATH]; // name of master asm file
  119. char asmbuf[MAX_PATH]; // name of generated asm file
  120. char diffbuf[MAX_PATH]; // name of generated diff file
  121. char objbuf[MAX_PATH]; // name of generated obj file
  122. char fullmasterasmbuf[MAX_PATH]; // path of master asm file
  123. char fullasmbuf[MAX_PATH]; // path of generated asm file
  124. char fulldiffbuf[MAX_PATH]; // path of generated diff file
  125. char fullobjbuf[MAX_PATH]; // path of generated obj file
  126. char standardoptbuf[BUFFER_SIZE]; // standard compilation options (-Fa..)
  127. char cmdbuf[BUFFER_SIZE]; // compilation command
  128. char compilerbuf[BUFFER_SIZE]; // compiler name buffer
  129. char buf[BUFFER_SIZE];
  130. char tmp_file1[BUFFER_SIZE];
  131. char tmp_file2[BUFFER_SIZE];
  132. char tmp_file3[BUFFER_SIZE];
  133. BOOL passFo = FALSE;
  134. BOOL noAsm = FALSE;
  135. // We don't allow non-target conditions for ASM currently, so we should
  136. // never see a conditionNodeList.
  137. ASSERTNR(pTest->conditionNodeList == NULL);
  138. // Check the directives.
  139. if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM,
  140. PASS_FO_CMD))
  141. {
  142. passFo = TRUE;
  143. }
  144. if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM,
  145. NO_ASM_CMD))
  146. {
  147. noAsm = TRUE;
  148. }
  149. // Base file name (name.* -> name)
  150. strcpy_s(basename, pTest->name);
  151. for(p = basename; *p != '.' && *p != '\0'; p++);
  152. *p = '\0';
  153. // Determine the standard options.
  154. asmdir = pDir->IsBaseline() ? MASTER_DIR : DIFF_DIR;
  155. sprintf_s(standardoptbuf, "-AsmDumpMode:%s\\%s.asm",
  156. asmdir, basename);
  157. switch (TargetMachine) {
  158. case TM_WVM:
  159. case TM_WVMX86:
  160. case TM_WVM64:
  161. strcat_s(standardoptbuf, " /BC ");
  162. break;
  163. }
  164. if (BaseCompiler || DiffCompiler) {
  165. char *b2;
  166. if (pDir->IsBaseline())
  167. b2 = BaseCompiler;
  168. else
  169. b2 = DiffCompiler;
  170. ASSERTNR(b2);
  171. sprintf_s(compilerbuf, " -B2 %s", b2);
  172. }
  173. else
  174. {
  175. *compilerbuf = '\0';
  176. }
  177. // Try to open the source file.
  178. sprintf_s(full, "%s\\%s", pDir->GetDirectoryPath(), pTest->name);
  179. fp = fopen_unsafe(full, "r");
  180. if (fp == NULL) {
  181. LogError("ERROR: Could not open '%s' with error '%s'", pTest->name, strerror_unsafe(errno));
  182. return -1;
  183. }
  184. fclose(fp);
  185. ASSERTNR(pTestVariant->optFlags == NULL);
  186. ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL);
  187. opts = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS];
  188. if (opts == NULL)
  189. {
  190. opts = "";
  191. }
  192. // Compute the object name if passing -Fo through.
  193. if (passFo)
  194. sprintf_s(objbuf, "%s.obj", basename);
  195. else
  196. sprintf_s(objbuf, "%s\\%s.obj", asmdir, basename);
  197. sprintf_s(fullobjbuf, "%s\\%s", pDir->GetDirectoryPath(), objbuf);
  198. // Compute the names for the assembly and diff files.
  199. sprintf_s(masterasmbuf, "%s\\%s.asm", MASTER_DIR, basename);
  200. sprintf_s(asmbuf, "%s\\%s.asm", asmdir, basename);
  201. sprintf_s(diffbuf, "%s\\%s.d", DIFF_DIR, basename);
  202. sprintf_s(fullmasterasmbuf, "%s\\%s", pDir->GetDirectoryPath(), masterasmbuf);
  203. sprintf_s(fullasmbuf, "%s\\%s", pDir->GetDirectoryPath(), asmbuf);
  204. sprintf_s(fulldiffbuf, "%s\\%s", pDir->GetDirectoryPath(), diffbuf);
  205. // There could be a diff file from previous regressions. Remove it now.
  206. if (!pDir->IsBaseline())
  207. DeleteFileIfFound(fulldiffbuf);
  208. // Create the compiler command and do the compile.
  209. sprintf_s(cmdbuf, "%s%s %s %s %s %s 2>nul 1>nul",
  210. JCBinary, compilerbuf, standardoptbuf, opts, EXTRA_CC_FLAGS,
  211. pTest->name);
  212. if (FVerbose) {
  213. strcpy_s(buf, cmdbuf);
  214. }
  215. else {
  216. // For non-verbose, don't display the standard options
  217. sprintf_s(buf, "%s%s %s %s %s",
  218. JCBinary, compilerbuf, opts, EXTRA_CC_FLAGS, pTest->name);
  219. }
  220. Message("%s", buf);
  221. if (FTest)
  222. {
  223. return 0;
  224. }
  225. // There could be a diff file from previous regressions. Remove it now.
  226. if (!pDir->IsBaseline())
  227. {
  228. DeleteFileIfFound(fulldiffbuf);
  229. }
  230. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf))
  231. {
  232. DeleteFileIfFound(fullasmbuf);
  233. return -1;
  234. }
  235. // If we are passing -Fo through to the compiler, remove the generated
  236. // object file.
  237. if (passFo)
  238. DeleteFileRetryMsg(fullobjbuf);
  239. // See if we generated an assembly file.
  240. if (GetFileAttributes(fullasmbuf) == INVALID_FILE_ATTRIBUTES) {
  241. LogOut("ERROR: Assembly file %s not generated", asmbuf);
  242. return -1;
  243. }
  244. // Create the assembler command and do the asm if necessary.
  245. if (REGR_ASM && !noAsm) {
  246. sprintf_s(cmdbuf, "%s %s", REGR_ASM, asmbuf);
  247. Message("%s", cmdbuf);
  248. // ignore the REGR_ASM return code; we still want to do diffs or
  249. // update the master with the generated asm file.
  250. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  251. retval = -1;
  252. }
  253. else {
  254. retval = 0;
  255. DeleteFileRetryMsg(fullobjbuf);
  256. }
  257. } else {
  258. retval = 0;
  259. }
  260. // Exit if not comparing to master or doing baselines.
  261. if (NoMasterCompare || pDir->IsBaseline())
  262. return retval;
  263. // FYI: Up to this point we haven't used fullmasterasmbuf.
  264. ASSERTNR(!strcmp(asmdir, DIFF_DIR));
  265. // If the master doesn't exist, consider this a failure.
  266. if (GetFileAttributes(fullmasterasmbuf) == INVALID_FILE_ATTRIBUTES) {
  267. LogOut("ERROR: %s doesn't exist", fullmasterasmbuf);
  268. return -1;
  269. }
  270. x = DoCompare(fullasmbuf, fullmasterasmbuf);
  271. if (x < 0)
  272. return -1;
  273. // If the files differ, remove potential bogus differences and try again.
  274. rc = retval;
  275. if (x) {
  276. // Remove the possible bogus diffs.
  277. // Generate some temporary file names.
  278. // Note: these are full pathnames, not relative pathnames.
  279. // NOTE: BE VERY CAREFUL TO CLEAN THESE UP!
  280. if (mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file1) == NULL ||
  281. mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file2) == NULL ||
  282. mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file3) == NULL) {
  283. Fatal("Unable to create temporary files");
  284. }
  285. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file1);
  286. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file2);
  287. ThreadInfo[ThreadId].AddToTmpFileList(tmp_file3);
  288. sprintf_s(cmdbuf, "%s %s %s %s",
  289. REGR_SHOWD, REGR_PERL, fullasmbuf, tmp_file1);
  290. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  291. rc = -1;
  292. }
  293. else {
  294. sprintf_s(cmdbuf, "%s %s %s %s",
  295. REGR_SHOWD, REGR_PERL, fullmasterasmbuf, tmp_file2);
  296. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  297. rc = -1;
  298. }
  299. else {
  300. // Compare again.
  301. x = DoCompare(tmp_file1, tmp_file2);
  302. if (x < 0) {
  303. rc = -1;
  304. }
  305. else if (x) {
  306. // They still differ, make a diff file.
  307. sprintf_s(cmdbuf, "%s %s %s > %s",
  308. REGR_DIFF, tmp_file1, tmp_file2, tmp_file3);
  309. DoCommand(pDir->GetDirectoryPath(), cmdbuf, false);
  310. sprintf_s(cmdbuf,"%s -p -e \"s/^[<>-].*\\r?\\n//\" < %s > %s",
  311. REGR_PERL, tmp_file3, fulldiffbuf);
  312. if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) {
  313. rc = -1;
  314. }
  315. else {
  316. Message("%s and %s differ", asmbuf, masterasmbuf);
  317. WriteLog("%s has diffs", pTest->name);
  318. rc = 1;
  319. }
  320. }
  321. else {
  322. // Move the new asm into the master directory
  323. DeleteFileRetryMsg(fullmasterasmbuf);
  324. if (!MoveFile(fullasmbuf, fullmasterasmbuf)) {
  325. LogOut("ERROR: MoveFile(%s, %s) failed with error %d",
  326. fullasmbuf, fullmasterasmbuf, GetLastError());
  327. rc = -1;
  328. }
  329. }
  330. }
  331. }
  332. ThreadInfo[ThreadId].DeleteTmpFileList();
  333. }
  334. else {
  335. // They don't differ, so delete the assembly file we just created.
  336. DeleteFileRetryMsg(fullasmbuf);
  337. }
  338. return rc;
  339. }