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