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/. */
9 # include "jit/JitSpewer.h"
11 # include "mozilla/Atomics.h"
12 # include "mozilla/Sprintf.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"
23 # define JIT_SPEW_DIR "."
24 # elif defined(__ANDROID__)
25 # define JIT_SPEW_DIR "/data/local/tmp"
27 # define JIT_SPEW_DIR "/tmp"
32 using namespace js::jit
;
36 Mutex outputLock_ MOZ_UNANNOTATED
;
46 : outputLock_(mutexid::IonSpewer
),
47 firstFunction_(false),
51 // File output is terminated safely upon destruction.
55 bool isEnabled() { return inited_
; }
56 void setAsyncLogging(bool incremental
) { asyncLogging_
= incremental
; }
57 bool getAsyncLogging() { return asyncLogging_
; }
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]) {
95 // Disable wasm output when filter is set.
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
);
105 if (index
== gSpewFilter
|| index
[-1] == ',') {
106 if (index
[filelen
] == 0 || index
[filelen
] == ',') {
109 if (index
[filelen
] == ':' && line
!= size_t(-1)) {
110 size_t read_line
= strtoul(&index
[filelen
+ 1], nullptr, 10);
111 if (read_line
== line
) {
116 index
= strstr(index
+ filelen
, filename
);
121 void jit::EnableIonDebugSyncLogging() {
123 ionspewer
.setAsyncLogging(false);
124 EnableChannel(JitSpew_IonSyncLogs
);
127 void jit::EnableIonDebugAsyncLogging() {
129 ionspewer
.setAsyncLogging(true);
132 void IonSpewer::release() {
133 if (jsonOutput_
.isInitialized()) {
134 jsonOutput_
.finish();
139 bool IonSpewer::init() {
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();
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.");
160 jsonFilename
= jsonBuffer
;
163 if (!jsonOutput_
.init(jsonFilename
)) {
168 jsonOutput_
.printf("{\n \"functions\": [\n");
169 firstFunction_
= 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() {
207 jsonOutput_
.printf("\n]}\n");
211 GraphSpewer::GraphSpewer(TempAllocator
* alloc
)
213 jsonPrinter_(alloc
->lifoAlloc()),
214 jsonSpewer_(jsonPrinter_
) {}
216 void GraphSpewer::init(MIRGraph
* graph
, JSScript
* function
) {
217 MOZ_ASSERT(!isSpewing());
218 if (!ionspewer
.isEnabled()) {
222 if (!FilterContainsLocation(function
)) {
223 // filter out logs during the compilation.
224 filteredOutCompilations
++;
225 MOZ_ASSERT(!isSpewing());
230 MOZ_ASSERT(isSpewing());
233 void GraphSpewer::beginFunction(JSScript
* function
) {
237 jsonSpewer_
.beginFunction(function
);
238 ionspewer
.beginFunction();
241 void GraphSpewer::beginWasmFunction(unsigned funcIndex
) {
245 jsonSpewer_
.beginWasmFunction(funcIndex
);
246 ionspewer
.beginFunction();
249 void GraphSpewer::spewPass(const char* pass
) {
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()) {
267 "Could not ensure enough ballast space after spewing graph "
272 void GraphSpewer::spewPass(const char* pass
, BacktrackingAllocator
* ra
) {
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()) {
292 MOZ_ASSERT(filteredOutCompilations
!= 0);
293 filteredOutCompilations
--;
297 jsonSpewer_
.endFunction();
299 ionspewer
.endFunction(this);
303 void GraphSpewer::dump(Fprinter
& jsonOut
) {
304 if (!jsonPrinter_
.hadOutOfMemory()) {
305 jsonPrinter_
.exportInto(jsonOut
);
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() {
334 static void PrintHelpAndExit(int status
= 0) {
338 "usage: IONFLAGS=option,option,option,... where options can be:\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"
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"
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"
387 "See also SPEW=help for information on the Structured Spewer."
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
) {
401 LoggingChecked
= true;
403 char* env
= getenv("IONFLAGS");
408 const char* found
= strtok(env
, ",");
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")) {
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
);
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
)) {
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
)) {
550 Fprinter
& out
= JitSpewPrinter();
551 out
.vprintf(fmt
, ap
);
554 void jit::JitSpewFin(JitSpewChannel channel
) {
555 if (!JitSpewEnabled(channel
)) {
559 Fprinter
& out
= JitSpewPrinter();
563 void jit::JitSpewVA(JitSpewChannel channel
, const char* fmt
, va_list ap
) {
564 JitSpewStartVA(channel
, fmt
, ap
);
568 void jit::JitSpew(JitSpewChannel channel
, const char* fmt
, ...) {
571 JitSpewVA(channel
, fmt
, ap
);
575 void jit::JitSpewDef(JitSpewChannel channel
, const char* str
,
577 if (!JitSpewEnabled(channel
)) {
581 JitSpewHeader(channel
);
582 Fprinter
& out
= JitSpewPrinter();
585 def
->dumpLocation(out
);
588 void jit::JitSpewStart(JitSpewChannel channel
, const char* fmt
, ...) {
591 JitSpewStartVA(channel
, fmt
, ap
);
594 void jit::JitSpewCont(JitSpewChannel channel
, const char* fmt
, ...) {
597 JitSpewContVA(channel
, fmt
, ap
);
601 void jit::JitSpewHeader(JitSpewChannel channel
) {
602 if (!JitSpewEnabled(channel
)) {
606 Fprinter
& out
= JitSpewPrinter();
607 out
.printf("[%s] ", ChannelNames
[channel
]);
608 for (size_t i
= ChannelIndentLevel
[channel
]; i
!= 0; i
--) {
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
) {
631 case JSVAL_TYPE_DOUBLE
:
633 case JSVAL_TYPE_INT32
:
635 case JSVAL_TYPE_BOOLEAN
:
637 case JSVAL_TYPE_UNDEFINED
:
639 case JSVAL_TYPE_NULL
:
641 case JSVAL_TYPE_MAGIC
:
643 case JSVAL_TYPE_STRING
:
645 case JSVAL_TYPE_SYMBOL
:
647 case JSVAL_TYPE_PRIVATE_GCTHING
:
648 return "PrivateGCThing";
649 case JSVAL_TYPE_BIGINT
:
651 case JSVAL_TYPE_OBJECT
:
653 case JSVAL_TYPE_UNKNOWN
:
656 MOZ_CRASH("Unknown JSValueType");
660 #endif /* JS_JITSPEW */