Bug 1890750 - Part 1: Include NATIVE_JIT_ENTRY in FunctionFlags::HasJitEntryFlags...
[gecko.git] / js / src / jit / JitSpewer.cpp
blob11e316524065f7447276185f80d07b6c3e9a4bfa
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 " warp-snapshots WarpSnapshots created by WarpOracle\n"
373 " warp-transpiler Warp CacheIR transpiler\n"
374 " warp-trial-inlining Trial inlining for Warp\n"
375 " all Everything\n"
376 "\n"
377 " bl-aborts Baseline compiler abort messages\n"
378 " bl-scripts Baseline script-compilation\n"
379 " bl-op Baseline compiler detailed op-specific messages\n"
380 " bl-ic Baseline inline-cache messages\n"
381 " bl-ic-fb Baseline IC fallback stub messages\n"
382 " bl-osr Baseline IC OSR messages\n"
383 " bl-bails Baseline bailouts\n"
384 " bl-dbg-osr Baseline debug mode on stack recompile messages\n"
385 " bl-all All baseline spew\n"
386 "\n"
387 "See also SPEW=help for information on the Structured Spewer."
388 "\n");
389 exit(status);
392 static bool IsFlag(const char* found, const char* flag) {
393 return strlen(found) == strlen(flag) && strcmp(found, flag) == 0;
396 void jit::CheckLogging() {
397 if (LoggingChecked) {
398 return;
401 LoggingChecked = true;
403 char* env = getenv("IONFLAGS");
404 if (!env) {
405 return;
408 const char* found = strtok(env, ",");
409 while (found) {
410 fprintf(stderr, "found tag: %s\n", found);
411 // We're at the end of a flag; check if the previous substring was a
412 // known flag (i-1 is the last character of the flag we just read).
413 if (IsFlag(found, "help")) {
414 PrintHelpAndExit();
415 } else if (IsFlag(found, "aborts")) {
416 EnableChannel(JitSpew_IonAbort);
417 } else if (IsFlag(found, "prune")) {
418 EnableChannel(JitSpew_Prune);
419 } else if (IsFlag(found, "escape")) {
420 EnableChannel(JitSpew_Escape);
421 } else if (IsFlag(found, "alias")) {
422 EnableChannel(JitSpew_Alias);
423 } else if (IsFlag(found, "alias-sum")) {
424 EnableChannel(JitSpew_AliasSummaries);
425 } else if (IsFlag(found, "scripts")) {
426 EnableChannel(JitSpew_IonScripts);
427 } else if (IsFlag(found, "mir")) {
428 EnableChannel(JitSpew_IonMIR);
429 } else if (IsFlag(found, "gvn")) {
430 EnableChannel(JitSpew_GVN);
431 } else if (IsFlag(found, "range")) {
432 EnableChannel(JitSpew_Range);
433 } else if (IsFlag(found, "wasmbce")) {
434 EnableChannel(JitSpew_WasmBCE);
435 } else if (IsFlag(found, "licm")) {
436 EnableChannel(JitSpew_LICM);
437 } else if (IsFlag(found, "flac")) {
438 EnableChannel(JitSpew_FLAC);
439 } else if (IsFlag(found, "eaa")) {
440 EnableChannel(JitSpew_EAA);
441 } else if (IsFlag(found, "sink")) {
442 EnableChannel(JitSpew_Sink);
443 } else if (IsFlag(found, "regalloc")) {
444 EnableChannel(JitSpew_RegAlloc);
445 } else if (IsFlag(found, "inline")) {
446 EnableChannel(JitSpew_Inlining);
447 } else if (IsFlag(found, "snapshots")) {
448 EnableChannel(JitSpew_IonSnapshots);
449 } else if (IsFlag(found, "codegen")) {
450 EnableChannel(JitSpew_Codegen);
451 } else if (IsFlag(found, "bailouts")) {
452 EnableChannel(JitSpew_IonBailouts);
453 } else if (IsFlag(found, "osi")) {
454 EnableChannel(JitSpew_IonInvalidate);
455 } else if (IsFlag(found, "caches")) {
456 EnableChannel(JitSpew_IonIC);
457 } else if (IsFlag(found, "safepoints")) {
458 EnableChannel(JitSpew_Safepoints);
459 } else if (IsFlag(found, "pools")) {
460 EnableChannel(JitSpew_Pools);
461 } else if (IsFlag(found, "cacheflush")) {
462 EnableChannel(JitSpew_CacheFlush);
463 } else if (IsFlag(found, "shapeguards")) {
464 EnableChannel(JitSpew_RedundantShapeGuards);
465 } else if (IsFlag(found, "gcbarriers")) {
466 EnableChannel(JitSpew_RedundantGCBarriers);
467 } else if (IsFlag(found, "loadkeys")) {
468 EnableChannel(JitSpew_MarkLoadsUsedAsPropertyKeys);
469 } else if (IsFlag(found, "logs")) {
470 EnableIonDebugAsyncLogging();
471 } else if (IsFlag(found, "logs-sync")) {
472 EnableIonDebugSyncLogging();
473 } else if (IsFlag(found, "profiling")) {
474 EnableChannel(JitSpew_Profiling);
475 } else if (IsFlag(found, "dump-mir-expr")) {
476 EnableChannel(JitSpew_MIRExpressions);
477 } else if (IsFlag(found, "warp-snapshots")) {
478 EnableChannel(JitSpew_WarpSnapshots);
479 } else if (IsFlag(found, "warp-transpiler")) {
480 EnableChannel(JitSpew_WarpTranspiler);
481 } else if (IsFlag(found, "warp-trial-inlining")) {
482 EnableChannel(JitSpew_WarpTrialInlining);
483 } else if (IsFlag(found, "all")) {
484 LoggingBits = uint64_t(-1);
485 } else if (IsFlag(found, "bl-aborts")) {
486 EnableChannel(JitSpew_BaselineAbort);
487 } else if (IsFlag(found, "bl-scripts")) {
488 EnableChannel(JitSpew_BaselineScripts);
489 } else if (IsFlag(found, "bl-op")) {
490 EnableChannel(JitSpew_BaselineOp);
491 } else if (IsFlag(found, "bl-ic")) {
492 EnableChannel(JitSpew_BaselineIC);
493 } else if (IsFlag(found, "bl-ic-fb")) {
494 EnableChannel(JitSpew_BaselineICFallback);
495 } else if (IsFlag(found, "bl-osr")) {
496 EnableChannel(JitSpew_BaselineOSR);
497 } else if (IsFlag(found, "bl-bails")) {
498 EnableChannel(JitSpew_BaselineBailouts);
499 } else if (IsFlag(found, "bl-dbg-osr")) {
500 EnableChannel(JitSpew_BaselineDebugModeOSR);
501 } else if (IsFlag(found, "bl-all")) {
502 EnableChannel(JitSpew_BaselineAbort);
503 EnableChannel(JitSpew_BaselineScripts);
504 EnableChannel(JitSpew_BaselineOp);
505 EnableChannel(JitSpew_BaselineIC);
506 EnableChannel(JitSpew_BaselineICFallback);
507 EnableChannel(JitSpew_BaselineOSR);
508 EnableChannel(JitSpew_BaselineBailouts);
509 EnableChannel(JitSpew_BaselineDebugModeOSR);
510 } else {
511 fprintf(stderr, "Unknown flag.\n");
512 PrintHelpAndExit(64);
514 found = strtok(nullptr, ",");
517 FILE* spewfh = stderr;
518 const char* filename = getenv("ION_SPEW_FILENAME");
519 if (filename && *filename) {
520 char actual_filename[2048] = {0};
521 SprintfLiteral(actual_filename, "%s.%d", filename, getpid());
522 spewfh = fopen(actual_filename, "w");
523 MOZ_RELEASE_ASSERT(spewfh);
524 setbuf(spewfh, nullptr); // Make unbuffered
526 JitSpewPrinter().init(spewfh);
529 JitSpewIndent::JitSpewIndent(JitSpewChannel channel) : channel_(channel) {
530 ChannelIndentLevel[channel]++;
533 JitSpewIndent::~JitSpewIndent() { ChannelIndentLevel[channel_]--; }
535 void jit::JitSpewStartVA(JitSpewChannel channel, const char* fmt, va_list ap) {
536 if (!JitSpewEnabled(channel)) {
537 return;
540 JitSpewHeader(channel);
541 Fprinter& out = JitSpewPrinter();
542 out.vprintf(fmt, ap);
545 void jit::JitSpewContVA(JitSpewChannel channel, const char* fmt, va_list ap) {
546 if (!JitSpewEnabled(channel)) {
547 return;
550 Fprinter& out = JitSpewPrinter();
551 out.vprintf(fmt, ap);
554 void jit::JitSpewFin(JitSpewChannel channel) {
555 if (!JitSpewEnabled(channel)) {
556 return;
559 Fprinter& out = JitSpewPrinter();
560 out.put("\n");
563 void jit::JitSpewVA(JitSpewChannel channel, const char* fmt, va_list ap) {
564 JitSpewStartVA(channel, fmt, ap);
565 JitSpewFin(channel);
568 void jit::JitSpew(JitSpewChannel channel, const char* fmt, ...) {
569 va_list ap;
570 va_start(ap, fmt);
571 JitSpewVA(channel, fmt, ap);
572 va_end(ap);
575 void jit::JitSpewDef(JitSpewChannel channel, const char* str,
576 MDefinition* def) {
577 if (!JitSpewEnabled(channel)) {
578 return;
581 JitSpewHeader(channel);
582 Fprinter& out = JitSpewPrinter();
583 out.put(str);
584 def->dump(out);
585 def->dumpLocation(out);
588 void jit::JitSpewStart(JitSpewChannel channel, const char* fmt, ...) {
589 va_list ap;
590 va_start(ap, fmt);
591 JitSpewStartVA(channel, fmt, ap);
592 va_end(ap);
594 void jit::JitSpewCont(JitSpewChannel channel, const char* fmt, ...) {
595 va_list ap;
596 va_start(ap, fmt);
597 JitSpewContVA(channel, fmt, ap);
598 va_end(ap);
601 void jit::JitSpewHeader(JitSpewChannel channel) {
602 if (!JitSpewEnabled(channel)) {
603 return;
606 Fprinter& out = JitSpewPrinter();
607 out.printf("[%s] ", ChannelNames[channel]);
608 for (size_t i = ChannelIndentLevel[channel]; i != 0; i--) {
609 out.put(" ");
613 bool jit::JitSpewEnabled(JitSpewChannel channel) {
614 MOZ_ASSERT(LoggingChecked);
615 return (LoggingBits & (uint64_t(1) << uint32_t(channel))) &&
616 !filteredOutCompilations;
619 void jit::EnableChannel(JitSpewChannel channel) {
620 MOZ_ASSERT(LoggingChecked);
621 LoggingBits |= uint64_t(1) << uint32_t(channel);
624 void jit::DisableChannel(JitSpewChannel channel) {
625 MOZ_ASSERT(LoggingChecked);
626 LoggingBits &= ~(uint64_t(1) << uint32_t(channel));
629 const char* js::jit::ValTypeToString(JSValueType type) {
630 switch (type) {
631 case JSVAL_TYPE_DOUBLE:
632 return "Double";
633 case JSVAL_TYPE_INT32:
634 return "Int32";
635 case JSVAL_TYPE_BOOLEAN:
636 return "Boolean";
637 case JSVAL_TYPE_UNDEFINED:
638 return "Undefined";
639 case JSVAL_TYPE_NULL:
640 return "Null";
641 case JSVAL_TYPE_MAGIC:
642 return "Magic";
643 case JSVAL_TYPE_STRING:
644 return "String";
645 case JSVAL_TYPE_SYMBOL:
646 return "Symbol";
647 case JSVAL_TYPE_PRIVATE_GCTHING:
648 return "PrivateGCThing";
649 case JSVAL_TYPE_BIGINT:
650 return "BigInt";
651 case JSVAL_TYPE_OBJECT:
652 return "Object";
653 case JSVAL_TYPE_UNKNOWN:
654 return "None";
655 default:
656 MOZ_CRASH("Unknown JSValueType");
660 #endif /* JS_JITSPEW */