| /* -*- 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" |
| |
| #if defined(__linux__) |
| # 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 "jslock.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 PRLock* 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 = js_sb_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 = PR_NewLock(); |
| if (!PerfMutex) |
| MOZ_CRASH(); |
| |
| 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; |
| |
| PR_Lock(PerfMutex); |
| |
| MOZ_ASSERT(PerfFilePtr); |
| return true; |
| } |
| |
| static void |
| unlockPerfMap() |
| { |
| MOZ_ASSERT(PerfFilePtr); |
| fflush(PerfFilePtr); |
| PR_Unlock(PerfMutex); |
| } |
| |
| 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::writePerfSpewerAsmJSFunctionMap(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) |