1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/PerfSpewer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,444 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/PerfSpewer.h" 1.11 + 1.12 +#if defined(__linux__) 1.13 +# include <unistd.h> 1.14 +#endif 1.15 + 1.16 +#ifdef JS_ION_PERF 1.17 +# include "jit/IonSpewer.h" 1.18 +# include "jit/LinearScan.h" 1.19 +# include "jit/LIR.h" 1.20 +# include "jit/MIR.h" 1.21 +# include "jit/MIRGraph.h" 1.22 +#endif 1.23 + 1.24 +// perf expects its data to be in a file /tmp/perf-PID.map, but for Android 1.25 +// and B2G the map files are written to /data/local/tmp/perf-PID.map 1.26 +// 1.27 +// Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/ 1.28 +// so also try /sdcard/. 1.29 + 1.30 +#ifndef PERF_SPEW_DIR 1.31 +# if defined(__ANDROID__) 1.32 +# define PERF_SPEW_DIR "/data/local/tmp/" 1.33 +# define PERF_SPEW_DIR_2 "/sdcard/" 1.34 +# else 1.35 +# define PERF_SPEW_DIR "/tmp/" 1.36 +# endif 1.37 +#endif 1.38 + 1.39 +using namespace js; 1.40 +using namespace js::jit; 1.41 + 1.42 +#define PERF_MODE_NONE 1 1.43 +#define PERF_MODE_FUNC 2 1.44 +#define PERF_MODE_BLOCK 3 1.45 + 1.46 +#ifdef JS_ION_PERF 1.47 + 1.48 +static uint32_t PerfMode = 0; 1.49 + 1.50 +static bool PerfChecked = false; 1.51 + 1.52 +static FILE *PerfFilePtr = nullptr; 1.53 + 1.54 +#ifdef JS_THREADSAFE 1.55 +# include "jslock.h" 1.56 +static PRLock *PerfMutex; 1.57 +#endif 1.58 + 1.59 +static bool 1.60 +openPerfMap(const char *dir) 1.61 +{ 1.62 + const ssize_t bufferSize = 256; 1.63 + char filenameBuffer[bufferSize]; 1.64 + 1.65 + if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize) 1.66 + return false; 1.67 + 1.68 + JS_ASSERT(!PerfFilePtr); 1.69 + PerfFilePtr = fopen(filenameBuffer, "a"); 1.70 + 1.71 + if (!PerfFilePtr) 1.72 + return false; 1.73 + 1.74 + return true; 1.75 +} 1.76 + 1.77 +void 1.78 +js::jit::CheckPerf() { 1.79 + if (!PerfChecked) { 1.80 + const char *env = getenv("IONPERF"); 1.81 + if (env == nullptr) { 1.82 + PerfMode = PERF_MODE_NONE; 1.83 + fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". "); 1.84 + fprintf(stderr, "Perf mapping will be deactivated.\n"); 1.85 + } else if (!strcmp(env, "none")) { 1.86 + PerfMode = PERF_MODE_NONE; 1.87 + } else if (!strcmp(env, "block")) { 1.88 + PerfMode = PERF_MODE_BLOCK; 1.89 + } else if (!strcmp(env, "func")) { 1.90 + PerfMode = PERF_MODE_FUNC; 1.91 + } else { 1.92 + fprintf(stderr, "Use IONPERF=func to record at function granularity\n"); 1.93 + fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n"); 1.94 + fprintf(stderr, "\n"); 1.95 + fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n"); 1.96 + fprintf(stderr, "to be leaked.\n"); 1.97 + exit(0); 1.98 + } 1.99 + 1.100 + if (PerfMode != PERF_MODE_NONE) { 1.101 +#ifdef JS_THREADSAFE 1.102 + PerfMutex = PR_NewLock(); 1.103 + if (!PerfMutex) 1.104 + MOZ_CRASH(); 1.105 +#endif 1.106 + 1.107 + if (openPerfMap(PERF_SPEW_DIR)) { 1.108 + PerfChecked = true; 1.109 + return; 1.110 + } 1.111 + 1.112 +#if defined(__ANDROID__) 1.113 + if (openPerfMap(PERF_SPEW_DIR_2)) { 1.114 + PerfChecked = true; 1.115 + return; 1.116 + } 1.117 +#endif 1.118 + fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n"); 1.119 + PerfMode = PERF_MODE_NONE; 1.120 + } 1.121 + PerfChecked = true; 1.122 + } 1.123 +} 1.124 + 1.125 +bool 1.126 +js::jit::PerfBlockEnabled() { 1.127 + JS_ASSERT(PerfMode); 1.128 + return PerfMode == PERF_MODE_BLOCK; 1.129 +} 1.130 + 1.131 +bool 1.132 +js::jit::PerfFuncEnabled() { 1.133 + JS_ASSERT(PerfMode); 1.134 + return PerfMode == PERF_MODE_FUNC; 1.135 +} 1.136 + 1.137 +static bool 1.138 +lockPerfMap(void) 1.139 +{ 1.140 + if (!PerfEnabled()) 1.141 + return false; 1.142 + 1.143 +#ifdef JS_THREADSAFE 1.144 + PR_Lock(PerfMutex); 1.145 +#endif 1.146 + 1.147 + JS_ASSERT(PerfFilePtr); 1.148 + return true; 1.149 +} 1.150 + 1.151 +static void 1.152 +unlockPerfMap() 1.153 +{ 1.154 + JS_ASSERT(PerfFilePtr); 1.155 + fflush(PerfFilePtr); 1.156 +#ifdef JS_THREADSAFE 1.157 + PR_Unlock(PerfMutex); 1.158 +#endif 1.159 +} 1.160 + 1.161 +uint32_t PerfSpewer::nextFunctionIndex = 0; 1.162 + 1.163 +bool 1.164 +PerfSpewer::startBasicBlock(MBasicBlock *blk, 1.165 + MacroAssembler &masm) 1.166 +{ 1.167 + if (!PerfBlockEnabled()) 1.168 + return true; 1.169 + 1.170 + const char *filename = blk->info().script()->filename(); 1.171 + unsigned lineNumber, columnNumber; 1.172 + if (blk->pc()) { 1.173 + lineNumber = PCToLineNumber(blk->info().script(), 1.174 + blk->pc(), 1.175 + &columnNumber); 1.176 + } else { 1.177 + lineNumber = 0; 1.178 + columnNumber = 0; 1.179 + } 1.180 + Record r(filename, lineNumber, columnNumber, blk->id()); 1.181 + masm.bind(&r.start); 1.182 + return basicBlocks_.append(r); 1.183 +} 1.184 + 1.185 +bool 1.186 +PerfSpewer::endBasicBlock(MacroAssembler &masm) 1.187 +{ 1.188 + if (!PerfBlockEnabled()) 1.189 + return true; 1.190 + 1.191 + masm.bind(&basicBlocks_.back().end); 1.192 + return true; 1.193 +} 1.194 + 1.195 +bool 1.196 +PerfSpewer::noteEndInlineCode(MacroAssembler &masm) 1.197 +{ 1.198 + if (!PerfBlockEnabled()) 1.199 + return true; 1.200 + 1.201 + masm.bind(&endInlineCode); 1.202 + return true; 1.203 +} 1.204 + 1.205 +void 1.206 +PerfSpewer::writeProfile(JSScript *script, 1.207 + JitCode *code, 1.208 + MacroAssembler &masm) 1.209 +{ 1.210 + if (PerfFuncEnabled()) { 1.211 + if (!lockPerfMap()) 1.212 + return; 1.213 + 1.214 + uint32_t thisFunctionIndex = nextFunctionIndex++; 1.215 + 1.216 + size_t size = code->instructionsSize(); 1.217 + if (size > 0) { 1.218 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d\n", 1.219 + reinterpret_cast<uintptr_t>(code->raw()), 1.220 + size, 1.221 + script->filename(), 1.222 + script->lineno(), 1.223 + thisFunctionIndex); 1.224 + } 1.225 + unlockPerfMap(); 1.226 + return; 1.227 + } 1.228 + 1.229 + if (PerfBlockEnabled() && basicBlocks_.length() > 0) { 1.230 + if (!lockPerfMap()) 1.231 + return; 1.232 + 1.233 + uint32_t thisFunctionIndex = nextFunctionIndex++; 1.234 + uintptr_t funcStart = uintptr_t(code->raw()); 1.235 + uintptr_t funcEndInlineCode = funcStart + masm.actualOffset(endInlineCode.offset()); 1.236 + uintptr_t funcEnd = funcStart + code->instructionsSize(); 1.237 + 1.238 + // function begins with the prologue, which is located before the first basic block 1.239 + size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset()); 1.240 + 1.241 + if (prologueSize > 0) { 1.242 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n", 1.243 + funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex); 1.244 + } 1.245 + 1.246 + uintptr_t cur = funcStart + prologueSize; 1.247 + for (uint32_t i = 0; i < basicBlocks_.length(); i++) { 1.248 + Record &r = basicBlocks_[i]; 1.249 + 1.250 + uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset()); 1.251 + uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset()); 1.252 + 1.253 + JS_ASSERT(cur <= blockStart); 1.254 + if (cur < blockStart) { 1.255 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Block?\n", 1.256 + static_cast<uintptr_t>(cur), 1.257 + static_cast<uintptr_t>(blockStart - cur), 1.258 + script->filename(), script->lineno(), 1.259 + thisFunctionIndex); 1.260 + } 1.261 + cur = blockEnd; 1.262 + 1.263 + size_t size = blockEnd - blockStart; 1.264 + 1.265 + if (size > 0) { 1.266 + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Func%02d-Block%d\n", 1.267 + static_cast<uintptr_t>(blockStart), size, 1.268 + r.filename, r.lineNumber, r.columnNumber, 1.269 + thisFunctionIndex, r.id); 1.270 + } 1.271 + } 1.272 + 1.273 + JS_ASSERT(cur <= funcEndInlineCode); 1.274 + if (cur < funcEndInlineCode) { 1.275 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Epilogue\n", 1.276 + cur, funcEndInlineCode - cur, 1.277 + script->filename(), script->lineno(), 1.278 + thisFunctionIndex); 1.279 + } 1.280 + 1.281 + JS_ASSERT(funcEndInlineCode <= funcEnd); 1.282 + if (funcEndInlineCode < funcEnd) { 1.283 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-OOL\n", 1.284 + funcEndInlineCode, funcEnd - funcEndInlineCode, 1.285 + script->filename(), script->lineno(), 1.286 + thisFunctionIndex); 1.287 + } 1.288 + 1.289 + unlockPerfMap(); 1.290 + return; 1.291 + } 1.292 +} 1.293 + 1.294 +void 1.295 +js::jit::writePerfSpewerBaselineProfile(JSScript *script, JitCode *code) 1.296 +{ 1.297 + if (!PerfEnabled()) 1.298 + return; 1.299 + 1.300 + if (!lockPerfMap()) 1.301 + return; 1.302 + 1.303 + size_t size = code->instructionsSize(); 1.304 + if (size > 0) { 1.305 + fprintf(PerfFilePtr, "%zx %zx %s:%d: Baseline\n", 1.306 + reinterpret_cast<uintptr_t>(code->raw()), 1.307 + size, script->filename(), script->lineno()); 1.308 + } 1.309 + 1.310 + unlockPerfMap(); 1.311 +} 1.312 + 1.313 +void 1.314 +js::jit::writePerfSpewerJitCodeProfile(JitCode *code, const char *msg) 1.315 +{ 1.316 + if (!code || !PerfEnabled()) 1.317 + return; 1.318 + 1.319 + if (!lockPerfMap()) 1.320 + return; 1.321 + 1.322 + size_t size = code->instructionsSize(); 1.323 + if (size > 0) { 1.324 + fprintf(PerfFilePtr, "%zx %zx %s (%p 0x%zx)\n", 1.325 + reinterpret_cast<uintptr_t>(code->raw()), 1.326 + size, msg, code->raw(), size); 1.327 + } 1.328 + 1.329 + unlockPerfMap(); 1.330 +} 1.331 + 1.332 +void 1.333 +js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, 1.334 + const char *filename, unsigned lineno, unsigned colIndex, 1.335 + const char *funcName) 1.336 +{ 1.337 + if (!PerfFuncEnabled() || size == 0U) 1.338 + return; 1.339 + 1.340 + if (!lockPerfMap()) 1.341 + return; 1.342 + 1.343 + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName); 1.344 + 1.345 + unlockPerfMap(); 1.346 +} 1.347 + 1.348 +bool 1.349 +AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm) 1.350 +{ 1.351 + if (!PerfBlockEnabled()) 1.352 + return true; 1.353 + 1.354 + Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later 1.355 + masm.bind(&r.start); 1.356 + return basicBlocks_.append(r); 1.357 +} 1.358 + 1.359 +void 1.360 +AsmJSPerfSpewer::noteBlocksOffsets() 1.361 +{ 1.362 + if (!PerfBlockEnabled()) 1.363 + return; 1.364 + 1.365 + for (uint32_t i = 0; i < basicBlocks_.length(); i++) { 1.366 + Record &r = basicBlocks_[i]; 1.367 + r.startOffset = r.start.offset(); 1.368 + r.endOffset = r.end.offset(); 1.369 + } 1.370 +} 1.371 + 1.372 +void 1.373 +js::jit::writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset, 1.374 + size_t funcEndInlineOffset, size_t funcSize, 1.375 + const char *filename, const char *funcName, 1.376 + const js::jit::BasicBlocksVector &basicBlocks) 1.377 +{ 1.378 + if (!PerfBlockEnabled() || basicBlocks.empty()) 1.379 + return; 1.380 + 1.381 + if (!lockPerfMap()) 1.382 + return; 1.383 + 1.384 + // function begins with the prologue, which is located before the first basic block 1.385 + size_t prologueSize = basicBlocks[0].startOffset - funcStartOffset; 1.386 + size_t cur = baseAddress + funcStartOffset + prologueSize; 1.387 + size_t funcEndInlineCode = baseAddress + funcEndInlineOffset; 1.388 + size_t funcEnd = baseAddress + funcStartOffset + funcSize; 1.389 + 1.390 + if (prologueSize > 0) { 1.391 + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n", 1.392 + baseAddress + funcStartOffset, prologueSize, filename, funcName); 1.393 + } 1.394 + 1.395 + for (uint32_t i = 0; i < basicBlocks.length(); i++) { 1.396 + const Record &r = basicBlocks[i]; 1.397 + 1.398 + size_t blockStart = baseAddress + r.startOffset; 1.399 + size_t blockEnd = baseAddress + r.endOffset; 1.400 + 1.401 + JS_ASSERT(cur <= blockStart); 1.402 + if (cur < blockStart) { 1.403 + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - unknown block\n", 1.404 + cur, blockStart - cur, 1.405 + filename, 1.406 + funcName); 1.407 + } 1.408 + cur = blockEnd; 1.409 + 1.410 + size_t size = blockEnd - blockStart; 1.411 + if (size > 0) { 1.412 + fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s - Block %d\n", 1.413 + blockStart, size, 1.414 + filename, r.lineNumber, r.columnNumber, 1.415 + funcName, r.id); 1.416 + } 1.417 + } 1.418 + 1.419 + JS_ASSERT(cur <= funcEndInlineCode); 1.420 + if (cur < funcEndInlineCode) 1.421 + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Epilogue\n", 1.422 + cur, funcEndInlineCode - cur, filename, funcName); 1.423 + 1.424 + JS_ASSERT(funcEndInlineCode <= funcEnd); 1.425 + if (funcEndInlineCode < funcEnd) { 1.426 + fprintf(PerfFilePtr, "%zx %zx %s: Function %s - OOL\n", 1.427 + funcEndInlineCode, funcEnd - funcEndInlineCode, filename, funcName); 1.428 + } 1.429 + 1.430 + unlockPerfMap(); 1.431 +} 1.432 + 1.433 +void 1.434 +js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size) 1.435 +{ 1.436 + if (size == 0) 1.437 + return; 1.438 + 1.439 + if (!lockPerfMap()) 1.440 + return; 1.441 + 1.442 + fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size); 1.443 + 1.444 + unlockPerfMap(); 1.445 +} 1.446 + 1.447 +#endif // defined (JS_ION_PERF)