|
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) |