summaryrefslogtreecommitdiffstats
path: root/js/src/jit/JitSpewer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/JitSpewer.cpp')
-rw-r--r--js/src/jit/JitSpewer.cpp679
1 files changed, 679 insertions, 0 deletions
diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
new file mode 100644
index 000000000..b939f5ea2
--- /dev/null
+++ b/js/src/jit/JitSpewer.cpp
@@ -0,0 +1,679 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef JS_JITSPEW
+
+#include "jit/JitSpewer.h"
+
+#include "mozilla/Atomics.h"
+
+#if defined(XP_WIN)
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "jsprf.h"
+
+#include "jit/Ion.h"
+#include "jit/MIR.h"
+#include "jit/MIRGenerator.h"
+#include "jit/MIRGraph.h"
+
+#include "threading/LockGuard.h"
+
+#include "vm/HelperThreads.h"
+#include "vm/MutexIDs.h"
+
+#ifndef JIT_SPEW_DIR
+# if defined(_WIN32)
+# define JIT_SPEW_DIR ""
+# elif defined(__ANDROID__)
+# define JIT_SPEW_DIR "/data/local/tmp/"
+# else
+# define JIT_SPEW_DIR "/tmp/"
+# endif
+#endif
+
+using namespace js;
+using namespace js::jit;
+
+class IonSpewer
+{
+ private:
+ Mutex outputLock_;
+ Fprinter c1Output_;
+ Fprinter jsonOutput_;
+ bool firstFunction_;
+ bool asyncLogging_;
+ bool inited_;
+
+ void release();
+
+ public:
+ IonSpewer()
+ : outputLock_(mutexid::IonSpewer),
+ firstFunction_(false),
+ asyncLogging_(false),
+ inited_(false)
+ { }
+
+ // File output is terminated safely upon destruction.
+ ~IonSpewer();
+
+ bool init();
+ bool isEnabled() {
+ return inited_;
+ }
+ void setAsyncLogging(bool incremental) {
+ asyncLogging_ = incremental;
+ }
+ bool getAsyncLogging() {
+ return asyncLogging_;
+ }
+
+ void beginFunction();
+ void spewPass(GraphSpewer* gs);
+ void endFunction(GraphSpewer* gs);
+};
+
+// IonSpewer singleton.
+static IonSpewer ionspewer;
+
+static bool LoggingChecked = false;
+static_assert(JitSpew_Terminator <= 64, "Increase the size of the LoggingBits global.");
+static uint64_t LoggingBits = 0;
+static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
+
+static const char * const ChannelNames[] =
+{
+#define JITSPEW_CHANNEL(name) #name,
+ JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
+#undef JITSPEW_CHANNEL
+};
+
+static size_t ChannelIndentLevel[] =
+{
+#define JITSPEW_CHANNEL(name) 0,
+ JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
+#undef JITSPEW_CHANNEL
+};
+
+static bool
+FilterContainsLocation(JSScript* function)
+{
+ static const char* filter = getenv("IONFILTER");
+
+ // If there is no filter we accept all outputs.
+ if (!filter || !filter[0])
+ return true;
+
+ // Disable wasm output when filter is set.
+ if (!function)
+ return false;
+
+ const char* filename = function->filename();
+ const size_t line = function->lineno();
+ const size_t filelen = strlen(filename);
+ const char* index = strstr(filter, filename);
+ while (index) {
+ if (index == filter || index[-1] == ',') {
+ if (index[filelen] == 0 || index[filelen] == ',')
+ return true;
+ if (index[filelen] == ':' && line != size_t(-1)) {
+ size_t read_line = strtoul(&index[filelen + 1], nullptr, 10);
+ if (read_line == line)
+ return true;
+ }
+ }
+ index = strstr(index + filelen, filename);
+ }
+ return false;
+}
+
+void
+jit::EnableIonDebugSyncLogging()
+{
+ ionspewer.init();
+ ionspewer.setAsyncLogging(false);
+ EnableChannel(JitSpew_IonSyncLogs);
+}
+
+void
+jit::EnableIonDebugAsyncLogging()
+{
+ ionspewer.init();
+ ionspewer.setAsyncLogging(true);
+}
+
+void
+IonSpewer::release()
+{
+ if (c1Output_.isInitialized())
+ c1Output_.finish();
+ if (jsonOutput_.isInitialized())
+ jsonOutput_.finish();
+ inited_ = false;
+}
+
+bool
+IonSpewer::init()
+{
+ if (inited_)
+ return true;
+
+ const size_t bufferLength = 256;
+ char c1Buffer[bufferLength];
+ char jsonBuffer[bufferLength];
+ const char *c1Filename = JIT_SPEW_DIR "/ion.cfg";
+ const char *jsonFilename = JIT_SPEW_DIR "/ion.json";
+
+ const char* usePid = getenv("ION_SPEW_BY_PID");
+ if (usePid && *usePid != 0) {
+#if defined(XP_WIN)
+ size_t pid = GetCurrentProcessId();
+#else
+ size_t pid = getpid();
+#endif
+
+ size_t len;
+ len = snprintf(jsonBuffer, bufferLength, JIT_SPEW_DIR "/ion%" PRIuSIZE ".json", pid);
+ if (bufferLength <= len) {
+ fprintf(stderr, "Warning: IonSpewer::init: Cannot serialize file name.");
+ return false;
+ }
+ jsonFilename = jsonBuffer;
+
+ len = snprintf(c1Buffer, bufferLength, JIT_SPEW_DIR "/ion%" PRIuSIZE ".cfg", pid);
+ if (bufferLength <= len) {
+ fprintf(stderr, "Warning: IonSpewer::init: Cannot serialize file name.");
+ return false;
+ }
+ c1Filename = c1Buffer;
+ }
+
+ if (!c1Output_.init(c1Filename) ||
+ !jsonOutput_.init(jsonFilename))
+ {
+ release();
+ return false;
+ }
+
+ jsonOutput_.printf("{\n \"functions\": [\n");
+ firstFunction_ = true;
+
+ inited_ = true;
+ return true;
+}
+
+void
+IonSpewer::beginFunction()
+{
+ // If we are doing a synchronous logging then we spew everything as we go,
+ // as this is useful in case of failure during the compilation. On the other
+ // hand, it is recommended to disabled off main thread compilation.
+ if (!getAsyncLogging() && !firstFunction_) {
+ LockGuard<Mutex> guard(outputLock_);
+ jsonOutput_.put(","); // separate functions
+ }
+}
+
+void
+IonSpewer::spewPass(GraphSpewer* gs)
+{
+ if (!getAsyncLogging()) {
+ LockGuard<Mutex> guard(outputLock_);
+ gs->dump(c1Output_, jsonOutput_);
+ }
+}
+
+void
+IonSpewer::endFunction(GraphSpewer* gs)
+{
+ LockGuard<Mutex> guard(outputLock_);
+ if (getAsyncLogging() && !firstFunction_)
+ jsonOutput_.put(","); // separate functions
+
+ gs->dump(c1Output_, jsonOutput_);
+ firstFunction_ = false;
+}
+
+IonSpewer::~IonSpewer()
+{
+ if (!inited_)
+ return;
+
+ jsonOutput_.printf("\n]}\n");
+ release();
+}
+
+GraphSpewer::GraphSpewer(TempAllocator *alloc)
+ : graph_(nullptr),
+ c1Printer_(alloc->lifoAlloc()),
+ jsonPrinter_(alloc->lifoAlloc()),
+ c1Spewer_(c1Printer_),
+ jsonSpewer_(jsonPrinter_)
+{
+}
+
+void
+GraphSpewer::init(MIRGraph* graph, JSScript* function)
+{
+ MOZ_ASSERT(!isSpewing());
+ if (!ionspewer.isEnabled())
+ return;
+
+ if (!FilterContainsLocation(function)) {
+ // filter out logs during the compilation.
+ filteredOutCompilations++;
+ MOZ_ASSERT(!isSpewing());
+ return;
+ }
+
+ graph_ = graph;
+ MOZ_ASSERT(isSpewing());
+}
+
+void
+GraphSpewer::beginFunction(JSScript* function)
+{
+ if (!isSpewing())
+ return;
+
+ c1Spewer_.beginFunction(graph_, function);
+ jsonSpewer_.beginFunction(function);
+
+ ionspewer.beginFunction();
+}
+
+void
+GraphSpewer::spewPass(const char* pass)
+{
+ if (!isSpewing())
+ return;
+
+ c1Spewer_.spewPass(pass);
+
+ jsonSpewer_.beginPass(pass);
+ jsonSpewer_.spewMIR(graph_);
+ jsonSpewer_.spewLIR(graph_);
+ jsonSpewer_.endPass();
+
+ ionspewer.spewPass(this);
+
+ // As this function is used for debugging, we ignore any of the previous
+ // failures and ensure there is enough ballast space, such that we do not
+ // exhaust the ballast space before running the next phase.
+ AutoEnterOOMUnsafeRegion oomUnsafe;
+ if (!graph_->alloc().ensureBallast())
+ oomUnsafe.crash("Could not ensure enough ballast space after spewing graph information.");
+}
+
+void
+GraphSpewer::spewPass(const char* pass, BacktrackingAllocator* ra)
+{
+ if (!isSpewing())
+ return;
+
+ c1Spewer_.spewPass(pass);
+ c1Spewer_.spewRanges(pass, ra);
+
+ jsonSpewer_.beginPass(pass);
+ jsonSpewer_.spewMIR(graph_);
+ jsonSpewer_.spewLIR(graph_);
+ jsonSpewer_.spewRanges(ra);
+ jsonSpewer_.endPass();
+
+ ionspewer.spewPass(this);
+}
+
+void
+GraphSpewer::endFunction()
+{
+ if (!ionspewer.isEnabled())
+ return;
+
+ if (!isSpewing()) {
+ MOZ_ASSERT(filteredOutCompilations != 0);
+ filteredOutCompilations--;
+ return;
+ }
+
+ c1Spewer_.endFunction();
+ jsonSpewer_.endFunction();
+
+ ionspewer.endFunction(this);
+ graph_ = nullptr;
+}
+
+void
+GraphSpewer::dump(Fprinter& c1Out, Fprinter& jsonOut)
+{
+ if (!c1Printer_.hadOutOfMemory()) {
+ c1Printer_.exportInto(c1Out);
+ c1Out.flush();
+ }
+ c1Printer_.clear();
+
+ if (!jsonPrinter_.hadOutOfMemory())
+ jsonPrinter_.exportInto(jsonOut);
+ else
+ jsonOut.put("{}");
+ jsonOut.flush();
+ jsonPrinter_.clear();
+}
+
+void
+jit::SpewBeginFunction(MIRGenerator* mir, JSScript* function)
+{
+ MIRGraph* graph = &mir->graph();
+ mir->graphSpewer().init(graph, function);
+ mir->graphSpewer().beginFunction(function);
+}
+
+AutoSpewEndFunction::~AutoSpewEndFunction()
+{
+ mir_->graphSpewer().endFunction();
+}
+
+Fprinter&
+jit::JitSpewPrinter()
+{
+ static Fprinter out;
+ return out;
+}
+
+
+static bool
+ContainsFlag(const char* str, const char* flag)
+{
+ size_t flaglen = strlen(flag);
+ const char* index = strstr(str, flag);
+ while (index) {
+ if ((index == str || index[-1] == ',') && (index[flaglen] == 0 || index[flaglen] == ','))
+ return true;
+ index = strstr(index + flaglen, flag);
+ }
+ return false;
+}
+
+void
+jit::CheckLogging()
+{
+ if (LoggingChecked)
+ return;
+ LoggingChecked = true;
+ const char* env = getenv("IONFLAGS");
+ if (!env)
+ return;
+ if (strstr(env, "help")) {
+ fflush(nullptr);
+ printf(
+ "\n"
+ "usage: IONFLAGS=option,option,option,... where options can be:\n"
+ "\n"
+ " aborts Compilation abort messages\n"
+ " scripts Compiled scripts\n"
+ " mir MIR information\n"
+ " prune Prune unused branches\n"
+ " escape Escape analysis\n"
+ " alias Alias analysis\n"
+ " alias-sum Alias analysis: shows summaries for every block\n"
+ " gvn Global Value Numbering\n"
+ " licm Loop invariant code motion\n"
+ " flac Fold linear arithmetic constants\n"
+ " eaa Effective address analysis\n"
+ " sincos Replace sin/cos by sincos\n"
+ " sink Sink transformation\n"
+ " regalloc Register allocation\n"
+ " inline Inlining\n"
+ " snapshots Snapshot information\n"
+ " codegen Native code generation\n"
+ " bailouts Bailouts\n"
+ " caches Inline caches\n"
+ " osi Invalidation\n"
+ " safepoints Safepoints\n"
+ " pools Literal Pools (ARM only for now)\n"
+ " cacheflush Instruction Cache flushes (ARM only for now)\n"
+ " range Range Analysis\n"
+ " unroll Loop unrolling\n"
+ " logs C1 and JSON visualization logging\n"
+ " logs-sync Same as logs, but flushes between each pass (sync. compiled functions only).\n"
+ " profiling Profiling-related information\n"
+ " trackopts Optimization tracking information\n"
+ " dump-mir-expr Dump the MIR expressions\n"
+ " all Everything\n"
+ "\n"
+ " bl-aborts Baseline compiler abort messages\n"
+ " bl-scripts Baseline script-compilation\n"
+ " bl-op Baseline compiler detailed op-specific messages\n"
+ " bl-ic Baseline inline-cache messages\n"
+ " bl-ic-fb Baseline IC fallback stub messages\n"
+ " bl-osr Baseline IC OSR messages\n"
+ " bl-bails Baseline bailouts\n"
+ " bl-dbg-osr Baseline debug mode on stack recompile messages\n"
+ " bl-all All baseline spew\n"
+ "\n"
+ );
+ exit(0);
+ /*NOTREACHED*/
+ }
+ if (ContainsFlag(env, "aborts"))
+ EnableChannel(JitSpew_IonAbort);
+ if (ContainsFlag(env, "prune"))
+ EnableChannel(JitSpew_Prune);
+ if (ContainsFlag(env, "escape"))
+ EnableChannel(JitSpew_Escape);
+ if (ContainsFlag(env, "alias"))
+ EnableChannel(JitSpew_Alias);
+ if (ContainsFlag(env, "alias-sum"))
+ EnableChannel(JitSpew_AliasSummaries);
+ if (ContainsFlag(env, "scripts"))
+ EnableChannel(JitSpew_IonScripts);
+ if (ContainsFlag(env, "mir"))
+ EnableChannel(JitSpew_IonMIR);
+ if (ContainsFlag(env, "gvn"))
+ EnableChannel(JitSpew_GVN);
+ if (ContainsFlag(env, "range"))
+ EnableChannel(JitSpew_Range);
+ if (ContainsFlag(env, "unroll"))
+ EnableChannel(JitSpew_Unrolling);
+ if (ContainsFlag(env, "licm"))
+ EnableChannel(JitSpew_LICM);
+ if (ContainsFlag(env, "flac"))
+ EnableChannel(JitSpew_FLAC);
+ if (ContainsFlag(env, "eaa"))
+ EnableChannel(JitSpew_EAA);
+ if (ContainsFlag(env, "sincos"))
+ EnableChannel(JitSpew_Sincos);
+ if (ContainsFlag(env, "sink"))
+ EnableChannel(JitSpew_Sink);
+ if (ContainsFlag(env, "regalloc"))
+ EnableChannel(JitSpew_RegAlloc);
+ if (ContainsFlag(env, "inline"))
+ EnableChannel(JitSpew_Inlining);
+ if (ContainsFlag(env, "snapshots"))
+ EnableChannel(JitSpew_IonSnapshots);
+ if (ContainsFlag(env, "codegen"))
+ EnableChannel(JitSpew_Codegen);
+ if (ContainsFlag(env, "bailouts"))
+ EnableChannel(JitSpew_IonBailouts);
+ if (ContainsFlag(env, "osi"))
+ EnableChannel(JitSpew_IonInvalidate);
+ if (ContainsFlag(env, "caches"))
+ EnableChannel(JitSpew_IonIC);
+ if (ContainsFlag(env, "safepoints"))
+ EnableChannel(JitSpew_Safepoints);
+ if (ContainsFlag(env, "pools"))
+ EnableChannel(JitSpew_Pools);
+ if (ContainsFlag(env, "cacheflush"))
+ EnableChannel(JitSpew_CacheFlush);
+ if (ContainsFlag(env, "logs"))
+ EnableIonDebugAsyncLogging();
+ if (ContainsFlag(env, "logs-sync"))
+ EnableIonDebugSyncLogging();
+ if (ContainsFlag(env, "profiling"))
+ EnableChannel(JitSpew_Profiling);
+ if (ContainsFlag(env, "trackopts"))
+ EnableChannel(JitSpew_OptimizationTracking);
+ if (ContainsFlag(env, "dump-mir-expr"))
+ EnableChannel(JitSpew_MIRExpressions);
+ if (ContainsFlag(env, "all"))
+ LoggingBits = uint64_t(-1);
+
+ if (ContainsFlag(env, "bl-aborts"))
+ EnableChannel(JitSpew_BaselineAbort);
+ if (ContainsFlag(env, "bl-scripts"))
+ EnableChannel(JitSpew_BaselineScripts);
+ if (ContainsFlag(env, "bl-op"))
+ EnableChannel(JitSpew_BaselineOp);
+ if (ContainsFlag(env, "bl-ic"))
+ EnableChannel(JitSpew_BaselineIC);
+ if (ContainsFlag(env, "bl-ic-fb"))
+ EnableChannel(JitSpew_BaselineICFallback);
+ if (ContainsFlag(env, "bl-osr"))
+ EnableChannel(JitSpew_BaselineOSR);
+ if (ContainsFlag(env, "bl-bails"))
+ EnableChannel(JitSpew_BaselineBailouts);
+ if (ContainsFlag(env, "bl-dbg-osr"))
+ EnableChannel(JitSpew_BaselineDebugModeOSR);
+ if (ContainsFlag(env, "bl-all")) {
+ EnableChannel(JitSpew_BaselineAbort);
+ EnableChannel(JitSpew_BaselineScripts);
+ EnableChannel(JitSpew_BaselineOp);
+ EnableChannel(JitSpew_BaselineIC);
+ EnableChannel(JitSpew_BaselineICFallback);
+ EnableChannel(JitSpew_BaselineOSR);
+ EnableChannel(JitSpew_BaselineBailouts);
+ EnableChannel(JitSpew_BaselineDebugModeOSR);
+ }
+
+ JitSpewPrinter().init(stderr);
+}
+
+JitSpewIndent::JitSpewIndent(JitSpewChannel channel)
+ : channel_(channel)
+{
+ ChannelIndentLevel[channel]++;
+}
+
+JitSpewIndent::~JitSpewIndent()
+{
+ ChannelIndentLevel[channel_]--;
+}
+
+void
+jit::JitSpewStartVA(JitSpewChannel channel, const char* fmt, va_list ap)
+{
+ if (!JitSpewEnabled(channel))
+ return;
+
+ JitSpewHeader(channel);
+ Fprinter& out = JitSpewPrinter();
+ out.vprintf(fmt, ap);
+}
+
+void
+jit::JitSpewContVA(JitSpewChannel channel, const char* fmt, va_list ap)
+{
+ if (!JitSpewEnabled(channel))
+ return;
+
+ Fprinter& out = JitSpewPrinter();
+ out.vprintf(fmt, ap);
+}
+
+void
+jit::JitSpewFin(JitSpewChannel channel)
+{
+ if (!JitSpewEnabled(channel))
+ return;
+
+ Fprinter& out = JitSpewPrinter();
+ out.put("\n");
+}
+
+void
+jit::JitSpewVA(JitSpewChannel channel, const char* fmt, va_list ap)
+{
+ JitSpewStartVA(channel, fmt, ap);
+ JitSpewFin(channel);
+}
+
+void
+jit::JitSpew(JitSpewChannel channel, const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ JitSpewVA(channel, fmt, ap);
+ va_end(ap);
+}
+
+void
+jit::JitSpewDef(JitSpewChannel channel, const char* str, MDefinition* def)
+{
+ if (!JitSpewEnabled(channel))
+ return;
+
+ JitSpewHeader(channel);
+ Fprinter& out = JitSpewPrinter();
+ out.put(str);
+ def->dump(out);
+ def->dumpLocation(out);
+}
+
+void
+jit::JitSpewStart(JitSpewChannel channel, const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ JitSpewStartVA(channel, fmt, ap);
+ va_end(ap);
+}
+void
+jit::JitSpewCont(JitSpewChannel channel, const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ JitSpewContVA(channel, fmt, ap);
+ va_end(ap);
+}
+
+void
+jit::JitSpewHeader(JitSpewChannel channel)
+{
+ if (!JitSpewEnabled(channel))
+ return;
+
+ Fprinter& out = JitSpewPrinter();
+ out.printf("[%s] ", ChannelNames[channel]);
+ for (size_t i = ChannelIndentLevel[channel]; i != 0; i--)
+ out.put(" ");
+}
+
+bool
+jit::JitSpewEnabled(JitSpewChannel channel)
+{
+ MOZ_ASSERT(LoggingChecked);
+ return (LoggingBits & (uint64_t(1) << uint32_t(channel))) && !filteredOutCompilations;
+}
+
+void
+jit::EnableChannel(JitSpewChannel channel)
+{
+ MOZ_ASSERT(LoggingChecked);
+ LoggingBits |= uint64_t(1) << uint32_t(channel);
+}
+
+void
+jit::DisableChannel(JitSpewChannel channel)
+{
+ MOZ_ASSERT(LoggingChecked);
+ LoggingBits &= ~(uint64_t(1) << uint32_t(channel));
+}
+
+#endif /* JS_JITSPEW */
+