Output.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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. #include "CommonCorePch.h"
  6. #include <string.h>
  7. #include <stdarg.h>
  8. // Initialization order
  9. // AB AutoSystemInfo
  10. // AD PerfCounter
  11. // AE PerfCounterSet
  12. // AM Output/Configuration
  13. // AN MemProtectHeap
  14. // AP DbgHelpSymbolManager
  15. // AQ CFGLogger
  16. // AR LeakReport
  17. // AS JavascriptDispatch/RecyclerObjectDumper
  18. // AT HeapAllocator/RecyclerHeuristic
  19. // AU RecyclerWriteBarrierManager
  20. #pragma warning(disable:4075) // initializers put in unrecognized initialization area on purpose
  21. #pragma init_seg(".CRT$XCAM")
  22. bool Output::s_useDebuggerWindow = false;
  23. CriticalSection Output::s_critsect;
  24. AutoFILE Output::s_outputFile; // Create a separate output file that is not thread-local.
  25. #ifdef ENABLE_TRACE
  26. Js::ILogger* Output::s_inMemoryLogger = nullptr;
  27. Js::IStackTraceHelper* Output::s_stackTraceHelper = nullptr;
  28. unsigned int Output::s_traceEntryId = 0;
  29. #endif
  30. THREAD_ST FILE* Output::s_file = nullptr;
  31. THREAD_ST char16* Output::buffer = nullptr;
  32. THREAD_ST size_t Output::bufferAllocSize = 0;
  33. THREAD_ST size_t Output::bufferFreeSize = 0;
  34. THREAD_ST size_t Output::s_Column = 0;
  35. THREAD_ST WORD Output::s_color = 0;
  36. THREAD_ST bool Output::s_hasColor = false;
  37. THREAD_ST bool Output::s_capture = false;
  38. #define MAX_OUTPUT_BUFFER_SIZE 10 * 1024 * 1024 // 10 MB maximum before we force a flush
  39. size_t __cdecl
  40. Output::VerboseNote(const char16 * format, ...)
  41. {
  42. #ifdef ENABLE_TRACE
  43. if (Js::Configuration::Global.flags.Verbose)
  44. {
  45. AutoCriticalSection autocs(&s_critsect);
  46. va_list argptr;
  47. va_start(argptr, format);
  48. size_t size = vfwprintf(stdout, format, argptr);
  49. fflush(stdout);
  50. va_end(argptr);
  51. return size;
  52. }
  53. #endif
  54. return 0;
  55. }
  56. #ifdef ENABLE_TRACE
  57. size_t __cdecl
  58. Output::Trace(Js::Phase phase, const char16 *form, ...)
  59. {
  60. size_t retValue = 0;
  61. if(Js::Configuration::Global.flags.Trace.IsEnabled(phase))
  62. {
  63. va_list argptr;
  64. va_start(argptr, form);
  65. retValue += Output::VTrace(_u("%s: "), Js::PhaseNames[static_cast<int>(phase)], form, argptr);
  66. }
  67. return retValue;
  68. }
  69. size_t __cdecl
  70. Output::Trace2(Js::Phase phase, const char16 *form, ...)
  71. {
  72. size_t retValue = 0;
  73. if (Js::Configuration::Global.flags.Trace.IsEnabled(phase))
  74. {
  75. va_list argptr;
  76. va_start(argptr, form);
  77. retValue += Output::VPrint(form, argptr);
  78. }
  79. return retValue;
  80. }
  81. size_t __cdecl
  82. Output::TraceWithPrefix(Js::Phase phase, const char16 prefix[], const char16 *form, ...)
  83. {
  84. size_t retValue = 0;
  85. if (Js::Configuration::Global.flags.Trace.IsEnabled(phase))
  86. {
  87. va_list argptr;
  88. va_start(argptr, form);
  89. WCHAR prefixValue[512];
  90. swprintf_s(prefixValue, _u("%s: %s: "), Js::PhaseNames[static_cast<int>(phase)], prefix);
  91. retValue += Output::VTrace(_u("%s"), prefixValue, form, argptr);
  92. }
  93. return retValue;
  94. }
  95. size_t __cdecl
  96. Output::TraceWithFlush(Js::Phase phase, const char16 *form, ...)
  97. {
  98. size_t retValue = 0;
  99. if(Js::Configuration::Global.flags.Trace.IsEnabled(phase))
  100. {
  101. va_list argptr;
  102. va_start(argptr, form);
  103. retValue += Output::VTrace(_u("%s:"), Js::PhaseNames[static_cast<int>(phase)], form, argptr);
  104. Output::Flush();
  105. }
  106. return retValue;
  107. }
  108. size_t __cdecl
  109. Output::TraceWithFlush(Js::Flag flag, const char16 *form, ...)
  110. {
  111. size_t retValue = 0;
  112. if (Js::Configuration::Global.flags.IsEnabled(flag))
  113. {
  114. va_list argptr;
  115. va_start(argptr, form);
  116. retValue += Output::VTrace(_u("[-%s]::"), Js::FlagNames[static_cast<int>(flag)], form, argptr);
  117. Output::Flush();
  118. }
  119. return retValue;
  120. }
  121. size_t
  122. Output::VTrace(const char16* shortPrefixFormat, const char16* prefix, const char16 *form, va_list argptr)
  123. {
  124. size_t retValue = 0;
  125. if (CONFIG_FLAG(RichTraceFormat))
  126. {
  127. InterlockedIncrement(&s_traceEntryId);
  128. retValue += Output::Print(_u("[%d ~%d %s] "), s_traceEntryId, ::GetCurrentThreadId(), prefix);
  129. }
  130. else
  131. {
  132. retValue += Output::Print(shortPrefixFormat, prefix);
  133. }
  134. retValue += Output::VPrint(form, argptr);
  135. // Print stack trace.
  136. if (s_stackTraceHelper)
  137. {
  138. const ULONG c_framesToSkip = 2; // Skip 2 frames -- Output::VTrace and Output::Trace.
  139. const ULONG c_frameCount = 10; // TODO: make it configurable.
  140. const char16 callStackPrefix[] = _u("call stack:");
  141. if (s_inMemoryLogger)
  142. {
  143. // Trace just addresses of functions, avoid symbol info as it takes too much memory.
  144. // One line for whole stack trace for easier parsing on the jd side.
  145. const size_t c_msgCharCount = _countof(callStackPrefix) + (1 + sizeof(void*) * 2) * c_frameCount; // 2 hexadecimal digits per byte + 1 for space.
  146. char16 callStackMsg[c_msgCharCount];
  147. void* frames[c_frameCount];
  148. size_t start = 0;
  149. size_t temp;
  150. temp = _snwprintf_s(callStackMsg, _countof(callStackMsg), _TRUNCATE, _u("%s"), callStackPrefix);
  151. Assert(temp != -1);
  152. start += temp;
  153. ULONG framesObtained = s_stackTraceHelper->GetStackTrace(c_framesToSkip, c_frameCount, frames);
  154. Assert(framesObtained <= c_frameCount);
  155. for (ULONG i = 0; i < framesObtained && i < c_frameCount; ++i)
  156. {
  157. Assert(_countof(callStackMsg) >= start);
  158. temp = _snwprintf_s(callStackMsg + start, _countof(callStackMsg) - start, _TRUNCATE, _u(" %p"), frames[i]);
  159. Assert(temp != -1);
  160. start += temp;
  161. }
  162. retValue += Output::Print(_u("%s\n"), callStackMsg);
  163. }
  164. else
  165. {
  166. // Trace with full symbol info.
  167. retValue += Output::Print(_u("%s\n"), callStackPrefix);
  168. retValue += s_stackTraceHelper->PrintStackTrace(c_framesToSkip, c_frameCount);
  169. }
  170. }
  171. return retValue;
  172. }
  173. #ifdef BGJIT_STATS
  174. size_t __cdecl
  175. Output::TraceStats(Js::Phase phase, const char16 *form, ...)
  176. {
  177. if(PHASE_STATS1(phase))
  178. {
  179. va_list argptr;
  180. va_start(argptr, form);
  181. return Output::VPrint(form, argptr);
  182. }
  183. return 0;
  184. }
  185. #endif
  186. #endif // ENABLE_TRACE
  187. ///----------------------------------------------------------------------------
  188. ///
  189. /// Output::Print
  190. ///
  191. /// Print the given format string.
  192. ///
  193. ///
  194. ///----------------------------------------------------------------------------
  195. size_t __cdecl
  196. Output::Print(const char16 *form, ...)
  197. {
  198. va_list argptr;
  199. va_start(argptr, form);
  200. return Output::VPrint(form, argptr);
  201. }
  202. size_t __cdecl
  203. Output::Print(int column, const char16 *form, ...)
  204. {
  205. Output::SkipToColumn(column);
  206. va_list argptr;
  207. va_start(argptr, form);
  208. return Output::VPrint(form, argptr);
  209. }
  210. size_t __cdecl
  211. Output::VPrint(const char16 *form, va_list argptr)
  212. {
  213. char16 buf[2048];
  214. size_t size;
  215. size = _vsnwprintf_s(buf, _countof(buf), _TRUNCATE, form, argptr);
  216. if(size == -1)
  217. {
  218. size = 2048;
  219. }
  220. return Output::PrintBuffer(buf, size);
  221. }
  222. size_t __cdecl
  223. Output::PrintBuffer(const char16 * buf, size_t size)
  224. {
  225. Output::s_Column += size;
  226. const char16 * endbuf = wcschr(buf, '\n');
  227. while (endbuf != nullptr)
  228. {
  229. Output::s_Column = size - (endbuf - buf) - 1;
  230. endbuf = wcschr(endbuf + 1, '\n');
  231. }
  232. bool useConsoleOrFile = true;
  233. if (!Output::s_capture)
  234. {
  235. if (Output::s_useDebuggerWindow)
  236. {
  237. OutputDebugStringW(buf);
  238. useConsoleOrFile = false;
  239. }
  240. #ifdef ENABLE_TRACE
  241. if (Output::s_inMemoryLogger)
  242. {
  243. s_inMemoryLogger->Write(buf);
  244. useConsoleOrFile = false;
  245. }
  246. #endif
  247. }
  248. if (useConsoleOrFile)
  249. {
  250. if (s_file == nullptr || Output::s_capture)
  251. {
  252. bool addToBuffer = true;
  253. if (Output::bufferFreeSize < size + 1)
  254. {
  255. if (Output::bufferAllocSize > MAX_OUTPUT_BUFFER_SIZE && !Output::s_capture)
  256. {
  257. Output::Flush();
  258. if (Output::bufferFreeSize < size + 1)
  259. {
  260. DirectPrint(buf);
  261. addToBuffer = false;
  262. }
  263. }
  264. else
  265. {
  266. size_t oldBufferSize = bufferAllocSize - bufferFreeSize;
  267. size_t newBufferAllocSize = (bufferAllocSize + size + 1) * 4 / 3;
  268. char16 * newBuffer = (char16 *)realloc(buffer, (newBufferAllocSize * sizeof(char16)));
  269. if (newBuffer == nullptr)
  270. {
  271. // See if I can just flush it and print directly
  272. Output::Flush();
  273. // Reset the buffer
  274. free(Output::buffer);
  275. Output::buffer = nullptr;
  276. Output::bufferAllocSize = 0;
  277. Output::bufferFreeSize = 0;
  278. // Print it directly
  279. DirectPrint(buf);
  280. addToBuffer = false;
  281. }
  282. else
  283. {
  284. bufferAllocSize = newBufferAllocSize;
  285. buffer = newBuffer;
  286. bufferFreeSize = bufferAllocSize - oldBufferSize;
  287. }
  288. }
  289. }
  290. if (addToBuffer)
  291. {
  292. Assert(Output::bufferFreeSize >= size + 1);
  293. memcpy_s(Output::buffer + Output::bufferAllocSize - Output::bufferFreeSize, Output::bufferFreeSize * sizeof(char16),
  294. buf, (size + 1) * sizeof(char16));
  295. bufferFreeSize -= size;
  296. }
  297. }
  298. else
  299. {
  300. fwprintf_s(Output::s_file, _u("%s"), buf);
  301. }
  302. if(s_outputFile != nullptr && !Output::s_capture)
  303. {
  304. fwprintf_s(s_outputFile, _u("%s"), buf);
  305. }
  306. }
  307. Output::Flush();
  308. return size;
  309. }
  310. void Output::Flush()
  311. {
  312. if (s_capture)
  313. {
  314. return;
  315. }
  316. if (bufferFreeSize != bufferAllocSize)
  317. {
  318. DirectPrint(Output::buffer);
  319. bufferFreeSize = bufferAllocSize;
  320. }
  321. if(s_outputFile != nullptr)
  322. {
  323. fflush(s_outputFile);
  324. }
  325. _flushall();
  326. }
  327. void Output::DirectPrint(char16 const * string)
  328. {
  329. AutoCriticalSection autocs(&s_critsect);
  330. WORD oldValue = 0;
  331. BOOL restoreColor = FALSE;
  332. HANDLE hConsole = NULL;
  333. if (Output::s_hasColor)
  334. {
  335. _CONSOLE_SCREEN_BUFFER_INFO info;
  336. hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  337. if (hConsole && GetConsoleScreenBufferInfo(hConsole, &info))
  338. {
  339. oldValue = info.wAttributes;
  340. restoreColor = SetConsoleTextAttribute(hConsole, Output::s_color);
  341. }
  342. }
  343. fwprintf(stdout, _u("%s"), string);
  344. if (restoreColor)
  345. {
  346. SetConsoleTextAttribute(hConsole, oldValue);
  347. }
  348. }
  349. ///----------------------------------------------------------------------------
  350. ///
  351. /// Output::SkipToColumn
  352. ///
  353. /// Inserts spaces up to the column passed in.
  354. ///
  355. ///----------------------------------------------------------------------------
  356. void
  357. Output::SkipToColumn(size_t column)
  358. {
  359. if (column <= Output::s_Column)
  360. {
  361. Output::Print(_u(" "));
  362. return;
  363. }
  364. // compute distance to our destination
  365. size_t dist = column - Output::s_Column;
  366. // Print at least one space
  367. while (dist > 0)
  368. {
  369. Output::Print(_u(" "));
  370. dist--;
  371. }
  372. }
  373. FILE*
  374. Output::GetFile()
  375. {
  376. return Output::s_file;
  377. }
  378. FILE*
  379. Output::SetFile(FILE *file)
  380. {
  381. Output::Flush();
  382. FILE *oldfile = Output::s_file;
  383. Output::s_file = file;
  384. return oldfile;
  385. }
  386. void
  387. Output::SetOutputFile(FILE* file)
  388. {
  389. if(s_outputFile != nullptr)
  390. {
  391. AssertMsg(false, "Output file is being set twice.");
  392. }
  393. else
  394. {
  395. s_outputFile = file;
  396. }
  397. }
  398. FILE*
  399. Output::GetOutputFile()
  400. {
  401. return s_outputFile;
  402. }
  403. #ifdef ENABLE_TRACE
  404. void
  405. Output::SetInMemoryLogger(Js::ILogger* logger)
  406. {
  407. AssertMsg(s_inMemoryLogger == nullptr, "This cannot be called more than once.");
  408. s_inMemoryLogger = logger;
  409. }
  410. void
  411. Output::SetStackTraceHelper(Js::IStackTraceHelper* helper)
  412. {
  413. AssertMsg(s_stackTraceHelper == nullptr, "This cannot be called more than once.");
  414. #ifndef STACK_BACK_TRACE
  415. AssertMsg("STACK_BACK_TRACE must be defined");
  416. #endif
  417. s_stackTraceHelper = helper;
  418. }
  419. #endif // ENABLE_TRACE
  420. //
  421. // Sets the foreground color and returns the old color. Returns 0 on failure
  422. //
  423. WORD
  424. Output::SetConsoleForeground(WORD color)
  425. {
  426. AutoCriticalSection autocs(&s_critsect);
  427. _CONSOLE_SCREEN_BUFFER_INFO info;
  428. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  429. if (hConsole && GetConsoleScreenBufferInfo(hConsole, &info))
  430. {
  431. Output::Flush();
  432. Output::s_color = color | (info.wAttributes & ~15);
  433. Output::s_hasColor = Output::s_color != info.wAttributes;
  434. return info.wAttributes;
  435. }
  436. return 0;
  437. }
  438. void
  439. Output::CaptureStart()
  440. {
  441. Assert(!s_capture);
  442. Output::Flush();
  443. s_capture = true;
  444. }
  445. char16 *
  446. Output::CaptureEnd()
  447. {
  448. Assert(s_capture);
  449. s_capture = false;
  450. bufferFreeSize = 0;
  451. bufferAllocSize = 0;
  452. char16 * returnBuffer = buffer;
  453. buffer = nullptr;
  454. return returnBuffer;
  455. }