michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/PerfSpewer.h" michael@0: michael@0: #if defined(__linux__) michael@0: # include michael@0: #endif michael@0: michael@0: #ifdef JS_ION_PERF michael@0: # include "jit/IonSpewer.h" michael@0: # include "jit/LinearScan.h" michael@0: # include "jit/LIR.h" michael@0: # include "jit/MIR.h" michael@0: # include "jit/MIRGraph.h" michael@0: #endif michael@0: michael@0: // perf expects its data to be in a file /tmp/perf-PID.map, but for Android michael@0: // and B2G the map files are written to /data/local/tmp/perf-PID.map michael@0: // michael@0: // Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/ michael@0: // so also try /sdcard/. michael@0: michael@0: #ifndef PERF_SPEW_DIR michael@0: # if defined(__ANDROID__) michael@0: # define PERF_SPEW_DIR "/data/local/tmp/" michael@0: # define PERF_SPEW_DIR_2 "/sdcard/" michael@0: # else michael@0: # define PERF_SPEW_DIR "/tmp/" michael@0: # endif michael@0: #endif michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: #define PERF_MODE_NONE 1 michael@0: #define PERF_MODE_FUNC 2 michael@0: #define PERF_MODE_BLOCK 3 michael@0: michael@0: #ifdef JS_ION_PERF michael@0: michael@0: static uint32_t PerfMode = 0; michael@0: michael@0: static bool PerfChecked = false; michael@0: michael@0: static FILE *PerfFilePtr = nullptr; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: # include "jslock.h" michael@0: static PRLock *PerfMutex; michael@0: #endif michael@0: michael@0: static bool michael@0: openPerfMap(const char *dir) michael@0: { michael@0: const ssize_t bufferSize = 256; michael@0: char filenameBuffer[bufferSize]; michael@0: michael@0: if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize) michael@0: return false; michael@0: michael@0: JS_ASSERT(!PerfFilePtr); michael@0: PerfFilePtr = fopen(filenameBuffer, "a"); michael@0: michael@0: if (!PerfFilePtr) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: js::jit::CheckPerf() { michael@0: if (!PerfChecked) { michael@0: const char *env = getenv("IONPERF"); michael@0: if (env == nullptr) { michael@0: PerfMode = PERF_MODE_NONE; michael@0: fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". "); michael@0: fprintf(stderr, "Perf mapping will be deactivated.\n"); michael@0: } else if (!strcmp(env, "none")) { michael@0: PerfMode = PERF_MODE_NONE; michael@0: } else if (!strcmp(env, "block")) { michael@0: PerfMode = PERF_MODE_BLOCK; michael@0: } else if (!strcmp(env, "func")) { michael@0: PerfMode = PERF_MODE_FUNC; michael@0: } else { michael@0: fprintf(stderr, "Use IONPERF=func to record at function granularity\n"); michael@0: fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n"); michael@0: fprintf(stderr, "\n"); michael@0: fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n"); michael@0: fprintf(stderr, "to be leaked.\n"); michael@0: exit(0); michael@0: } michael@0: michael@0: if (PerfMode != PERF_MODE_NONE) { michael@0: #ifdef JS_THREADSAFE michael@0: PerfMutex = PR_NewLock(); michael@0: if (!PerfMutex) michael@0: MOZ_CRASH(); michael@0: #endif michael@0: michael@0: if (openPerfMap(PERF_SPEW_DIR)) { michael@0: PerfChecked = true; michael@0: return; michael@0: } michael@0: michael@0: #if defined(__ANDROID__) michael@0: if (openPerfMap(PERF_SPEW_DIR_2)) { michael@0: PerfChecked = true; michael@0: return; michael@0: } michael@0: #endif michael@0: fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n"); michael@0: PerfMode = PERF_MODE_NONE; michael@0: } michael@0: PerfChecked = true; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: js::jit::PerfBlockEnabled() { michael@0: JS_ASSERT(PerfMode); michael@0: return PerfMode == PERF_MODE_BLOCK; michael@0: } michael@0: michael@0: bool michael@0: js::jit::PerfFuncEnabled() { michael@0: JS_ASSERT(PerfMode); michael@0: return PerfMode == PERF_MODE_FUNC; michael@0: } michael@0: michael@0: static bool michael@0: lockPerfMap(void) michael@0: { michael@0: if (!PerfEnabled()) michael@0: return false; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: PR_Lock(PerfMutex); michael@0: #endif michael@0: michael@0: JS_ASSERT(PerfFilePtr); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: unlockPerfMap() michael@0: { michael@0: JS_ASSERT(PerfFilePtr); michael@0: fflush(PerfFilePtr); michael@0: #ifdef JS_THREADSAFE michael@0: PR_Unlock(PerfMutex); michael@0: #endif michael@0: } michael@0: michael@0: uint32_t PerfSpewer::nextFunctionIndex = 0; michael@0: michael@0: bool michael@0: PerfSpewer::startBasicBlock(MBasicBlock *blk, michael@0: MacroAssembler &masm) michael@0: { michael@0: if (!PerfBlockEnabled()) michael@0: return true; michael@0: michael@0: const char *filename = blk->info().script()->filename(); michael@0: unsigned lineNumber, columnNumber; michael@0: if (blk->pc()) { michael@0: lineNumber = PCToLineNumber(blk->info().script(), michael@0: blk->pc(), michael@0: &columnNumber); michael@0: } else { michael@0: lineNumber = 0; michael@0: columnNumber = 0; michael@0: } michael@0: Record r(filename, lineNumber, columnNumber, blk->id()); michael@0: masm.bind(&r.start); michael@0: return basicBlocks_.append(r); michael@0: } michael@0: michael@0: bool michael@0: PerfSpewer::endBasicBlock(MacroAssembler &masm) michael@0: { michael@0: if (!PerfBlockEnabled()) michael@0: return true; michael@0: michael@0: masm.bind(&basicBlocks_.back().end); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PerfSpewer::noteEndInlineCode(MacroAssembler &masm) michael@0: { michael@0: if (!PerfBlockEnabled()) michael@0: return true; michael@0: michael@0: masm.bind(&endInlineCode); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: PerfSpewer::writeProfile(JSScript *script, michael@0: JitCode *code, michael@0: MacroAssembler &masm) michael@0: { michael@0: if (PerfFuncEnabled()) { michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: uint32_t thisFunctionIndex = nextFunctionIndex++; michael@0: michael@0: size_t size = code->instructionsSize(); michael@0: if (size > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d\n", michael@0: reinterpret_cast(code->raw()), michael@0: size, michael@0: script->filename(), michael@0: script->lineno(), michael@0: thisFunctionIndex); michael@0: } michael@0: unlockPerfMap(); michael@0: return; michael@0: } michael@0: michael@0: if (PerfBlockEnabled() && basicBlocks_.length() > 0) { michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: uint32_t thisFunctionIndex = nextFunctionIndex++; michael@0: uintptr_t funcStart = uintptr_t(code->raw()); michael@0: uintptr_t funcEndInlineCode = funcStart + masm.actualOffset(endInlineCode.offset()); michael@0: uintptr_t funcEnd = funcStart + code->instructionsSize(); michael@0: michael@0: // function begins with the prologue, which is located before the first basic block michael@0: size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset()); michael@0: michael@0: if (prologueSize > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n", michael@0: funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex); michael@0: } michael@0: michael@0: uintptr_t cur = funcStart + prologueSize; michael@0: for (uint32_t i = 0; i < basicBlocks_.length(); i++) { michael@0: Record &r = basicBlocks_[i]; michael@0: michael@0: uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset()); michael@0: uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset()); michael@0: michael@0: JS_ASSERT(cur <= blockStart); michael@0: if (cur < blockStart) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Block?\n", michael@0: static_cast(cur), michael@0: static_cast(blockStart - cur), michael@0: script->filename(), script->lineno(), michael@0: thisFunctionIndex); michael@0: } michael@0: cur = blockEnd; michael@0: michael@0: size_t size = blockEnd - blockStart; michael@0: michael@0: if (size > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Func%02d-Block%d\n", michael@0: static_cast(blockStart), size, michael@0: r.filename, r.lineNumber, r.columnNumber, michael@0: thisFunctionIndex, r.id); michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(cur <= funcEndInlineCode); michael@0: if (cur < funcEndInlineCode) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Epilogue\n", michael@0: cur, funcEndInlineCode - cur, michael@0: script->filename(), script->lineno(), michael@0: thisFunctionIndex); michael@0: } michael@0: michael@0: JS_ASSERT(funcEndInlineCode <= funcEnd); michael@0: if (funcEndInlineCode < funcEnd) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-OOL\n", michael@0: funcEndInlineCode, funcEnd - funcEndInlineCode, michael@0: script->filename(), script->lineno(), michael@0: thisFunctionIndex); michael@0: } michael@0: michael@0: unlockPerfMap(); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: js::jit::writePerfSpewerBaselineProfile(JSScript *script, JitCode *code) michael@0: { michael@0: if (!PerfEnabled()) michael@0: return; michael@0: michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: size_t size = code->instructionsSize(); michael@0: if (size > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d: Baseline\n", michael@0: reinterpret_cast(code->raw()), michael@0: size, script->filename(), script->lineno()); michael@0: } michael@0: michael@0: unlockPerfMap(); michael@0: } michael@0: michael@0: void michael@0: js::jit::writePerfSpewerJitCodeProfile(JitCode *code, const char *msg) michael@0: { michael@0: if (!code || !PerfEnabled()) michael@0: return; michael@0: michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: size_t size = code->instructionsSize(); michael@0: if (size > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s (%p 0x%zx)\n", michael@0: reinterpret_cast(code->raw()), michael@0: size, msg, code->raw(), size); michael@0: } michael@0: michael@0: unlockPerfMap(); michael@0: } michael@0: michael@0: void michael@0: js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, michael@0: const char *filename, unsigned lineno, unsigned colIndex, michael@0: const char *funcName) michael@0: { michael@0: if (!PerfFuncEnabled() || size == 0U) michael@0: return; michael@0: michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName); michael@0: michael@0: unlockPerfMap(); michael@0: } michael@0: michael@0: bool michael@0: AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm) michael@0: { michael@0: if (!PerfBlockEnabled()) michael@0: return true; michael@0: michael@0: Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later michael@0: masm.bind(&r.start); michael@0: return basicBlocks_.append(r); michael@0: } michael@0: michael@0: void michael@0: AsmJSPerfSpewer::noteBlocksOffsets() michael@0: { michael@0: if (!PerfBlockEnabled()) michael@0: return; michael@0: michael@0: for (uint32_t i = 0; i < basicBlocks_.length(); i++) { michael@0: Record &r = basicBlocks_[i]; michael@0: r.startOffset = r.start.offset(); michael@0: r.endOffset = r.end.offset(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: js::jit::writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset, michael@0: size_t funcEndInlineOffset, size_t funcSize, michael@0: const char *filename, const char *funcName, michael@0: const js::jit::BasicBlocksVector &basicBlocks) michael@0: { michael@0: if (!PerfBlockEnabled() || basicBlocks.empty()) michael@0: return; michael@0: michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: // function begins with the prologue, which is located before the first basic block michael@0: size_t prologueSize = basicBlocks[0].startOffset - funcStartOffset; michael@0: size_t cur = baseAddress + funcStartOffset + prologueSize; michael@0: size_t funcEndInlineCode = baseAddress + funcEndInlineOffset; michael@0: size_t funcEnd = baseAddress + funcStartOffset + funcSize; michael@0: michael@0: if (prologueSize > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n", michael@0: baseAddress + funcStartOffset, prologueSize, filename, funcName); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < basicBlocks.length(); i++) { michael@0: const Record &r = basicBlocks[i]; michael@0: michael@0: size_t blockStart = baseAddress + r.startOffset; michael@0: size_t blockEnd = baseAddress + r.endOffset; michael@0: michael@0: JS_ASSERT(cur <= blockStart); michael@0: if (cur < blockStart) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s: Function %s - unknown block\n", michael@0: cur, blockStart - cur, michael@0: filename, michael@0: funcName); michael@0: } michael@0: cur = blockEnd; michael@0: michael@0: size_t size = blockEnd - blockStart; michael@0: if (size > 0) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s - Block %d\n", michael@0: blockStart, size, michael@0: filename, r.lineNumber, r.columnNumber, michael@0: funcName, r.id); michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(cur <= funcEndInlineCode); michael@0: if (cur < funcEndInlineCode) michael@0: fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Epilogue\n", michael@0: cur, funcEndInlineCode - cur, filename, funcName); michael@0: michael@0: JS_ASSERT(funcEndInlineCode <= funcEnd); michael@0: if (funcEndInlineCode < funcEnd) { michael@0: fprintf(PerfFilePtr, "%zx %zx %s: Function %s - OOL\n", michael@0: funcEndInlineCode, funcEnd - funcEndInlineCode, filename, funcName); michael@0: } michael@0: michael@0: unlockPerfMap(); michael@0: } michael@0: michael@0: void michael@0: js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size) michael@0: { michael@0: if (size == 0) michael@0: return; michael@0: michael@0: if (!lockPerfMap()) michael@0: return; michael@0: michael@0: fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size); michael@0: michael@0: unlockPerfMap(); michael@0: } michael@0: michael@0: #endif // defined (JS_ION_PERF)