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 " 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"
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"
388 "See also SPEW=help for information on the Structured Spewer."
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
) {
402 LoggingChecked
= true;
404 char* env
= getenv("IONFLAGS");
409 const char* found
= strtok(env
, ",");
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")) {
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
);
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
)) {
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
)) {
553 Fprinter
& out
= JitSpewPrinter();
554 out
.vprintf(fmt
, ap
);
557 void jit::JitSpewFin(JitSpewChannel channel
) {
558 if (!JitSpewEnabled(channel
)) {
562 Fprinter
& out
= JitSpewPrinter();
566 void jit::JitSpewVA(JitSpewChannel channel
, const char* fmt
, va_list ap
) {
567 JitSpewStartVA(channel
, fmt
, ap
);
571 void jit::JitSpew(JitSpewChannel channel
, const char* fmt
, ...) {
574 JitSpewVA(channel
, fmt
, ap
);
578 void jit::JitSpewDef(JitSpewChannel channel
, const char* str
,
580 if (!JitSpewEnabled(channel
)) {
584 JitSpewHeader(channel
);
585 Fprinter
& out
= JitSpewPrinter();
588 def
->dumpLocation(out
);
591 void jit::JitSpewStart(JitSpewChannel channel
, const char* fmt
, ...) {
594 JitSpewStartVA(channel
, fmt
, ap
);
597 void jit::JitSpewCont(JitSpewChannel channel
, const char* fmt
, ...) {
600 JitSpewContVA(channel
, fmt
, ap
);
604 void jit::JitSpewHeader(JitSpewChannel channel
) {
605 if (!JitSpewEnabled(channel
)) {
609 Fprinter
& out
= JitSpewPrinter();
610 out
.printf("[%s] ", ChannelNames
[channel
]);
611 for (size_t i
= ChannelIndentLevel
[channel
]; i
!= 0; i
--) {
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
) {
634 case JSVAL_TYPE_DOUBLE
:
636 case JSVAL_TYPE_INT32
:
638 case JSVAL_TYPE_BOOLEAN
:
640 case JSVAL_TYPE_UNDEFINED
:
642 case JSVAL_TYPE_NULL
:
644 case JSVAL_TYPE_MAGIC
:
646 case JSVAL_TYPE_STRING
:
648 case JSVAL_TYPE_SYMBOL
:
650 case JSVAL_TYPE_PRIVATE_GCTHING
:
651 return "PrivateGCThing";
652 case JSVAL_TYPE_BIGINT
:
654 case JSVAL_TYPE_OBJECT
:
656 case JSVAL_TYPE_UNKNOWN
:
659 MOZ_CRASH("Unknown JSValueType");
663 #endif /* JS_JITSPEW */