diff options
Diffstat (limited to 'js/src/jit/PerfSpewer.cpp')
-rw-r--r-- | js/src/jit/PerfSpewer.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/js/src/jit/PerfSpewer.cpp b/js/src/jit/PerfSpewer.cpp new file mode 100644 index 000000000..cb80d04aa --- /dev/null +++ b/js/src/jit/PerfSpewer.cpp @@ -0,0 +1,340 @@ +/* -*- 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/. */ + +#include "jit/PerfSpewer.h" + +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/SizePrintfMacros.h" + +#ifdef XP_UNIX +# include <unistd.h> +#endif + +#ifdef JS_ION_PERF +# include "jit/JitSpewer.h" +# include "jit/LIR.h" +# include "jit/MIR.h" +# include "jit/MIRGraph.h" +#endif + +#include "vm/MutexIDs.h" + +// perf expects its data to be in a file /tmp/perf-PID.map, but for Android +// and B2G the map files are written to /data/local/tmp/perf-PID.map +// +// Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/ +// so also try /sdcard/. + +#ifndef PERF_SPEW_DIR +# if defined(__ANDROID__) +# define PERF_SPEW_DIR "/data/local/tmp/" +# define PERF_SPEW_DIR_2 "/sdcard/" +# else +# define PERF_SPEW_DIR "/tmp/" +# endif +#endif + +using namespace js; +using namespace js::jit; + +#define PERF_MODE_NONE 1 +#define PERF_MODE_FUNC 2 +#define PERF_MODE_BLOCK 3 + +#ifdef JS_ION_PERF + +static uint32_t PerfMode = 0; + +static bool PerfChecked = false; + +static FILE* PerfFilePtr = nullptr; + +static js::Mutex* PerfMutex; + +static bool +openPerfMap(const char* dir) +{ + const ssize_t bufferSize = 256; + char filenameBuffer[bufferSize]; + + if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize) + return false; + + MOZ_ASSERT(!PerfFilePtr); + PerfFilePtr = fopen(filenameBuffer, "a"); + + if (!PerfFilePtr) + return false; + + return true; +} + +void +js::jit::CheckPerf() { + if (!PerfChecked) { + const char* env = getenv("IONPERF"); + if (env == nullptr) { + PerfMode = PERF_MODE_NONE; + fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". "); + fprintf(stderr, "Perf mapping will be deactivated.\n"); + } else if (!strcmp(env, "none")) { + PerfMode = PERF_MODE_NONE; + } else if (!strcmp(env, "block")) { + PerfMode = PERF_MODE_BLOCK; + } else if (!strcmp(env, "func")) { + PerfMode = PERF_MODE_FUNC; + } else { + fprintf(stderr, "Use IONPERF=func to record at function granularity\n"); + fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n"); + fprintf(stderr, "to be leaked.\n"); + exit(0); + } + + if (PerfMode != PERF_MODE_NONE) { + PerfMutex = js_new<js::Mutex>(mutexid::PerfSpewer); + if (!PerfMutex) + MOZ_CRASH("failed to allocate PerfMutex"); + + if (openPerfMap(PERF_SPEW_DIR)) { + PerfChecked = true; + return; + } + +#if defined(__ANDROID__) + if (openPerfMap(PERF_SPEW_DIR_2)) { + PerfChecked = true; + return; + } +#endif + fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n"); + PerfMode = PERF_MODE_NONE; + } + PerfChecked = true; + } +} + +bool +js::jit::PerfBlockEnabled() { + MOZ_ASSERT(PerfMode); + return PerfMode == PERF_MODE_BLOCK; +} + +bool +js::jit::PerfFuncEnabled() { + MOZ_ASSERT(PerfMode); + return PerfMode == PERF_MODE_FUNC; +} + +static bool +lockPerfMap(void) +{ + if (!PerfEnabled()) + return false; + + PerfMutex->lock(); + + MOZ_ASSERT(PerfFilePtr); + return true; +} + +static void +unlockPerfMap() +{ + MOZ_ASSERT(PerfFilePtr); + fflush(PerfFilePtr); + PerfMutex->unlock(); +} + +uint32_t PerfSpewer::nextFunctionIndex = 0; + +bool +PerfSpewer::startBasicBlock(MBasicBlock* blk, + MacroAssembler& masm) +{ + if (!PerfBlockEnabled()) + return true; + + const char* filename = blk->info().script()->filename(); + unsigned lineNumber, columnNumber; + if (blk->pc()) { + lineNumber = PCToLineNumber(blk->info().script(), + blk->pc(), + &columnNumber); + } else { + lineNumber = 0; + columnNumber = 0; + } + Record r(filename, lineNumber, columnNumber, blk->id()); + masm.bind(&r.start); + return basicBlocks_.append(r); +} + +bool +PerfSpewer::endBasicBlock(MacroAssembler& masm) +{ + if (!PerfBlockEnabled()) + return true; + + masm.bind(&basicBlocks_.back().end); + return true; +} + +bool +PerfSpewer::noteEndInlineCode(MacroAssembler& masm) +{ + if (!PerfBlockEnabled()) + return true; + + masm.bind(&endInlineCode); + return true; +} + +void +PerfSpewer::writeProfile(JSScript* script, + JitCode* code, + MacroAssembler& masm) +{ + if (PerfFuncEnabled()) { + if (!lockPerfMap()) + return; + + uint32_t thisFunctionIndex = nextFunctionIndex++; + + size_t size = code->instructionsSize(); + if (size > 0) { + fprintf(PerfFilePtr, "%p %" PRIxSIZE " %s:%" PRIuSIZE ": Func%02d\n", + code->raw(), + size, + script->filename(), + script->lineno(), + thisFunctionIndex); + } + unlockPerfMap(); + return; + } + + if (PerfBlockEnabled() && basicBlocks_.length() > 0) { + if (!lockPerfMap()) + return; + + uint32_t thisFunctionIndex = nextFunctionIndex++; + uintptr_t funcStart = uintptr_t(code->raw()); + uintptr_t funcEndInlineCode = funcStart + endInlineCode.offset(); + uintptr_t funcEnd = funcStart + code->instructionsSize(); + + // function begins with the prologue, which is located before the first basic block + size_t prologueSize = basicBlocks_[0].start.offset(); + + if (prologueSize > 0) { + fprintf(PerfFilePtr, "%" PRIxSIZE " %" PRIxSIZE " %s:%" PRIuSIZE ": Func%02d-Prologue\n", + funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex); + } + + uintptr_t cur = funcStart + prologueSize; + for (uint32_t i = 0; i < basicBlocks_.length(); i++) { + Record& r = basicBlocks_[i]; + + uintptr_t blockStart = funcStart + r.start.offset(); + uintptr_t blockEnd = funcStart + r.end.offset(); + + MOZ_ASSERT(cur <= blockStart); + if (cur < blockStart) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-Block?\n", + cur, blockStart - cur, + script->filename(), script->lineno(), + thisFunctionIndex); + } + cur = blockEnd; + + size_t size = blockEnd - blockStart; + + if (size > 0) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s:%d:%d: Func%02d-Block%d\n", + blockStart, size, + r.filename, r.lineNumber, r.columnNumber, + thisFunctionIndex, r.id); + } + } + + MOZ_ASSERT(cur <= funcEndInlineCode); + if (cur < funcEndInlineCode) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-Epilogue\n", + cur, funcEndInlineCode - cur, + script->filename(), script->lineno(), + thisFunctionIndex); + } + + MOZ_ASSERT(funcEndInlineCode <= funcEnd); + if (funcEndInlineCode < funcEnd) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-OOL\n", + funcEndInlineCode, funcEnd - funcEndInlineCode, + script->filename(), script->lineno(), + thisFunctionIndex); + } + + unlockPerfMap(); + return; + } +} + +void +js::jit::writePerfSpewerBaselineProfile(JSScript* script, JitCode* code) +{ + if (!PerfEnabled()) + return; + + if (!lockPerfMap()) + return; + + size_t size = code->instructionsSize(); + if (size > 0) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s:%" PRIuSIZE ": Baseline\n", + reinterpret_cast<uintptr_t>(code->raw()), + size, script->filename(), script->lineno()); + } + + unlockPerfMap(); +} + +void +js::jit::writePerfSpewerJitCodeProfile(JitCode* code, const char* msg) +{ + if (!code || !PerfEnabled()) + return; + + if (!lockPerfMap()) + return; + + size_t size = code->instructionsSize(); + if (size > 0) { + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s (%p 0x%" PRIxSIZE ")\n", + reinterpret_cast<uintptr_t>(code->raw()), + size, msg, code->raw(), size); + } + + unlockPerfMap(); +} + +void +js::jit::writePerfSpewerWasmFunctionMap(uintptr_t base, uintptr_t size, + const char* filename, unsigned lineno, unsigned colIndex, + const char* funcName) +{ + if (!PerfFuncEnabled() || size == 0U) + return; + + if (!lockPerfMap()) + return; + + fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%u:%u: Function %s\n", + base, size, filename, lineno, colIndex, funcName); + + unlockPerfMap(); +} + +#endif // defined (JS_ION_PERF) |