js/src/jit/PerfSpewer.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:2cb01dfeb903
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/. */
6
7 #include "jit/PerfSpewer.h"
8
9 #if defined(__linux__)
10 # include <unistd.h>
11 #endif
12
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
20
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/.
26
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
35
36 using namespace js;
37 using namespace js::jit;
38
39 #define PERF_MODE_NONE 1
40 #define PERF_MODE_FUNC 2
41 #define PERF_MODE_BLOCK 3
42
43 #ifdef JS_ION_PERF
44
45 static uint32_t PerfMode = 0;
46
47 static bool PerfChecked = false;
48
49 static FILE *PerfFilePtr = nullptr;
50
51 #ifdef JS_THREADSAFE
52 # include "jslock.h"
53 static PRLock *PerfMutex;
54 #endif
55
56 static bool
57 openPerfMap(const char *dir)
58 {
59 const ssize_t bufferSize = 256;
60 char filenameBuffer[bufferSize];
61
62 if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize)
63 return false;
64
65 JS_ASSERT(!PerfFilePtr);
66 PerfFilePtr = fopen(filenameBuffer, "a");
67
68 if (!PerfFilePtr)
69 return false;
70
71 return true;
72 }
73
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 }
96
97 if (PerfMode != PERF_MODE_NONE) {
98 #ifdef JS_THREADSAFE
99 PerfMutex = PR_NewLock();
100 if (!PerfMutex)
101 MOZ_CRASH();
102 #endif
103
104 if (openPerfMap(PERF_SPEW_DIR)) {
105 PerfChecked = true;
106 return;
107 }
108
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 }
121
122 bool
123 js::jit::PerfBlockEnabled() {
124 JS_ASSERT(PerfMode);
125 return PerfMode == PERF_MODE_BLOCK;
126 }
127
128 bool
129 js::jit::PerfFuncEnabled() {
130 JS_ASSERT(PerfMode);
131 return PerfMode == PERF_MODE_FUNC;
132 }
133
134 static bool
135 lockPerfMap(void)
136 {
137 if (!PerfEnabled())
138 return false;
139
140 #ifdef JS_THREADSAFE
141 PR_Lock(PerfMutex);
142 #endif
143
144 JS_ASSERT(PerfFilePtr);
145 return true;
146 }
147
148 static void
149 unlockPerfMap()
150 {
151 JS_ASSERT(PerfFilePtr);
152 fflush(PerfFilePtr);
153 #ifdef JS_THREADSAFE
154 PR_Unlock(PerfMutex);
155 #endif
156 }
157
158 uint32_t PerfSpewer::nextFunctionIndex = 0;
159
160 bool
161 PerfSpewer::startBasicBlock(MBasicBlock *blk,
162 MacroAssembler &masm)
163 {
164 if (!PerfBlockEnabled())
165 return true;
166
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 }
181
182 bool
183 PerfSpewer::endBasicBlock(MacroAssembler &masm)
184 {
185 if (!PerfBlockEnabled())
186 return true;
187
188 masm.bind(&basicBlocks_.back().end);
189 return true;
190 }
191
192 bool
193 PerfSpewer::noteEndInlineCode(MacroAssembler &masm)
194 {
195 if (!PerfBlockEnabled())
196 return true;
197
198 masm.bind(&endInlineCode);
199 return true;
200 }
201
202 void
203 PerfSpewer::writeProfile(JSScript *script,
204 JitCode *code,
205 MacroAssembler &masm)
206 {
207 if (PerfFuncEnabled()) {
208 if (!lockPerfMap())
209 return;
210
211 uint32_t thisFunctionIndex = nextFunctionIndex++;
212
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 }
225
226 if (PerfBlockEnabled() && basicBlocks_.length() > 0) {
227 if (!lockPerfMap())
228 return;
229
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();
234
235 // function begins with the prologue, which is located before the first basic block
236 size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset());
237
238 if (prologueSize > 0) {
239 fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n",
240 funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex);
241 }
242
243 uintptr_t cur = funcStart + prologueSize;
244 for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
245 Record &r = basicBlocks_[i];
246
247 uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset());
248 uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset());
249
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;
259
260 size_t size = blockEnd - blockStart;
261
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 }
269
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 }
277
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 }
285
286 unlockPerfMap();
287 return;
288 }
289 }
290
291 void
292 js::jit::writePerfSpewerBaselineProfile(JSScript *script, JitCode *code)
293 {
294 if (!PerfEnabled())
295 return;
296
297 if (!lockPerfMap())
298 return;
299
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 }
306
307 unlockPerfMap();
308 }
309
310 void
311 js::jit::writePerfSpewerJitCodeProfile(JitCode *code, const char *msg)
312 {
313 if (!code || !PerfEnabled())
314 return;
315
316 if (!lockPerfMap())
317 return;
318
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 }
325
326 unlockPerfMap();
327 }
328
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;
336
337 if (!lockPerfMap())
338 return;
339
340 fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName);
341
342 unlockPerfMap();
343 }
344
345 bool
346 AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm)
347 {
348 if (!PerfBlockEnabled())
349 return true;
350
351 Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later
352 masm.bind(&r.start);
353 return basicBlocks_.append(r);
354 }
355
356 void
357 AsmJSPerfSpewer::noteBlocksOffsets()
358 {
359 if (!PerfBlockEnabled())
360 return;
361
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 }
368
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;
377
378 if (!lockPerfMap())
379 return;
380
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;
386
387 if (prologueSize > 0) {
388 fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n",
389 baseAddress + funcStartOffset, prologueSize, filename, funcName);
390 }
391
392 for (uint32_t i = 0; i < basicBlocks.length(); i++) {
393 const Record &r = basicBlocks[i];
394
395 size_t blockStart = baseAddress + r.startOffset;
396 size_t blockEnd = baseAddress + r.endOffset;
397
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;
406
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 }
415
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);
420
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 }
426
427 unlockPerfMap();
428 }
429
430 void
431 js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size)
432 {
433 if (size == 0)
434 return;
435
436 if (!lockPerfMap())
437 return;
438
439 fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size);
440
441 unlockPerfMap();
442 }
443
444 #endif // defined (JS_ION_PERF)

mercurial