diff options
Diffstat (limited to 'js/src/jit/JitSpewer.cpp')
-rw-r--r-- | js/src/jit/JitSpewer.cpp | 679 |
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 */ + |