js/src/jit/PerfSpewer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/PerfSpewer.h"
michael@0 8
michael@0 9 #if defined(__linux__)
michael@0 10 # include <unistd.h>
michael@0 11 #endif
michael@0 12
michael@0 13 #ifdef JS_ION_PERF
michael@0 14 # include "jit/IonSpewer.h"
michael@0 15 # include "jit/LinearScan.h"
michael@0 16 # include "jit/LIR.h"
michael@0 17 # include "jit/MIR.h"
michael@0 18 # include "jit/MIRGraph.h"
michael@0 19 #endif
michael@0 20
michael@0 21 // perf expects its data to be in a file /tmp/perf-PID.map, but for Android
michael@0 22 // and B2G the map files are written to /data/local/tmp/perf-PID.map
michael@0 23 //
michael@0 24 // Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/
michael@0 25 // so also try /sdcard/.
michael@0 26
michael@0 27 #ifndef PERF_SPEW_DIR
michael@0 28 # if defined(__ANDROID__)
michael@0 29 # define PERF_SPEW_DIR "/data/local/tmp/"
michael@0 30 # define PERF_SPEW_DIR_2 "/sdcard/"
michael@0 31 # else
michael@0 32 # define PERF_SPEW_DIR "/tmp/"
michael@0 33 # endif
michael@0 34 #endif
michael@0 35
michael@0 36 using namespace js;
michael@0 37 using namespace js::jit;
michael@0 38
michael@0 39 #define PERF_MODE_NONE 1
michael@0 40 #define PERF_MODE_FUNC 2
michael@0 41 #define PERF_MODE_BLOCK 3
michael@0 42
michael@0 43 #ifdef JS_ION_PERF
michael@0 44
michael@0 45 static uint32_t PerfMode = 0;
michael@0 46
michael@0 47 static bool PerfChecked = false;
michael@0 48
michael@0 49 static FILE *PerfFilePtr = nullptr;
michael@0 50
michael@0 51 #ifdef JS_THREADSAFE
michael@0 52 # include "jslock.h"
michael@0 53 static PRLock *PerfMutex;
michael@0 54 #endif
michael@0 55
michael@0 56 static bool
michael@0 57 openPerfMap(const char *dir)
michael@0 58 {
michael@0 59 const ssize_t bufferSize = 256;
michael@0 60 char filenameBuffer[bufferSize];
michael@0 61
michael@0 62 if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize)
michael@0 63 return false;
michael@0 64
michael@0 65 JS_ASSERT(!PerfFilePtr);
michael@0 66 PerfFilePtr = fopen(filenameBuffer, "a");
michael@0 67
michael@0 68 if (!PerfFilePtr)
michael@0 69 return false;
michael@0 70
michael@0 71 return true;
michael@0 72 }
michael@0 73
michael@0 74 void
michael@0 75 js::jit::CheckPerf() {
michael@0 76 if (!PerfChecked) {
michael@0 77 const char *env = getenv("IONPERF");
michael@0 78 if (env == nullptr) {
michael@0 79 PerfMode = PERF_MODE_NONE;
michael@0 80 fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". ");
michael@0 81 fprintf(stderr, "Perf mapping will be deactivated.\n");
michael@0 82 } else if (!strcmp(env, "none")) {
michael@0 83 PerfMode = PERF_MODE_NONE;
michael@0 84 } else if (!strcmp(env, "block")) {
michael@0 85 PerfMode = PERF_MODE_BLOCK;
michael@0 86 } else if (!strcmp(env, "func")) {
michael@0 87 PerfMode = PERF_MODE_FUNC;
michael@0 88 } else {
michael@0 89 fprintf(stderr, "Use IONPERF=func to record at function granularity\n");
michael@0 90 fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n");
michael@0 91 fprintf(stderr, "\n");
michael@0 92 fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n");
michael@0 93 fprintf(stderr, "to be leaked.\n");
michael@0 94 exit(0);
michael@0 95 }
michael@0 96
michael@0 97 if (PerfMode != PERF_MODE_NONE) {
michael@0 98 #ifdef JS_THREADSAFE
michael@0 99 PerfMutex = PR_NewLock();
michael@0 100 if (!PerfMutex)
michael@0 101 MOZ_CRASH();
michael@0 102 #endif
michael@0 103
michael@0 104 if (openPerfMap(PERF_SPEW_DIR)) {
michael@0 105 PerfChecked = true;
michael@0 106 return;
michael@0 107 }
michael@0 108
michael@0 109 #if defined(__ANDROID__)
michael@0 110 if (openPerfMap(PERF_SPEW_DIR_2)) {
michael@0 111 PerfChecked = true;
michael@0 112 return;
michael@0 113 }
michael@0 114 #endif
michael@0 115 fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n");
michael@0 116 PerfMode = PERF_MODE_NONE;
michael@0 117 }
michael@0 118 PerfChecked = true;
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 bool
michael@0 123 js::jit::PerfBlockEnabled() {
michael@0 124 JS_ASSERT(PerfMode);
michael@0 125 return PerfMode == PERF_MODE_BLOCK;
michael@0 126 }
michael@0 127
michael@0 128 bool
michael@0 129 js::jit::PerfFuncEnabled() {
michael@0 130 JS_ASSERT(PerfMode);
michael@0 131 return PerfMode == PERF_MODE_FUNC;
michael@0 132 }
michael@0 133
michael@0 134 static bool
michael@0 135 lockPerfMap(void)
michael@0 136 {
michael@0 137 if (!PerfEnabled())
michael@0 138 return false;
michael@0 139
michael@0 140 #ifdef JS_THREADSAFE
michael@0 141 PR_Lock(PerfMutex);
michael@0 142 #endif
michael@0 143
michael@0 144 JS_ASSERT(PerfFilePtr);
michael@0 145 return true;
michael@0 146 }
michael@0 147
michael@0 148 static void
michael@0 149 unlockPerfMap()
michael@0 150 {
michael@0 151 JS_ASSERT(PerfFilePtr);
michael@0 152 fflush(PerfFilePtr);
michael@0 153 #ifdef JS_THREADSAFE
michael@0 154 PR_Unlock(PerfMutex);
michael@0 155 #endif
michael@0 156 }
michael@0 157
michael@0 158 uint32_t PerfSpewer::nextFunctionIndex = 0;
michael@0 159
michael@0 160 bool
michael@0 161 PerfSpewer::startBasicBlock(MBasicBlock *blk,
michael@0 162 MacroAssembler &masm)
michael@0 163 {
michael@0 164 if (!PerfBlockEnabled())
michael@0 165 return true;
michael@0 166
michael@0 167 const char *filename = blk->info().script()->filename();
michael@0 168 unsigned lineNumber, columnNumber;
michael@0 169 if (blk->pc()) {
michael@0 170 lineNumber = PCToLineNumber(blk->info().script(),
michael@0 171 blk->pc(),
michael@0 172 &columnNumber);
michael@0 173 } else {
michael@0 174 lineNumber = 0;
michael@0 175 columnNumber = 0;
michael@0 176 }
michael@0 177 Record r(filename, lineNumber, columnNumber, blk->id());
michael@0 178 masm.bind(&r.start);
michael@0 179 return basicBlocks_.append(r);
michael@0 180 }
michael@0 181
michael@0 182 bool
michael@0 183 PerfSpewer::endBasicBlock(MacroAssembler &masm)
michael@0 184 {
michael@0 185 if (!PerfBlockEnabled())
michael@0 186 return true;
michael@0 187
michael@0 188 masm.bind(&basicBlocks_.back().end);
michael@0 189 return true;
michael@0 190 }
michael@0 191
michael@0 192 bool
michael@0 193 PerfSpewer::noteEndInlineCode(MacroAssembler &masm)
michael@0 194 {
michael@0 195 if (!PerfBlockEnabled())
michael@0 196 return true;
michael@0 197
michael@0 198 masm.bind(&endInlineCode);
michael@0 199 return true;
michael@0 200 }
michael@0 201
michael@0 202 void
michael@0 203 PerfSpewer::writeProfile(JSScript *script,
michael@0 204 JitCode *code,
michael@0 205 MacroAssembler &masm)
michael@0 206 {
michael@0 207 if (PerfFuncEnabled()) {
michael@0 208 if (!lockPerfMap())
michael@0 209 return;
michael@0 210
michael@0 211 uint32_t thisFunctionIndex = nextFunctionIndex++;
michael@0 212
michael@0 213 size_t size = code->instructionsSize();
michael@0 214 if (size > 0) {
michael@0 215 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d\n",
michael@0 216 reinterpret_cast<uintptr_t>(code->raw()),
michael@0 217 size,
michael@0 218 script->filename(),
michael@0 219 script->lineno(),
michael@0 220 thisFunctionIndex);
michael@0 221 }
michael@0 222 unlockPerfMap();
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 if (PerfBlockEnabled() && basicBlocks_.length() > 0) {
michael@0 227 if (!lockPerfMap())
michael@0 228 return;
michael@0 229
michael@0 230 uint32_t thisFunctionIndex = nextFunctionIndex++;
michael@0 231 uintptr_t funcStart = uintptr_t(code->raw());
michael@0 232 uintptr_t funcEndInlineCode = funcStart + masm.actualOffset(endInlineCode.offset());
michael@0 233 uintptr_t funcEnd = funcStart + code->instructionsSize();
michael@0 234
michael@0 235 // function begins with the prologue, which is located before the first basic block
michael@0 236 size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset());
michael@0 237
michael@0 238 if (prologueSize > 0) {
michael@0 239 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n",
michael@0 240 funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex);
michael@0 241 }
michael@0 242
michael@0 243 uintptr_t cur = funcStart + prologueSize;
michael@0 244 for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
michael@0 245 Record &r = basicBlocks_[i];
michael@0 246
michael@0 247 uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset());
michael@0 248 uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset());
michael@0 249
michael@0 250 JS_ASSERT(cur <= blockStart);
michael@0 251 if (cur < blockStart) {
michael@0 252 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Block?\n",
michael@0 253 static_cast<uintptr_t>(cur),
michael@0 254 static_cast<uintptr_t>(blockStart - cur),
michael@0 255 script->filename(), script->lineno(),
michael@0 256 thisFunctionIndex);
michael@0 257 }
michael@0 258 cur = blockEnd;
michael@0 259
michael@0 260 size_t size = blockEnd - blockStart;
michael@0 261
michael@0 262 if (size > 0) {
michael@0 263 fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Func%02d-Block%d\n",
michael@0 264 static_cast<uintptr_t>(blockStart), size,
michael@0 265 r.filename, r.lineNumber, r.columnNumber,
michael@0 266 thisFunctionIndex, r.id);
michael@0 267 }
michael@0 268 }
michael@0 269
michael@0 270 JS_ASSERT(cur <= funcEndInlineCode);
michael@0 271 if (cur < funcEndInlineCode) {
michael@0 272 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Epilogue\n",
michael@0 273 cur, funcEndInlineCode - cur,
michael@0 274 script->filename(), script->lineno(),
michael@0 275 thisFunctionIndex);
michael@0 276 }
michael@0 277
michael@0 278 JS_ASSERT(funcEndInlineCode <= funcEnd);
michael@0 279 if (funcEndInlineCode < funcEnd) {
michael@0 280 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-OOL\n",
michael@0 281 funcEndInlineCode, funcEnd - funcEndInlineCode,
michael@0 282 script->filename(), script->lineno(),
michael@0 283 thisFunctionIndex);
michael@0 284 }
michael@0 285
michael@0 286 unlockPerfMap();
michael@0 287 return;
michael@0 288 }
michael@0 289 }
michael@0 290
michael@0 291 void
michael@0 292 js::jit::writePerfSpewerBaselineProfile(JSScript *script, JitCode *code)
michael@0 293 {
michael@0 294 if (!PerfEnabled())
michael@0 295 return;
michael@0 296
michael@0 297 if (!lockPerfMap())
michael@0 298 return;
michael@0 299
michael@0 300 size_t size = code->instructionsSize();
michael@0 301 if (size > 0) {
michael@0 302 fprintf(PerfFilePtr, "%zx %zx %s:%d: Baseline\n",
michael@0 303 reinterpret_cast<uintptr_t>(code->raw()),
michael@0 304 size, script->filename(), script->lineno());
michael@0 305 }
michael@0 306
michael@0 307 unlockPerfMap();
michael@0 308 }
michael@0 309
michael@0 310 void
michael@0 311 js::jit::writePerfSpewerJitCodeProfile(JitCode *code, const char *msg)
michael@0 312 {
michael@0 313 if (!code || !PerfEnabled())
michael@0 314 return;
michael@0 315
michael@0 316 if (!lockPerfMap())
michael@0 317 return;
michael@0 318
michael@0 319 size_t size = code->instructionsSize();
michael@0 320 if (size > 0) {
michael@0 321 fprintf(PerfFilePtr, "%zx %zx %s (%p 0x%zx)\n",
michael@0 322 reinterpret_cast<uintptr_t>(code->raw()),
michael@0 323 size, msg, code->raw(), size);
michael@0 324 }
michael@0 325
michael@0 326 unlockPerfMap();
michael@0 327 }
michael@0 328
michael@0 329 void
michael@0 330 js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size,
michael@0 331 const char *filename, unsigned lineno, unsigned colIndex,
michael@0 332 const char *funcName)
michael@0 333 {
michael@0 334 if (!PerfFuncEnabled() || size == 0U)
michael@0 335 return;
michael@0 336
michael@0 337 if (!lockPerfMap())
michael@0 338 return;
michael@0 339
michael@0 340 fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName);
michael@0 341
michael@0 342 unlockPerfMap();
michael@0 343 }
michael@0 344
michael@0 345 bool
michael@0 346 AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm)
michael@0 347 {
michael@0 348 if (!PerfBlockEnabled())
michael@0 349 return true;
michael@0 350
michael@0 351 Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later
michael@0 352 masm.bind(&r.start);
michael@0 353 return basicBlocks_.append(r);
michael@0 354 }
michael@0 355
michael@0 356 void
michael@0 357 AsmJSPerfSpewer::noteBlocksOffsets()
michael@0 358 {
michael@0 359 if (!PerfBlockEnabled())
michael@0 360 return;
michael@0 361
michael@0 362 for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
michael@0 363 Record &r = basicBlocks_[i];
michael@0 364 r.startOffset = r.start.offset();
michael@0 365 r.endOffset = r.end.offset();
michael@0 366 }
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 js::jit::writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset,
michael@0 371 size_t funcEndInlineOffset, size_t funcSize,
michael@0 372 const char *filename, const char *funcName,
michael@0 373 const js::jit::BasicBlocksVector &basicBlocks)
michael@0 374 {
michael@0 375 if (!PerfBlockEnabled() || basicBlocks.empty())
michael@0 376 return;
michael@0 377
michael@0 378 if (!lockPerfMap())
michael@0 379 return;
michael@0 380
michael@0 381 // function begins with the prologue, which is located before the first basic block
michael@0 382 size_t prologueSize = basicBlocks[0].startOffset - funcStartOffset;
michael@0 383 size_t cur = baseAddress + funcStartOffset + prologueSize;
michael@0 384 size_t funcEndInlineCode = baseAddress + funcEndInlineOffset;
michael@0 385 size_t funcEnd = baseAddress + funcStartOffset + funcSize;
michael@0 386
michael@0 387 if (prologueSize > 0) {
michael@0 388 fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n",
michael@0 389 baseAddress + funcStartOffset, prologueSize, filename, funcName);
michael@0 390 }
michael@0 391
michael@0 392 for (uint32_t i = 0; i < basicBlocks.length(); i++) {
michael@0 393 const Record &r = basicBlocks[i];
michael@0 394
michael@0 395 size_t blockStart = baseAddress + r.startOffset;
michael@0 396 size_t blockEnd = baseAddress + r.endOffset;
michael@0 397
michael@0 398 JS_ASSERT(cur <= blockStart);
michael@0 399 if (cur < blockStart) {
michael@0 400 fprintf(PerfFilePtr, "%zx %zx %s: Function %s - unknown block\n",
michael@0 401 cur, blockStart - cur,
michael@0 402 filename,
michael@0 403 funcName);
michael@0 404 }
michael@0 405 cur = blockEnd;
michael@0 406
michael@0 407 size_t size = blockEnd - blockStart;
michael@0 408 if (size > 0) {
michael@0 409 fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s - Block %d\n",
michael@0 410 blockStart, size,
michael@0 411 filename, r.lineNumber, r.columnNumber,
michael@0 412 funcName, r.id);
michael@0 413 }
michael@0 414 }
michael@0 415
michael@0 416 JS_ASSERT(cur <= funcEndInlineCode);
michael@0 417 if (cur < funcEndInlineCode)
michael@0 418 fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Epilogue\n",
michael@0 419 cur, funcEndInlineCode - cur, filename, funcName);
michael@0 420
michael@0 421 JS_ASSERT(funcEndInlineCode <= funcEnd);
michael@0 422 if (funcEndInlineCode < funcEnd) {
michael@0 423 fprintf(PerfFilePtr, "%zx %zx %s: Function %s - OOL\n",
michael@0 424 funcEndInlineCode, funcEnd - funcEndInlineCode, filename, funcName);
michael@0 425 }
michael@0 426
michael@0 427 unlockPerfMap();
michael@0 428 }
michael@0 429
michael@0 430 void
michael@0 431 js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size)
michael@0 432 {
michael@0 433 if (size == 0)
michael@0 434 return;
michael@0 435
michael@0 436 if (!lockPerfMap())
michael@0 437 return;
michael@0 438
michael@0 439 fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size);
michael@0 440
michael@0 441 unlockPerfMap();
michael@0 442 }
michael@0 443
michael@0 444 #endif // defined (JS_ION_PERF)

mercurial