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 #include "mozilla/IntegerPrintfMacros.h"
10 #include "mozilla/SizePrintfMacros.h"
11
12 #ifdef XP_UNIX
13 # include <unistd.h>
14 #endif
15
16 #ifdef JS_ION_PERF
17 # include "jit/JitSpewer.h"
18 # include "jit/LIR.h"
19 # include "jit/MIR.h"
20 # include "jit/MIRGraph.h"
21 #endif
22
23 #include "vm/MutexIDs.h"
24
25 // perf expects its data to be in a file /tmp/perf-PID.map, but for Android
26 // and B2G the map files are written to /data/local/tmp/perf-PID.map
27 //
28 // Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/
29 // so also try /sdcard/.
30
31 #ifndef PERF_SPEW_DIR
32 # if defined(__ANDROID__)
33 # define PERF_SPEW_DIR "/data/local/tmp/"
34 # define PERF_SPEW_DIR_2 "/sdcard/"
35 # else
36 # define PERF_SPEW_DIR "/tmp/"
37 # endif
38 #endif
39
40 using namespace js;
41 using namespace js::jit;
42
43 #define PERF_MODE_NONE 1
44 #define PERF_MODE_FUNC 2
45 #define PERF_MODE_BLOCK 3
46
47 #ifdef JS_ION_PERF
48
49 static uint32_t PerfMode = 0;
50
51 static bool PerfChecked = false;
52
53 static FILE* PerfFilePtr = nullptr;
54
55 static js::Mutex* PerfMutex;
56
57 static bool
openPerfMap(const char * dir)58 openPerfMap(const char* dir)
59 {
60 const ssize_t bufferSize = 256;
61 char filenameBuffer[bufferSize];
62
63 if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize)
64 return false;
65
66 MOZ_ASSERT(!PerfFilePtr);
67 PerfFilePtr = fopen(filenameBuffer, "a");
68
69 if (!PerfFilePtr)
70 return false;
71
72 return true;
73 }
74
75 void
CheckPerf()76 js::jit::CheckPerf() {
77 if (!PerfChecked) {
78 const char* env = getenv("IONPERF");
79 if (env == nullptr) {
80 PerfMode = PERF_MODE_NONE;
81 fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". ");
82 fprintf(stderr, "Perf mapping will be deactivated.\n");
83 } else if (!strcmp(env, "none")) {
84 PerfMode = PERF_MODE_NONE;
85 } else if (!strcmp(env, "block")) {
86 PerfMode = PERF_MODE_BLOCK;
87 } else if (!strcmp(env, "func")) {
88 PerfMode = PERF_MODE_FUNC;
89 } else {
90 fprintf(stderr, "Use IONPERF=func to record at function granularity\n");
91 fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n");
92 fprintf(stderr, "\n");
93 fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n");
94 fprintf(stderr, "to be leaked.\n");
95 exit(0);
96 }
97
98 if (PerfMode != PERF_MODE_NONE) {
99 PerfMutex = js_new<js::Mutex>(mutexid::PerfSpewer);
100 if (!PerfMutex)
101 MOZ_CRASH("failed to allocate PerfMutex");
102
103 if (openPerfMap(PERF_SPEW_DIR)) {
104 PerfChecked = true;
105 return;
106 }
107
108 #if defined(__ANDROID__)
109 if (openPerfMap(PERF_SPEW_DIR_2)) {
110 PerfChecked = true;
111 return;
112 }
113 #endif
114 fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n");
115 PerfMode = PERF_MODE_NONE;
116 }
117 PerfChecked = true;
118 }
119 }
120
121 bool
PerfBlockEnabled()122 js::jit::PerfBlockEnabled() {
123 MOZ_ASSERT(PerfMode);
124 return PerfMode == PERF_MODE_BLOCK;
125 }
126
127 bool
PerfFuncEnabled()128 js::jit::PerfFuncEnabled() {
129 MOZ_ASSERT(PerfMode);
130 return PerfMode == PERF_MODE_FUNC;
131 }
132
133 static bool
lockPerfMap(void)134 lockPerfMap(void)
135 {
136 if (!PerfEnabled())
137 return false;
138
139 PerfMutex->lock();
140
141 MOZ_ASSERT(PerfFilePtr);
142 return true;
143 }
144
145 static void
unlockPerfMap()146 unlockPerfMap()
147 {
148 MOZ_ASSERT(PerfFilePtr);
149 fflush(PerfFilePtr);
150 PerfMutex->unlock();
151 }
152
153 uint32_t PerfSpewer::nextFunctionIndex = 0;
154
155 bool
startBasicBlock(MBasicBlock * blk,MacroAssembler & masm)156 PerfSpewer::startBasicBlock(MBasicBlock* blk,
157 MacroAssembler& masm)
158 {
159 if (!PerfBlockEnabled())
160 return true;
161
162 const char* filename = blk->info().script()->filename();
163 unsigned lineNumber, columnNumber;
164 if (blk->pc()) {
165 lineNumber = PCToLineNumber(blk->info().script(),
166 blk->pc(),
167 &columnNumber);
168 } else {
169 lineNumber = 0;
170 columnNumber = 0;
171 }
172 Record r(filename, lineNumber, columnNumber, blk->id());
173 masm.bind(&r.start);
174 return basicBlocks_.append(r);
175 }
176
177 bool
endBasicBlock(MacroAssembler & masm)178 PerfSpewer::endBasicBlock(MacroAssembler& masm)
179 {
180 if (!PerfBlockEnabled())
181 return true;
182
183 masm.bind(&basicBlocks_.back().end);
184 return true;
185 }
186
187 bool
noteEndInlineCode(MacroAssembler & masm)188 PerfSpewer::noteEndInlineCode(MacroAssembler& masm)
189 {
190 if (!PerfBlockEnabled())
191 return true;
192
193 masm.bind(&endInlineCode);
194 return true;
195 }
196
197 void
writeProfile(JSScript * script,JitCode * code,MacroAssembler & masm)198 PerfSpewer::writeProfile(JSScript* script,
199 JitCode* code,
200 MacroAssembler& masm)
201 {
202 if (PerfFuncEnabled()) {
203 if (!lockPerfMap())
204 return;
205
206 uint32_t thisFunctionIndex = nextFunctionIndex++;
207
208 size_t size = code->instructionsSize();
209 if (size > 0) {
210 fprintf(PerfFilePtr, "%p %" PRIxSIZE " %s:%" PRIuSIZE ": Func%02d\n",
211 code->raw(),
212 size,
213 script->filename(),
214 script->lineno(),
215 thisFunctionIndex);
216 }
217 unlockPerfMap();
218 return;
219 }
220
221 if (PerfBlockEnabled() && basicBlocks_.length() > 0) {
222 if (!lockPerfMap())
223 return;
224
225 uint32_t thisFunctionIndex = nextFunctionIndex++;
226 uintptr_t funcStart = uintptr_t(code->raw());
227 uintptr_t funcEndInlineCode = funcStart + endInlineCode.offset();
228 uintptr_t funcEnd = funcStart + code->instructionsSize();
229
230 // function begins with the prologue, which is located before the first basic block
231 size_t prologueSize = basicBlocks_[0].start.offset();
232
233 if (prologueSize > 0) {
234 fprintf(PerfFilePtr, "%" PRIxSIZE " %" PRIxSIZE " %s:%" PRIuSIZE ": Func%02d-Prologue\n",
235 funcStart, prologueSize, script->filename(), script->lineno(), thisFunctionIndex);
236 }
237
238 uintptr_t cur = funcStart + prologueSize;
239 for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
240 Record& r = basicBlocks_[i];
241
242 uintptr_t blockStart = funcStart + r.start.offset();
243 uintptr_t blockEnd = funcStart + r.end.offset();
244
245 MOZ_ASSERT(cur <= blockStart);
246 if (cur < blockStart) {
247 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-Block?\n",
248 cur, blockStart - cur,
249 script->filename(), script->lineno(),
250 thisFunctionIndex);
251 }
252 cur = blockEnd;
253
254 size_t size = blockEnd - blockStart;
255
256 if (size > 0) {
257 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s:%d:%d: Func%02d-Block%d\n",
258 blockStart, size,
259 r.filename, r.lineNumber, r.columnNumber,
260 thisFunctionIndex, r.id);
261 }
262 }
263
264 MOZ_ASSERT(cur <= funcEndInlineCode);
265 if (cur < funcEndInlineCode) {
266 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-Epilogue\n",
267 cur, funcEndInlineCode - cur,
268 script->filename(), script->lineno(),
269 thisFunctionIndex);
270 }
271
272 MOZ_ASSERT(funcEndInlineCode <= funcEnd);
273 if (funcEndInlineCode < funcEnd) {
274 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%" PRIuSIZE ": Func%02d-OOL\n",
275 funcEndInlineCode, funcEnd - funcEndInlineCode,
276 script->filename(), script->lineno(),
277 thisFunctionIndex);
278 }
279
280 unlockPerfMap();
281 return;
282 }
283 }
284
285 void
writePerfSpewerBaselineProfile(JSScript * script,JitCode * code)286 js::jit::writePerfSpewerBaselineProfile(JSScript* script, JitCode* code)
287 {
288 if (!PerfEnabled())
289 return;
290
291 if (!lockPerfMap())
292 return;
293
294 size_t size = code->instructionsSize();
295 if (size > 0) {
296 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s:%" PRIuSIZE ": Baseline\n",
297 reinterpret_cast<uintptr_t>(code->raw()),
298 size, script->filename(), script->lineno());
299 }
300
301 unlockPerfMap();
302 }
303
304 void
writePerfSpewerJitCodeProfile(JitCode * code,const char * msg)305 js::jit::writePerfSpewerJitCodeProfile(JitCode* code, const char* msg)
306 {
307 if (!code || !PerfEnabled())
308 return;
309
310 if (!lockPerfMap())
311 return;
312
313 size_t size = code->instructionsSize();
314 if (size > 0) {
315 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " %s (%p 0x%" PRIxSIZE ")\n",
316 reinterpret_cast<uintptr_t>(code->raw()),
317 size, msg, code->raw(), size);
318 }
319
320 unlockPerfMap();
321 }
322
323 void
writePerfSpewerWasmFunctionMap(uintptr_t base,uintptr_t size,const char * filename,unsigned lineno,unsigned colIndex,const char * funcName)324 js::jit::writePerfSpewerWasmFunctionMap(uintptr_t base, uintptr_t size,
325 const char* filename, unsigned lineno, unsigned colIndex,
326 const char* funcName)
327 {
328 if (!PerfFuncEnabled() || size == 0U)
329 return;
330
331 if (!lockPerfMap())
332 return;
333
334 fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%u:%u: Function %s\n",
335 base, size, filename, lineno, colIndex, funcName);
336
337 unlockPerfMap();
338 }
339
340 #endif // defined (JS_ION_PERF)
341