Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / jit / JitSpewer.cpp
blob6fcd25d6e32fdfaa09522f1a1fa78e07fd21260d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifdef JS_JITSPEW
9 # include "jit/JitSpewer.h"
11 # include "mozilla/Atomics.h"
12 # include "mozilla/Sprintf.h"
14 # include "jit/MIR.h"
15 # include "jit/MIRGenerator.h"
16 # include "jit/MIRGraph.h"
17 # include "threading/LockGuard.h"
18 # include "util/GetPidProvider.h" // getpid()
19 # include "vm/MutexIDs.h"
21 # ifndef JIT_SPEW_DIR
22 # if defined(_WIN32)
23 # define JIT_SPEW_DIR "."
24 # elif defined(__ANDROID__)
25 # define JIT_SPEW_DIR "/data/local/tmp"
26 # else
27 # define JIT_SPEW_DIR "/tmp"
28 # endif
29 # endif
31 using namespace js;
32 using namespace js::jit;
34 class IonSpewer {
35 private:
36 Mutex outputLock_ MOZ_UNANNOTATED;
37 Fprinter jsonOutput_;
38 bool firstFunction_;
39 bool asyncLogging_;
40 bool inited_;
42 void release();
44 public:
45 IonSpewer()
46 : outputLock_(mutexid::IonSpewer),
47 firstFunction_(false),
48 asyncLogging_(false),
49 inited_(false) {}
51 // File output is terminated safely upon destruction.
52 ~IonSpewer();
54 bool init();
55 bool isEnabled() { return inited_; }
56 void setAsyncLogging(bool incremental) { asyncLogging_ = incremental; }
57 bool getAsyncLogging() { return asyncLogging_; }
59 void beginFunction();
60 void spewPass(GraphSpewer* gs);
61 void endFunction(GraphSpewer* gs);
64 // IonSpewer singleton.
65 static IonSpewer ionspewer;
67 static bool LoggingChecked = false;
68 static_assert(JitSpew_Terminator <= 64,
69 "Increase the size of the LoggingBits global.");
70 static uint64_t LoggingBits = 0;
71 static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
73 static const char* const ChannelNames[] = {
74 # define JITSPEW_CHANNEL(name) #name,
75 JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
76 # undef JITSPEW_CHANNEL
79 static size_t ChannelIndentLevel[] = {
80 # define JITSPEW_CHANNEL(name) 0,
81 JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
82 # undef JITSPEW_CHANNEL
85 // The IONFILTER environment variable specifies an expression to select only
86 // certain functions for spewing to reduce amount of log data generated.
87 static const char* gSpewFilter = nullptr;
89 static bool FilterContainsLocation(JSScript* function) {
90 // If there is no filter we accept all outputs.
91 if (!gSpewFilter || !gSpewFilter[0]) {
92 return true;
95 // Disable wasm output when filter is set.
96 if (!function) {
97 return false;
100 const char* filename = function->filename();
101 const size_t line = function->lineno();
102 const size_t filelen = strlen(filename);
103 const char* index = strstr(gSpewFilter, filename);
104 while (index) {
105 if (index == gSpewFilter || index[-1] == ',') {
106 if (index[filelen] == 0 || index[filelen] == ',') {
107 return true;
109 if (index[filelen] == ':' && line != size_t(-1)) {
110 size_t read_line = strtoul(&index[filelen + 1], nullptr, 10);
111 if (read_line == line) {
112 return true;
116 index = strstr(index + filelen, filename);
118 return false;
121 void jit::EnableIonDebugSyncLogging() {
122 ionspewer.init();
123 ionspewer.setAsyncLogging(false);
124 EnableChannel(JitSpew_IonSyncLogs);
127 void jit::EnableIonDebugAsyncLogging() {
128 ionspewer.init();
129 ionspewer.setAsyncLogging(true);
132 void IonSpewer::release() {
133 if (jsonOutput_.isInitialized()) {
134 jsonOutput_.finish();
136 inited_ = false;
139 bool IonSpewer::init() {
140 if (inited_) {
141 return true;
144 // Filter expression for spewing
145 gSpewFilter = getenv("IONFILTER");
147 const size_t bufferLength = 256;
148 char jsonBuffer[bufferLength];
149 const char* jsonFilename = JIT_SPEW_DIR "/ion.json";
151 const char* usePid = getenv("ION_SPEW_BY_PID");
152 if (usePid && *usePid != 0) {
153 uint32_t pid = getpid();
154 size_t len;
155 len = SprintfLiteral(jsonBuffer, JIT_SPEW_DIR "/ion%" PRIu32 ".json", pid);
156 if (bufferLength <= len) {
157 fprintf(stderr, "Warning: IonSpewer::init: Cannot serialize file name.");
158 return false;
160 jsonFilename = jsonBuffer;
163 if (!jsonOutput_.init(jsonFilename)) {
164 release();
165 return false;
168 jsonOutput_.printf("{\n \"functions\": [\n");
169 firstFunction_ = true;
171 inited_ = true;
172 return true;
175 void IonSpewer::beginFunction() {
176 // If we are doing a synchronous logging then we spew everything as we go,
177 // as this is useful in case of failure during the compilation. On the other
178 // hand, it is recommended to disable off thread compilation.
179 if (!getAsyncLogging() && !firstFunction_) {
180 LockGuard<Mutex> guard(outputLock_);
181 jsonOutput_.put(","); // separate functions
185 void IonSpewer::spewPass(GraphSpewer* gs) {
186 if (!getAsyncLogging()) {
187 LockGuard<Mutex> guard(outputLock_);
188 gs->dump(jsonOutput_);
192 void IonSpewer::endFunction(GraphSpewer* gs) {
193 LockGuard<Mutex> guard(outputLock_);
194 if (getAsyncLogging() && !firstFunction_) {
195 jsonOutput_.put(","); // separate functions
198 gs->dump(jsonOutput_);
199 firstFunction_ = false;
202 IonSpewer::~IonSpewer() {
203 if (!inited_) {
204 return;
207 jsonOutput_.printf("\n]}\n");
208 release();
211 GraphSpewer::GraphSpewer(TempAllocator* alloc)
212 : graph_(nullptr),
213 jsonPrinter_(alloc->lifoAlloc()),
214 jsonSpewer_(jsonPrinter_) {}
216 void GraphSpewer::init(MIRGraph* graph, JSScript* function) {
217 MOZ_ASSERT(!isSpewing());
218 if (!ionspewer.isEnabled()) {
219 return;
222 if (!FilterContainsLocation(function)) {
223 // filter out logs during the compilation.
224 filteredOutCompilations++;
225 MOZ_ASSERT(!isSpewing());
226 return;
229 graph_ = graph;
230 MOZ_ASSERT(isSpewing());
233 void GraphSpewer::beginFunction(JSScript* function) {
234 if (!isSpewing()) {
235 return;
237 jsonSpewer_.beginFunction(function);
238 ionspewer.beginFunction();
241 void GraphSpewer::beginWasmFunction(unsigned funcIndex) {
242 if (!isSpewing()) {
243 return;
245 jsonSpewer_.beginWasmFunction(funcIndex);
246 ionspewer.beginFunction();
249 void GraphSpewer::spewPass(const char* pass) {
250 if (!isSpewing()) {
251 return;
254 jsonSpewer_.beginPass(pass);
255 jsonSpewer_.spewMIR(graph_);
256 jsonSpewer_.spewLIR(graph_);
257 jsonSpewer_.endPass();
259 ionspewer.spewPass(this);
261 // As this function is used for debugging, we ignore any of the previous
262 // failures and ensure there is enough ballast space, such that we do not
263 // exhaust the ballast space before running the next phase.
264 AutoEnterOOMUnsafeRegion oomUnsafe;
265 if (!graph_->alloc().ensureBallast()) {
266 oomUnsafe.crash(
267 "Could not ensure enough ballast space after spewing graph "
268 "information.");
272 void GraphSpewer::spewPass(const char* pass, BacktrackingAllocator* ra) {
273 if (!isSpewing()) {
274 return;
277 jsonSpewer_.beginPass(pass);
278 jsonSpewer_.spewMIR(graph_);
279 jsonSpewer_.spewLIR(graph_);
280 jsonSpewer_.spewRanges(ra);
281 jsonSpewer_.endPass();
283 ionspewer.spewPass(this);
286 void GraphSpewer::endFunction() {
287 if (!ionspewer.isEnabled()) {
288 return;
291 if (!isSpewing()) {
292 MOZ_ASSERT(filteredOutCompilations != 0);
293 filteredOutCompilations--;
294 return;
297 jsonSpewer_.endFunction();
299 ionspewer.endFunction(this);
300 graph_ = nullptr;
303 void GraphSpewer::dump(Fprinter& jsonOut) {
304 if (!jsonPrinter_.hadOutOfMemory()) {
305 jsonPrinter_.exportInto(jsonOut);
306 } else {
307 jsonOut.put("{}");
309 jsonOut.flush();
310 jsonPrinter_.clear();
313 void jit::SpewBeginFunction(MIRGenerator* mir, JSScript* function) {
314 MIRGraph* graph = &mir->graph();
315 mir->graphSpewer().init(graph, function);
316 mir->graphSpewer().beginFunction(function);
319 void jit::SpewBeginWasmFunction(MIRGenerator* mir, unsigned funcIndex) {
320 MIRGraph* graph = &mir->graph();
321 mir->graphSpewer().init(graph, nullptr);
322 mir->graphSpewer().beginWasmFunction(funcIndex);
325 AutoSpewEndFunction::~AutoSpewEndFunction() {
326 mir_->graphSpewer().endFunction();
329 Fprinter& jit::JitSpewPrinter() {
330 static Fprinter out;
331 return out;
334 static void PrintHelpAndExit(int status = 0) {
335 fflush(nullptr);
336 printf(
337 "\n"
338 "usage: IONFLAGS=option,option,option,... where options can be:\n"
339 "\n"
340 " aborts Compilation abort messages\n"
341 " scripts Compiled scripts\n"
342 " mir MIR information\n"
343 " prune Prune unused branches\n"
344 " escape Escape analysis\n"
345 " alias Alias analysis\n"
346 " alias-sum Alias analysis: shows summaries for every block\n"
347 " gvn Global Value Numbering\n"
348 " licm Loop invariant code motion\n"
349 " flac Fold linear arithmetic constants\n"
350 " eaa Effective address analysis\n"
351 " sink Sink transformation\n"
352 " regalloc Register allocation\n"
353 " inline Inlining\n"
354 " snapshots Snapshot information\n"
355 " codegen Native code generation\n"
356 " bailouts Bailouts\n"
357 " caches Inline caches\n"
358 " osi Invalidation\n"
359 " safepoints Safepoints\n"
360 " pools Literal Pools (ARM only for now)\n"
361 " cacheflush Instruction Cache flushes (ARM only for now)\n"
362 " range Range Analysis\n"
363 " wasmbce Wasm Bounds Check Elimination\n"
364 " shapeguards Redundant shape guard elimination\n"
365 " gcbarriers Redundant GC barrier elimination\n"
366 " loadkeys Loads used as property keys\n"
367 " logs JSON visualization logging\n"
368 " logs-sync Same as logs, but flushes between each pass (sync. "
369 "compiled functions only).\n"
370 " profiling Profiling-related information\n"
371 " dump-mir-expr Dump the MIR expressions\n"
372 " scriptstats Tracelogger summary stats\n"
373 " warp-snapshots WarpSnapshots created by WarpOracle\n"
374 " warp-transpiler Warp CacheIR transpiler\n"
375 " warp-trial-inlining Trial inlining for Warp\n"
376 " all Everything\n"
377 "\n"
378 " bl-aborts Baseline compiler abort messages\n"
379 " bl-scripts Baseline script-compilation\n"
380 " bl-op Baseline compiler detailed op-specific messages\n"
381 " bl-ic Baseline inline-cache messages\n"
382 " bl-ic-fb Baseline IC fallback stub messages\n"
383 " bl-osr Baseline IC OSR messages\n"
384 " bl-bails Baseline bailouts\n"
385 " bl-dbg-osr Baseline debug mode on stack recompile messages\n"
386 " bl-all All baseline spew\n"
387 "\n"
388 "See also SPEW=help for information on the Structured Spewer."
389 "\n");
390 exit(status);
393 static bool IsFlag(const char* found, const char* flag) {
394 return strlen(found) == strlen(flag) && strcmp(found, flag) == 0;
397 void jit::CheckLogging() {
398 if (LoggingChecked) {
399 return;
402 LoggingChecked = true;
404 char* env = getenv("IONFLAGS");
405 if (!env) {
406 return;
409 const char* found = strtok(env, ",");
410 while (found) {
411 fprintf(stderr, "found tag: %s\n", found);
412 // We're at the end of a flag; check if the previous substring was a
413 // known flag (i-1 is the last character of the flag we just read).
414 if (IsFlag(found, "help")) {
415 PrintHelpAndExit();
416 } else if (IsFlag(found, "aborts")) {
417 EnableChannel(JitSpew_IonAbort);
418 } else if (IsFlag(found, "prune")) {
419 EnableChannel(JitSpew_Prune);
420 } else if (IsFlag(found, "escape")) {
421 EnableChannel(JitSpew_Escape);
422 } else if (IsFlag(found, "alias")) {
423 EnableChannel(JitSpew_Alias);
424 } else if (IsFlag(found, "alias-sum")) {
425 EnableChannel(JitSpew_AliasSummaries);
426 } else if (IsFlag(found, "scripts")) {
427 EnableChannel(JitSpew_IonScripts);
428 } else if (IsFlag(found, "mir")) {
429 EnableChannel(JitSpew_IonMIR);
430 } else if (IsFlag(found, "gvn")) {
431 EnableChannel(JitSpew_GVN);
432 } else if (IsFlag(found, "range")) {
433 EnableChannel(JitSpew_Range);
434 } else if (IsFlag(found, "wasmbce")) {
435 EnableChannel(JitSpew_WasmBCE);
436 } else if (IsFlag(found, "licm")) {
437 EnableChannel(JitSpew_LICM);
438 } else if (IsFlag(found, "flac")) {
439 EnableChannel(JitSpew_FLAC);
440 } else if (IsFlag(found, "eaa")) {
441 EnableChannel(JitSpew_EAA);
442 } else if (IsFlag(found, "sink")) {
443 EnableChannel(JitSpew_Sink);
444 } else if (IsFlag(found, "regalloc")) {
445 EnableChannel(JitSpew_RegAlloc);
446 } else if (IsFlag(found, "inline")) {
447 EnableChannel(JitSpew_Inlining);
448 } else if (IsFlag(found, "snapshots")) {
449 EnableChannel(JitSpew_IonSnapshots);
450 } else if (IsFlag(found, "codegen")) {
451 EnableChannel(JitSpew_Codegen);
452 } else if (IsFlag(found, "bailouts")) {
453 EnableChannel(JitSpew_IonBailouts);
454 } else if (IsFlag(found, "osi")) {
455 EnableChannel(JitSpew_IonInvalidate);
456 } else if (IsFlag(found, "caches")) {
457 EnableChannel(JitSpew_IonIC);
458 } else if (IsFlag(found, "safepoints")) {
459 EnableChannel(JitSpew_Safepoints);
460 } else if (IsFlag(found, "pools")) {
461 EnableChannel(JitSpew_Pools);
462 } else if (IsFlag(found, "cacheflush")) {
463 EnableChannel(JitSpew_CacheFlush);
464 } else if (IsFlag(found, "shapeguards")) {
465 EnableChannel(JitSpew_RedundantShapeGuards);
466 } else if (IsFlag(found, "gcbarriers")) {
467 EnableChannel(JitSpew_RedundantGCBarriers);
468 } else if (IsFlag(found, "loadkeys")) {
469 EnableChannel(JitSpew_MarkLoadsUsedAsPropertyKeys);
470 } else if (IsFlag(found, "logs")) {
471 EnableIonDebugAsyncLogging();
472 } else if (IsFlag(found, "logs-sync")) {
473 EnableIonDebugSyncLogging();
474 } else if (IsFlag(found, "profiling")) {
475 EnableChannel(JitSpew_Profiling);
476 } else if (IsFlag(found, "dump-mir-expr")) {
477 EnableChannel(JitSpew_MIRExpressions);
478 } else if (IsFlag(found, "scriptstats")) {
479 EnableChannel(JitSpew_ScriptStats);
480 } else if (IsFlag(found, "warp-snapshots")) {
481 EnableChannel(JitSpew_WarpSnapshots);
482 } else if (IsFlag(found, "warp-transpiler")) {
483 EnableChannel(JitSpew_WarpTranspiler);
484 } else if (IsFlag(found, "warp-trial-inlining")) {
485 EnableChannel(JitSpew_WarpTrialInlining);
486 } else if (IsFlag(found, "all")) {
487 LoggingBits = uint64_t(-1);
488 } else if (IsFlag(found, "bl-aborts")) {
489 EnableChannel(JitSpew_BaselineAbort);
490 } else if (IsFlag(found, "bl-scripts")) {
491 EnableChannel(JitSpew_BaselineScripts);
492 } else if (IsFlag(found, "bl-op")) {
493 EnableChannel(JitSpew_BaselineOp);
494 } else if (IsFlag(found, "bl-ic")) {
495 EnableChannel(JitSpew_BaselineIC);
496 } else if (IsFlag(found, "bl-ic-fb")) {
497 EnableChannel(JitSpew_BaselineICFallback);
498 } else if (IsFlag(found, "bl-osr")) {
499 EnableChannel(JitSpew_BaselineOSR);
500 } else if (IsFlag(found, "bl-bails")) {
501 EnableChannel(JitSpew_BaselineBailouts);
502 } else if (IsFlag(found, "bl-dbg-osr")) {
503 EnableChannel(JitSpew_BaselineDebugModeOSR);
504 } else if (IsFlag(found, "bl-all")) {
505 EnableChannel(JitSpew_BaselineAbort);
506 EnableChannel(JitSpew_BaselineScripts);
507 EnableChannel(JitSpew_BaselineOp);
508 EnableChannel(JitSpew_BaselineIC);
509 EnableChannel(JitSpew_BaselineICFallback);
510 EnableChannel(JitSpew_BaselineOSR);
511 EnableChannel(JitSpew_BaselineBailouts);
512 EnableChannel(JitSpew_BaselineDebugModeOSR);
513 } else {
514 fprintf(stderr, "Unknown flag.\n");
515 PrintHelpAndExit(64);
517 found = strtok(nullptr, ",");
520 FILE* spewfh = stderr;
521 const char* filename = getenv("ION_SPEW_FILENAME");
522 if (filename && *filename) {
523 char actual_filename[2048] = {0};
524 SprintfLiteral(actual_filename, "%s.%d", filename, getpid());
525 spewfh = fopen(actual_filename, "w");
526 MOZ_RELEASE_ASSERT(spewfh);
527 setbuf(spewfh, nullptr); // Make unbuffered
529 JitSpewPrinter().init(spewfh);
532 JitSpewIndent::JitSpewIndent(JitSpewChannel channel) : channel_(channel) {
533 ChannelIndentLevel[channel]++;
536 JitSpewIndent::~JitSpewIndent() { ChannelIndentLevel[channel_]--; }
538 void jit::JitSpewStartVA(JitSpewChannel channel, const char* fmt, va_list ap) {
539 if (!JitSpewEnabled(channel)) {
540 return;
543 JitSpewHeader(channel);
544 Fprinter& out = JitSpewPrinter();
545 out.vprintf(fmt, ap);
548 void jit::JitSpewContVA(JitSpewChannel channel, const char* fmt, va_list ap) {
549 if (!JitSpewEnabled(channel)) {
550 return;
553 Fprinter& out = JitSpewPrinter();
554 out.vprintf(fmt, ap);
557 void jit::JitSpewFin(JitSpewChannel channel) {
558 if (!JitSpewEnabled(channel)) {
559 return;
562 Fprinter& out = JitSpewPrinter();
563 out.put("\n");
566 void jit::JitSpewVA(JitSpewChannel channel, const char* fmt, va_list ap) {
567 JitSpewStartVA(channel, fmt, ap);
568 JitSpewFin(channel);
571 void jit::JitSpew(JitSpewChannel channel, const char* fmt, ...) {
572 va_list ap;
573 va_start(ap, fmt);
574 JitSpewVA(channel, fmt, ap);
575 va_end(ap);
578 void jit::JitSpewDef(JitSpewChannel channel, const char* str,
579 MDefinition* def) {
580 if (!JitSpewEnabled(channel)) {
581 return;
584 JitSpewHeader(channel);
585 Fprinter& out = JitSpewPrinter();
586 out.put(str);
587 def->dump(out);
588 def->dumpLocation(out);
591 void jit::JitSpewStart(JitSpewChannel channel, const char* fmt, ...) {
592 va_list ap;
593 va_start(ap, fmt);
594 JitSpewStartVA(channel, fmt, ap);
595 va_end(ap);
597 void jit::JitSpewCont(JitSpewChannel channel, const char* fmt, ...) {
598 va_list ap;
599 va_start(ap, fmt);
600 JitSpewContVA(channel, fmt, ap);
601 va_end(ap);
604 void jit::JitSpewHeader(JitSpewChannel channel) {
605 if (!JitSpewEnabled(channel)) {
606 return;
609 Fprinter& out = JitSpewPrinter();
610 out.printf("[%s] ", ChannelNames[channel]);
611 for (size_t i = ChannelIndentLevel[channel]; i != 0; i--) {
612 out.put(" ");
616 bool jit::JitSpewEnabled(JitSpewChannel channel) {
617 MOZ_ASSERT(LoggingChecked);
618 return (LoggingBits & (uint64_t(1) << uint32_t(channel))) &&
619 !filteredOutCompilations;
622 void jit::EnableChannel(JitSpewChannel channel) {
623 MOZ_ASSERT(LoggingChecked);
624 LoggingBits |= uint64_t(1) << uint32_t(channel);
627 void jit::DisableChannel(JitSpewChannel channel) {
628 MOZ_ASSERT(LoggingChecked);
629 LoggingBits &= ~(uint64_t(1) << uint32_t(channel));
632 const char* js::jit::ValTypeToString(JSValueType type) {
633 switch (type) {
634 case JSVAL_TYPE_DOUBLE:
635 return "Double";
636 case JSVAL_TYPE_INT32:
637 return "Int32";
638 case JSVAL_TYPE_BOOLEAN:
639 return "Boolean";
640 case JSVAL_TYPE_UNDEFINED:
641 return "Undefined";
642 case JSVAL_TYPE_NULL:
643 return "Null";
644 case JSVAL_TYPE_MAGIC:
645 return "Magic";
646 case JSVAL_TYPE_STRING:
647 return "String";
648 case JSVAL_TYPE_SYMBOL:
649 return "Symbol";
650 case JSVAL_TYPE_PRIVATE_GCTHING:
651 return "PrivateGCThing";
652 case JSVAL_TYPE_BIGINT:
653 return "BigInt";
654 case JSVAL_TYPE_OBJECT:
655 return "Object";
656 case JSVAL_TYPE_UNKNOWN:
657 return "None";
658 default:
659 MOZ_CRASH("Unknown JSValueType");
663 #endif /* JS_JITSPEW */