Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / jit / PerfSpewer.cpp
blobc9d9cc8d883be6a3d210f9379d90043ca4a63a47
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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 "mozilla/IntegerPrintfMacros.h"
8 #include "mozilla/Printf.h"
10 #if defined(JS_ION_PERF) && defined(XP_UNIX)
11 # include <fcntl.h>
12 # include <sys/mman.h>
13 # include <sys/stat.h>
14 # include <unistd.h>
15 #endif
17 #if defined(JS_ION_PERF) && defined(XP_LINUX) && !defined(ANDROID) && \
18 defined(__GLIBC__)
19 # include <dlfcn.h>
20 # include <sys/syscall.h>
21 # include <sys/types.h>
22 # include <unistd.h>
23 # define gettid() static_cast<pid_t>(syscall(__NR_gettid))
24 #endif
26 #if defined(JS_ION_PERF) && (defined(ANDROID) || defined(XP_DARWIN))
27 # include <limits.h>
28 # include <stdlib.h>
29 # include <unistd.h>
30 char* get_current_dir_name() {
31 char* buffer = (char*)malloc(PATH_MAX * sizeof(char));
32 if (buffer == nullptr) {
33 return nullptr;
36 if (getcwd(buffer, PATH_MAX) == nullptr) {
37 free(buffer);
38 return nullptr;
41 return buffer;
43 #endif
45 #if defined(JS_ION_PERF) && defined(XP_DARWIN)
46 # include <pthread.h>
47 # include <unistd.h>
49 pid_t gettid_pthread() {
50 uint64_t tid;
51 if (pthread_threadid_np(nullptr, &tid) != 0) {
52 return 0;
54 // Truncate the tid to 32 bits. macOS thread IDs are usually small enough.
55 // And even if we do end up truncating, it doesn't matter much for Jitdump
56 // as long as the process ID is correct.
57 return pid_t(tid);
59 # define gettid() gettid_pthread()
60 #endif
62 #include "jit/PerfSpewer.h"
64 #include <atomic>
66 #include "jit/Jitdump.h"
67 #include "jit/JitSpewer.h"
68 #include "jit/LIR.h"
69 #include "jit/MIR.h"
70 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOffset
71 #include "js/JitCodeAPI.h"
72 #include "js/Printf.h"
73 #include "vm/BytecodeUtil.h"
74 #include "vm/MutexIDs.h"
76 #ifdef XP_WIN
77 # include <windef.h>
78 # include <codecvt>
79 # include <evntprov.h>
80 # include <locale>
81 # include <string>
82 # include <windows.h>
84 const GUID PROVIDER_JSCRIPT9 = {
85 0x57277741,
86 0x3638,
87 0x4a4b,
88 {0xbd, 0xba, 0x0a, 0xc6, 0xe4, 0x5d, 0xa5, 0x6c}};
89 const EVENT_DESCRIPTOR MethodLoad = {0x9, 0x0, 0x0, 0x4, 0xa, 0x1, 0x1};
91 static REGHANDLE sETWRegistrationHandle = NULL;
93 static std::atomic<bool> etwCollection = false;
94 #endif
96 using namespace js;
97 using namespace js::jit;
99 enum class PerfModeType { None, Function, Source, IR, IROperands };
101 static std::atomic<bool> geckoProfiling = false;
102 static std::atomic<PerfModeType> PerfMode = PerfModeType::None;
104 // Mutex to guard access to the profiler vectors and jitdump file if perf
105 // profiling is enabled.
106 static js::Mutex PerfMutex(mutexid::PerfSpewer);
108 static PersistentRooted<GCVector<JitCode*, 0, js::SystemAllocPolicy>>
109 jitCodeVector;
110 static ProfilerJitCodeVector profilerData;
112 static bool IsGeckoProfiling() { return geckoProfiling; }
113 #ifdef JS_ION_PERF
114 static UniqueChars spew_dir;
115 static FILE* JitDumpFilePtr = nullptr;
116 static void* mmap_address = nullptr;
117 static bool IsPerfProfiling() { return JitDumpFilePtr != nullptr; }
118 #endif
120 AutoLockPerfSpewer::AutoLockPerfSpewer() { PerfMutex.lock(); }
122 AutoLockPerfSpewer::~AutoLockPerfSpewer() { PerfMutex.unlock(); }
124 #ifdef JS_ION_PERF
125 static uint64_t GetMonotonicTimestamp() {
126 using mozilla::TimeStamp;
127 # ifdef XP_LINUX
128 return TimeStamp::Now().RawClockMonotonicNanosecondsSinceBoot();
129 # elif XP_WIN
130 return TimeStamp::Now().RawQueryPerformanceCounterValue().value();
131 # elif XP_DARWIN
132 return TimeStamp::Now().RawMachAbsoluteTimeNanoseconds();
133 # else
134 MOZ_CRASH("no timestamp");
135 # endif
138 // values are from /usr/include/elf.h
139 static uint32_t GetMachineEncoding() {
140 # if defined(JS_CODEGEN_X86)
141 return 3; // EM_386
142 # elif defined(JS_CODEGEN_X64)
143 return 62; // EM_X86_64
144 # elif defined(JS_CODEGEN_ARM)
145 return 40; // EM_ARM
146 # elif defined(JS_CODEGEN_ARM64)
147 return 183; // EM_AARCH64
148 # elif defined(JS_CODEGEN_MIPS32)
149 return 8; // EM_MIPS
150 # elif defined(JS_CODEGEN_MIPS64)
151 return 8; // EM_MIPS
152 # else
153 return 0; // Unsupported
154 # endif
157 static void WriteToJitDumpFile(const void* addr, uint32_t size,
158 AutoLockPerfSpewer& lock) {
159 MOZ_RELEASE_ASSERT(JitDumpFilePtr);
160 size_t rv = fwrite(addr, 1, size, JitDumpFilePtr);
161 MOZ_RELEASE_ASSERT(rv == size);
164 static void WriteJitDumpDebugEntry(uint64_t addr, const char* filename,
165 uint32_t lineno,
166 JS::LimitedColumnNumberOneOrigin colno,
167 AutoLockPerfSpewer& lock) {
168 JitDumpDebugEntry entry = {addr, lineno, colno.oneOriginValue()};
169 WriteToJitDumpFile(&entry, sizeof(entry), lock);
170 WriteToJitDumpFile(filename, strlen(filename) + 1, lock);
173 static void writeJitDumpHeader(AutoLockPerfSpewer& lock) {
174 JitDumpHeader header = {};
175 header.magic = 0x4A695444;
176 header.version = 1;
177 header.total_size = sizeof(header);
178 header.elf_mach = GetMachineEncoding();
179 header.pad1 = 0;
180 header.pid = getpid();
181 header.timestamp = GetMonotonicTimestamp();
182 header.flags = 0;
184 WriteToJitDumpFile(&header, sizeof(header), lock);
187 static bool openJitDump() {
188 if (JitDumpFilePtr) {
189 return true;
191 AutoLockPerfSpewer lock;
193 const ssize_t bufferSize = 256;
194 char filenameBuffer[bufferSize];
196 // We want to write absolute filenames into the debug info or else we get the
197 // filenames in the perf report
198 if (getenv("PERF_SPEW_DIR")) {
199 char* env_dir = getenv("PERF_SPEW_DIR");
200 if (env_dir[0] == '/') {
201 spew_dir = JS_smprintf("%s", env_dir);
202 } else {
203 const char* dir = get_current_dir_name();
204 if (!dir) {
205 fprintf(stderr, "couldn't get current dir name\n");
206 return false;
208 spew_dir = JS_smprintf("%s/%s", dir, env_dir);
209 free((void*)dir);
211 } else {
212 fprintf(stderr, "Please define PERF_SPEW_DIR as an output directory.\n");
213 return false;
216 if (snprintf(filenameBuffer, bufferSize, "%s/jit-%d.dump", spew_dir.get(),
217 getpid()) >= bufferSize) {
218 return false;
221 MOZ_ASSERT(!JitDumpFilePtr);
223 int fd = open(filenameBuffer, O_CREAT | O_TRUNC | O_RDWR, 0666);
224 JitDumpFilePtr = fdopen(fd, "w+");
226 if (!JitDumpFilePtr) {
227 return false;
230 # ifdef XP_LINUX
231 // We need to mmap the jitdump file for perf to find it.
232 long page_size = sysconf(_SC_PAGESIZE);
233 int prot = PROT_READ | PROT_EXEC;
234 // The mmap call fails on some Android devices if PROT_EXEC is specified, and
235 // it does not appear to be required by simpleperf, so omit it on Android.
236 # ifdef ANDROID
237 prot &= ~PROT_EXEC;
238 # endif
239 mmap_address = mmap(nullptr, page_size, prot, MAP_PRIVATE, fd, 0);
240 if (mmap_address == MAP_FAILED) {
241 PerfMode = PerfModeType::None;
242 return false;
244 # endif
246 writeJitDumpHeader(lock);
247 return true;
250 static void CheckPerf() {
251 static bool PerfChecked = false;
253 if (!PerfChecked) {
254 const char* env = getenv("IONPERF");
255 if (env == nullptr) {
256 PerfMode = PerfModeType::None;
257 fprintf(stderr,
258 "Warning: JIT perf reporting requires IONPERF set to \"func\" "
259 ", \"src\" or \"ir\". ");
260 fprintf(stderr, "Perf mapping will be deactivated.\n");
261 } else if (!strcmp(env, "src")) {
262 PerfMode = PerfModeType::Source;
263 } else if (!strcmp(env, "ir")) {
264 PerfMode = PerfModeType::IR;
265 } else if (!strcmp(env, "ir-ops")) {
266 # ifdef JS_JITSPEW
267 PerfMode = PerfModeType::IROperands;
268 # else
269 fprintf(stderr,
270 "Warning: IONPERF=ir-ops requires --enable-jitspew to be "
271 "enabled, defaulting to IONPERF=ir\n");
272 PerfMode = PerfModeType::IR;
273 # endif
274 } else if (!strcmp(env, "func")) {
275 PerfMode = PerfModeType::Function;
276 } else {
277 fprintf(stderr, "Use IONPERF=func to record at function granularity\n");
278 fprintf(stderr,
279 "Use IONPERF=ir to record and annotate assembly with IR\n");
280 fprintf(stderr,
281 "Use IONPERF=src to record and annotate assembly with source, if "
282 "available locally\n");
283 exit(0);
286 if (PerfMode != PerfModeType::None) {
287 if (openJitDump()) {
288 PerfChecked = true;
289 return;
292 fprintf(stderr, "Failed to open perf map file. Disabling IONPERF.\n");
293 PerfMode = PerfModeType::None;
295 PerfChecked = true;
298 #endif
300 #ifdef XP_WIN
301 void NTAPI ETWEnableCallback(LPCGUID aSourceId, ULONG aIsEnabled, UCHAR aLevel,
302 ULONGLONG aMatchAnyKeyword,
303 ULONGLONG aMatchAllKeyword,
304 PEVENT_FILTER_DESCRIPTOR aFilterData,
305 PVOID aCallbackContext) {
306 // This is called on a CRT worker thread. This means this might race with
307 // our main thread, but that is okay.
308 etwCollection = aIsEnabled;
309 PerfMode = aIsEnabled ? PerfModeType::Function : PerfModeType::None;
312 void RegisterETW() {
313 static bool sHasRegisteredETW = false;
314 if (!sHasRegisteredETW) {
315 if (getenv("ETW_ENABLED")) {
316 EventRegister(&PROVIDER_JSCRIPT9, // GUID that identifies the provider
317 ETWEnableCallback, // Callback for enabling collection
318 NULL, // Context not used
319 &sETWRegistrationHandle // Used when calling EventWrite
320 // and EventUnregister
323 sHasRegisteredETW = true;
326 #endif
328 /* static */
329 void PerfSpewer::Init() {
330 #ifdef JS_ION_PERF
331 CheckPerf();
332 #endif
333 #ifdef XP_WIN
334 RegisterETW();
335 #endif
338 static void DisablePerfSpewer(AutoLockPerfSpewer& lock) {
339 fprintf(stderr, "Warning: Disabling PerfSpewer.");
341 geckoProfiling = false;
342 #ifdef XP_WIN
343 etwCollection = false;
344 #endif
345 PerfMode = PerfModeType::None;
346 #ifdef JS_ION_PERF
347 long page_size = sysconf(_SC_PAGESIZE);
348 munmap(mmap_address, page_size);
349 fclose(JitDumpFilePtr);
350 JitDumpFilePtr = nullptr;
351 #endif
354 void js::jit::ResetPerfSpewer(bool enabled) {
355 AutoLockPerfSpewer lock;
357 profilerData.clear();
358 jitCodeVector.clear();
359 geckoProfiling = enabled;
362 static JS::JitCodeRecord* CreateProfilerEntry(AutoLockPerfSpewer& lock) {
363 if (!IsGeckoProfiling()) {
364 return nullptr;
367 if (!profilerData.growBy(1)) {
368 DisablePerfSpewer(lock);
369 return nullptr;
371 return &profilerData.back();
374 static JS::JitCodeIRInfo* CreateProfilerIREntry(JS::JitCodeRecord* record,
375 AutoLockPerfSpewer& lock) {
376 if (!record) {
377 return nullptr;
380 if (!record->irInfo.growBy(1)) {
381 DisablePerfSpewer(lock);
382 return nullptr;
384 return &record->irInfo.back();
387 static JS::JitCodeSourceInfo* CreateProfilerSourceEntry(
388 JS::JitCodeRecord* record, AutoLockPerfSpewer& lock) {
389 if (!record) {
390 return nullptr;
393 if (!record->sourceInfo.growBy(1)) {
394 DisablePerfSpewer(lock);
395 return nullptr;
397 return &record->sourceInfo.back();
400 JS::JitOpcodeDictionary::JitOpcodeDictionary() {
401 MOZ_ASSERT(JS_IsInitialized());
403 #define COPY_JSOP_OPCODE(name, ...) \
404 if (!baselineDictionary.append(JS_smprintf(#name))) { \
405 return; \
407 FOR_EACH_OPCODE(COPY_JSOP_OPCODE)
408 #undef COPY_JSOP_OPCODE
410 #define COPY_LIR_OPCODE(name, ...) \
411 if (!ionDictionary.append(JS_smprintf(#name))) { \
412 return; \
414 LIR_OPCODE_LIST(COPY_LIR_OPCODE)
415 #undef COPY_LIR_OPCODE
417 #define COPY_CACHEIR_OPCODE(name, ...) \
418 if (!icDictionary.append(JS_smprintf(#name))) { \
419 return; \
421 CACHE_IR_OPS(COPY_CACHEIR_OPCODE)
422 #undef COPY_CACHEIR_OPCODE
425 // API to access JitCode data for the Gecko Profiler.
426 void JS::JitCodeIterator::getDataForIndex(size_t IteratorIndex) {
427 if (IteratorIndex >= profilerData.length()) {
428 data = nullptr;
429 } else {
430 data = &profilerData[IteratorIndex];
434 JS::JitCodeIterator::JitCodeIterator() : iteratorIndex(0) {
435 MOZ_ASSERT(JS_IsInitialized());
436 PerfMutex.lock();
437 getDataForIndex(0);
440 JS::JitCodeIterator::~JitCodeIterator() { PerfMutex.unlock(); }
442 static bool PerfSrcEnabled() {
443 return PerfMode == PerfModeType::Source || geckoProfiling;
446 #ifdef JS_JITSPEW
447 static bool PerfIROpsEnabled() { return PerfMode == PerfModeType::IROperands; }
448 #endif
450 static bool PerfIREnabled() {
451 return (PerfMode == PerfModeType::IROperands) ||
452 (PerfMode == PerfModeType::IR) || geckoProfiling;
455 static bool PerfFuncEnabled() {
456 return PerfMode == PerfModeType::Function || geckoProfiling;
459 bool js::jit::PerfEnabled() {
460 return PerfSrcEnabled() || PerfIREnabled() || PerfFuncEnabled();
463 void InlineCachePerfSpewer::recordInstruction(MacroAssembler& masm,
464 CacheOp op) {
465 if (!PerfIREnabled()) {
466 return;
468 AutoLockPerfSpewer lock;
470 if (!opcodes_.emplaceBack(masm.currentOffset(), static_cast<unsigned>(op))) {
471 opcodes_.clear();
472 DisablePerfSpewer(lock);
476 #define CHECK_RETURN(x) \
477 if (!(x)) { \
478 AutoLockPerfSpewer lock; \
479 DisablePerfSpewer(lock); \
480 return; \
483 void IonPerfSpewer::recordInstruction(MacroAssembler& masm, LInstruction* ins) {
484 if (!PerfIREnabled() && !PerfSrcEnabled()) {
485 return;
488 LNode::Opcode op = ins->op();
489 UniqueChars opcodeStr;
491 jsbytecode* bytecodepc = nullptr;
492 if (MDefinition* mir = ins->mirRaw()) {
493 bytecodepc = mir->trackedSite()->pc();
496 #ifdef JS_JITSPEW
497 if (PerfIROpsEnabled()) {
498 Sprinter buf;
499 CHECK_RETURN(buf.init());
500 buf.put(LIRCodeName(op));
501 ins->printOperands(buf);
502 opcodeStr = buf.release();
504 #endif
505 if (!opcodes_.emplaceBack(masm.currentOffset(), static_cast<unsigned>(op),
506 opcodeStr, bytecodepc)) {
507 opcodes_.clear();
508 AutoLockPerfSpewer lock;
509 DisablePerfSpewer(lock);
513 #ifdef JS_JITSPEW
514 static void PrintStackValue(JSContext* cx, StackValue* stackVal,
515 CompilerFrameInfo& frame, Sprinter& buf) {
516 switch (stackVal->kind()) {
517 /****** Constant ******/
518 case StackValue::Constant: {
519 js::Value constantVal = stackVal->constant();
520 if (constantVal.isInt32()) {
521 buf.printf("%d", constantVal.toInt32());
522 } else if (constantVal.isObjectOrNull()) {
523 buf.printf("obj:%p", constantVal.toObjectOrNull());
524 } else if (constantVal.isString()) {
525 buf.put("str:");
526 buf.putString(cx, constantVal.toString());
527 } else if (constantVal.isNumber()) {
528 buf.printf("num:%f", constantVal.toNumber());
529 } else if (constantVal.isSymbol()) {
530 buf.put("sym:");
531 constantVal.toSymbol()->dump(buf);
532 } else {
533 buf.printf("raw:%" PRIx64, constantVal.asRawBits());
535 } break;
536 /****** Register ******/
537 case StackValue::Register: {
538 Register reg = stackVal->reg().payloadOrValueReg();
539 buf.put(reg.name());
540 } break;
541 /****** Stack ******/
542 case StackValue::Stack:
543 buf.put("stack");
544 break;
545 /****** ThisSlot ******/
546 case StackValue::ThisSlot: {
547 # ifdef JS_HAS_HIDDEN_SP
548 buf.put("this");
549 # else
550 Address addr = frame.addressOfThis();
551 buf.printf("this:%s(%d)", addr.base.name(), addr.offset);
552 # endif
553 } break;
554 /****** LocalSlot ******/
555 case StackValue::LocalSlot:
556 buf.printf("local:%u", stackVal->localSlot());
557 break;
558 /****** ArgSlot ******/
559 case StackValue::ArgSlot:
560 buf.printf("arg:%u", stackVal->argSlot());
561 break;
563 default:
564 MOZ_CRASH("Unexpected kind");
565 break;
568 #endif
570 void BaselinePerfSpewer::recordInstruction(JSContext* cx, MacroAssembler& masm,
571 jsbytecode* pc,
572 CompilerFrameInfo& frame) {
573 if (!PerfIREnabled() && !PerfSrcEnabled()) {
574 return;
577 JSOp op = JSOp(*pc);
578 UniqueChars opcodeStr;
580 #ifdef JS_JITSPEW
581 if (PerfIROpsEnabled()) {
582 JSScript* script = frame.script;
583 unsigned numOperands = js::StackUses(op, pc);
585 Sprinter buf(cx);
586 CHECK_RETURN(buf.init());
587 buf.put(js::CodeName(op));
589 switch (op) {
590 case JSOp::SetName:
591 case JSOp::SetGName:
592 case JSOp::BindName:
593 case JSOp::BindGName:
594 case JSOp::GetName:
595 case JSOp::GetGName: {
596 // Emit the name used for these ops
597 Rooted<PropertyName*> name(cx, script->getName(pc));
598 buf.put(" ");
599 buf.putString(cx, name);
600 } break;
601 default:
602 break;
605 // Output should be "JSOp (operand1), (operand2), ..."
606 for (unsigned i = 1; i <= numOperands; i++) {
607 buf.put(" (");
608 StackValue* stackVal = frame.peek(-int(i));
609 PrintStackValue(cx, stackVal, frame, buf);
611 if (i < numOperands) {
612 buf.put("),");
613 } else {
614 buf.put(")");
617 opcodeStr = buf.release();
619 #endif
621 if (!opcodes_.emplaceBack(masm.currentOffset(), static_cast<unsigned>(op),
622 opcodeStr, pc)) {
623 opcodes_.clear();
624 AutoLockPerfSpewer lock;
625 DisablePerfSpewer(lock);
629 const char* BaselinePerfSpewer::CodeName(unsigned op) {
630 return js::CodeName(static_cast<JSOp>(op));
633 const char* BaselineInterpreterPerfSpewer::CodeName(unsigned op) {
634 return js::CodeName(static_cast<JSOp>(op));
637 const char* IonPerfSpewer::CodeName(unsigned op) {
638 return js::jit::LIRCodeName(static_cast<LNode::Opcode>(op));
640 const char* InlineCachePerfSpewer::CodeName(unsigned op) {
641 return js::jit::CacheIRCodeName(static_cast<CacheOp>(op));
644 void PerfSpewer::CollectJitCodeInfo(UniqueChars& function_name, JitCode* code,
645 JS::JitCodeRecord* profilerRecord,
646 AutoLockPerfSpewer& lock) {
647 // Hold the JitCode objects here so they are not GC'd during profiling.
648 if (IsGeckoProfiling()) {
649 if (!jitCodeVector.append(code)) {
650 DisablePerfSpewer(lock);
654 CollectJitCodeInfo(function_name, reinterpret_cast<void*>(code->raw()),
655 code->instructionsSize(), profilerRecord, lock);
658 void PerfSpewer::CollectJitCodeInfo(UniqueChars& function_name, void* code_addr,
659 uint64_t code_size,
660 JS::JitCodeRecord* profilerRecord,
661 AutoLockPerfSpewer& lock) {
662 #ifdef JS_ION_PERF
663 static uint64_t codeIndex = 1;
665 if (IsPerfProfiling()) {
666 JitDumpLoadRecord record = {};
668 record.header.id = JIT_CODE_LOAD;
669 record.header.total_size =
670 sizeof(record) + strlen(function_name.get()) + 1 + code_size;
671 record.header.timestamp = GetMonotonicTimestamp();
672 record.pid = getpid();
673 record.tid = gettid();
674 record.vma = uint64_t(code_addr);
675 record.code_addr = uint64_t(code_addr);
676 record.code_size = code_size;
677 record.code_index = codeIndex++;
679 WriteToJitDumpFile(&record, sizeof(record), lock);
680 WriteToJitDumpFile(function_name.get(), strlen(function_name.get()) + 1,
681 lock);
682 WriteToJitDumpFile(code_addr, code_size, lock);
684 #endif
685 #ifdef XP_WIN
686 if (etwCollection) {
687 void* scriptContextId = NULL;
688 uint32_t flags = 0;
689 uint64_t map = 0;
690 uint64_t assembly = 0;
691 uint32_t line_col = 0;
692 uint32_t method = 0;
694 int name_len = strlen(function_name.get());
695 std::wstring name(name_len + 1, '\0');
696 if (MultiByteToWideChar(CP_UTF8, 0, function_name.get(), name_len,
697 name.data(), name.size()) == 0) {
698 DisablePerfSpewer(lock);
699 return;
702 EVENT_DATA_DESCRIPTOR EventData[10];
704 EventDataDescCreate(&EventData[0], &scriptContextId, sizeof(PVOID));
705 EventDataDescCreate(&EventData[1], &code_addr, sizeof(PVOID));
706 EventDataDescCreate(&EventData[2], &code_size, sizeof(unsigned __int64));
707 EventDataDescCreate(&EventData[3], &method, sizeof(uint32_t));
708 EventDataDescCreate(&EventData[4], &flags, sizeof(const unsigned short));
709 EventDataDescCreate(&EventData[5], &map, sizeof(const unsigned short));
710 EventDataDescCreate(&EventData[6], &assembly, sizeof(unsigned __int64));
711 EventDataDescCreate(&EventData[7], &line_col, sizeof(const unsigned int));
712 EventDataDescCreate(&EventData[8], &line_col, sizeof(const unsigned int));
713 EventDataDescCreate(&EventData[9], name.c_str(),
714 sizeof(wchar_t) * (name.length() + 1));
716 ULONG result = EventWrite(
717 sETWRegistrationHandle, // From EventRegister
718 &MethodLoad, // EVENT_DESCRIPTOR generated from the manifest
719 (ULONG)10, // Size of the array of EVENT_DATA_DESCRIPTORs
720 EventData // Array of descriptors that contain the event data
723 if (result != ERROR_SUCCESS) {
724 DisablePerfSpewer(lock);
725 return;
728 #endif
730 if (IsGeckoProfiling()) {
731 profilerRecord->instructionSize = code_size;
732 profilerRecord->code_addr = uint64_t(code_addr);
733 profilerRecord->functionName = std::move(function_name);
737 void PerfSpewer::recordOffset(MacroAssembler& masm, const char* msg) {
738 if (!PerfIREnabled()) {
739 return;
742 UniqueChars offsetStr = DuplicateString(msg);
743 if (!opcodes_.emplaceBack(masm.currentOffset(), offsetStr)) {
744 opcodes_.clear();
745 AutoLockPerfSpewer lock;
746 DisablePerfSpewer(lock);
750 void PerfSpewer::saveJitCodeIRInfo(JitCode* code,
751 JS::JitCodeRecord* profilerRecord,
752 AutoLockPerfSpewer& lock) {
753 #ifdef JS_ION_PERF
754 static uint32_t filenameCounter = 0;
755 UniqueChars scriptFilename;
756 FILE* scriptFile = nullptr;
758 if (IsPerfProfiling()) {
759 scriptFilename = JS_smprintf("%s/jitdump-script-%u.%u.txt", spew_dir.get(),
760 filenameCounter++, getpid());
761 scriptFile = fopen(scriptFilename.get(), "w");
762 if (!scriptFile) {
763 DisablePerfSpewer(lock);
764 return;
767 JitDumpDebugRecord debug_record = {};
768 uint64_t n_records = opcodes_.length();
770 debug_record.header.id = JIT_CODE_DEBUG_INFO;
771 debug_record.header.total_size =
772 sizeof(debug_record) + n_records * (sizeof(JitDumpDebugEntry) +
773 strlen(scriptFilename.get()) + 1);
774 debug_record.header.timestamp = GetMonotonicTimestamp();
775 debug_record.code_addr = uint64_t(code->raw());
776 debug_record.nr_entry = n_records;
778 WriteToJitDumpFile(&debug_record, sizeof(debug_record), lock);
780 #endif
782 if (profilerRecord) {
783 profilerRecord->tier = GetTier();
786 for (size_t i = 0; i < opcodes_.length(); i++) {
787 OpcodeEntry& entry = opcodes_[i];
788 #ifdef JS_ION_PERF
789 if (IsPerfProfiling()) {
790 // If a string was recorded for this offset, use that instead.
791 if (entry.str) {
792 fprintf(scriptFile, "%s\n", entry.str.get());
793 } else {
794 fprintf(scriptFile, "%s\n", CodeName(entry.opcode));
796 uint64_t addr = uint64_t(code->raw()) + entry.offset;
797 uint64_t lineno = i + 1;
798 WriteJitDumpDebugEntry(addr, scriptFilename.get(), lineno,
799 JS::LimitedColumnNumberOneOrigin(), lock);
801 #endif
803 if (JS::JitCodeIRInfo* irInfo =
804 CreateProfilerIREntry(profilerRecord, lock)) {
805 irInfo->offset = entry.offset;
806 irInfo->opcode = entry.opcode;
807 // Profiler API now owns this string, if defined.
808 irInfo->str = std::move(entry.str);
811 opcodes_.clear();
813 #ifdef JS_ION_PERF
814 if (IsPerfProfiling()) {
815 fclose(scriptFile);
817 #endif
820 void PerfSpewer::saveJitCodeSourceInfo(JSScript* script, JitCode* code,
821 JS::JitCodeRecord* profilerRecord,
822 AutoLockPerfSpewer& lock) {
823 const char* filename = script->filename();
824 if (!filename) {
825 return;
828 #ifdef JS_ION_PERF
829 bool perfProfiling = IsPerfProfiling();
831 if (perfProfiling) {
832 JitDumpDebugRecord debug_record = {};
834 uint64_t n_records = 0;
835 for (OpcodeEntry& entry : opcodes_) {
836 if (entry.bytecodepc) {
837 n_records++;
841 debug_record.header.id = JIT_CODE_DEBUG_INFO;
842 debug_record.header.total_size =
843 sizeof(debug_record) +
844 n_records * (sizeof(JitDumpDebugEntry) + strlen(filename) + 1);
845 debug_record.header.timestamp = GetMonotonicTimestamp();
846 debug_record.code_addr = uint64_t(code->raw());
847 debug_record.nr_entry = n_records;
849 WriteToJitDumpFile(&debug_record, sizeof(debug_record), lock);
851 #endif
852 uint32_t lineno = 0;
853 JS::LimitedColumnNumberOneOrigin colno;
855 for (OpcodeEntry& entry : opcodes_) {
856 jsbytecode* pc = entry.bytecodepc;
857 if (!pc) {
858 continue;
860 // We could probably make this a bit faster by caching the previous pc
861 // offset, but it currently doesn't seem noticeable when testing.
862 lineno = PCToLineNumber(script, pc, &colno);
864 if (JS::JitCodeSourceInfo* srcInfo =
865 CreateProfilerSourceEntry(profilerRecord, lock)) {
866 srcInfo->offset = entry.offset;
867 srcInfo->lineno = lineno;
868 srcInfo->colno = colno;
869 srcInfo->filename = JS_smprintf("%s", filename);
872 #ifdef JS_ION_PERF
873 if (perfProfiling) {
874 WriteJitDumpDebugEntry(uint64_t(code->raw()) + entry.offset, filename,
875 lineno, colno, lock);
877 #endif
881 static UniqueChars GetFunctionDesc(const char* tierName, JSContext* cx,
882 JSScript* script,
883 const char* stubName = nullptr) {
884 MOZ_ASSERT(script && tierName && cx);
885 UniqueChars funName;
886 if (script->function() && script->function()->maybePartialDisplayAtom()) {
887 funName = AtomToPrintableString(
888 cx, script->function()->maybePartialDisplayAtom());
891 if (stubName) {
892 return JS_smprintf("%s: %s : %s (%s:%u:%u)", tierName, stubName,
893 funName ? funName.get() : "*", script->filename(),
894 script->lineno(), script->column().oneOriginValue());
896 return JS_smprintf("%s: %s (%s:%u:%u)", tierName,
897 funName ? funName.get() : "*", script->filename(),
898 script->lineno(), script->column().oneOriginValue());
901 void PerfSpewer::saveDebugInfo(JSScript* script, JitCode* code,
902 JS::JitCodeRecord* profilerRecord,
903 AutoLockPerfSpewer& lock) {
904 MOZ_ASSERT(code);
905 if (PerfIREnabled()) {
906 saveJitCodeIRInfo(code, profilerRecord, lock);
907 } else if (PerfSrcEnabled() && script) {
908 saveJitCodeSourceInfo(script, code, profilerRecord, lock);
912 void PerfSpewer::saveProfile(JitCode* code, UniqueChars& desc,
913 JSScript* script) {
914 MOZ_ASSERT(PerfEnabled());
915 MOZ_ASSERT(code && desc);
916 AutoLockPerfSpewer lock;
917 JS::JitCodeRecord* profilerRecord = CreateProfilerEntry(lock);
919 saveDebugInfo(script, code, profilerRecord, lock);
920 CollectJitCodeInfo(desc, code, profilerRecord, lock);
923 IonICPerfSpewer::IonICPerfSpewer(jsbytecode* pc) {
924 if (!opcodes_.emplaceBack(pc)) {
925 AutoLockPerfSpewer lock;
926 opcodes_.clear();
927 DisablePerfSpewer(lock);
931 void IonICPerfSpewer::saveJitCodeSourceInfo(JSScript* script, JitCode* code,
932 JS::JitCodeRecord* profilerRecord,
933 AutoLockPerfSpewer& lock) {
934 #ifdef JS_ION_PERF
935 MOZ_ASSERT(opcodes_.length() == 1);
936 jsbytecode* pc = opcodes_[0].bytecodepc;
938 if (!pc) {
939 return;
942 const char* filename = script->filename();
943 if (!filename) {
944 return;
947 if (!IsPerfProfiling()) {
948 return;
951 JitDumpDebugRecord debug_record = {};
952 uint64_t n_records = 1;
954 debug_record.header.id = JIT_CODE_DEBUG_INFO;
955 debug_record.header.total_size =
956 sizeof(debug_record) +
957 n_records * (sizeof(JitDumpDebugEntry) + strlen(filename) + 1);
959 debug_record.header.timestamp = GetMonotonicTimestamp();
960 debug_record.code_addr = uint64_t(code->raw());
961 debug_record.nr_entry = n_records;
963 WriteToJitDumpFile(&debug_record, sizeof(debug_record), lock);
965 uint32_t lineno;
966 JS::LimitedColumnNumberOneOrigin colno;
967 lineno = PCToLineNumber(script, pc, &colno);
969 WriteJitDumpDebugEntry(uint64_t(code->raw()), filename, lineno, colno, lock);
970 #endif
973 void IonICPerfSpewer::saveProfile(JSContext* cx, JSScript* script,
974 JitCode* code, const char* stubName) {
975 if (!PerfEnabled()) {
976 return;
978 UniqueChars desc = GetFunctionDesc("IonIC", cx, script, stubName);
979 PerfSpewer::saveProfile(code, desc, script);
982 void BaselineICPerfSpewer::saveProfile(JitCode* code, const char* stubName) {
983 if (!PerfEnabled()) {
984 return;
986 UniqueChars desc = JS_smprintf("BaselineIC: %s", stubName);
987 PerfSpewer::saveProfile(code, desc, nullptr);
990 void BaselinePerfSpewer::saveProfile(JSContext* cx, JSScript* script,
991 JitCode* code) {
992 if (!PerfEnabled()) {
993 return;
995 UniqueChars desc = GetFunctionDesc("Baseline", cx, script);
996 PerfSpewer::saveProfile(code, desc, script);
999 void BaselineInterpreterPerfSpewer::saveProfile(JitCode* code) {
1000 if (!PerfEnabled()) {
1001 return;
1004 enum class SpewKind { Uninitialized, SingleSym, MultiSym };
1006 // Check which type of Baseline Interpreter Spew is requested.
1007 static SpewKind kind = SpewKind::Uninitialized;
1008 if (kind == SpewKind::Uninitialized) {
1009 if (getenv("IONPERF_SINGLE_BLINTERP")) {
1010 kind = SpewKind::SingleSym;
1011 } else {
1012 kind = SpewKind::MultiSym;
1016 // For SingleSym, just emit one "BaselineInterpreter" symbol
1017 // and emit the opcodes as IR if IONPERF=ir is used.
1018 if (kind == SpewKind::SingleSym) {
1019 UniqueChars desc = DuplicateString("BaselineInterpreter");
1020 PerfSpewer::saveProfile(code, desc, nullptr);
1021 return;
1024 // For MultiSym, split up each opcode into its own symbol.
1025 // No IR is emitted in this case, so we can skip PerfSpewer::saveProfile.
1026 MOZ_ASSERT(kind == SpewKind::MultiSym);
1027 for (size_t i = 1; i < opcodes_.length(); i++) {
1028 uintptr_t base = uintptr_t(code->raw()) + opcodes_[i - 1].offset;
1029 uintptr_t size = opcodes_[i].offset - opcodes_[i - 1].offset;
1031 UniqueChars rangeName;
1032 if (opcodes_[i - 1].str) {
1033 rangeName = JS_smprintf("BlinterpOp: %s", opcodes_[i - 1].str.get());
1034 } else {
1035 rangeName =
1036 JS_smprintf("BlinterpOp: %s", CodeName(opcodes_[i - 1].opcode));
1039 // If rangeName is empty, we probably went OOM.
1040 if (!rangeName) {
1041 AutoLockPerfSpewer lock;
1042 DisablePerfSpewer(lock);
1043 return;
1046 MOZ_ASSERT(base + size <=
1047 uintptr_t(code->raw()) + code->instructionsSize());
1048 CollectPerfSpewerJitCodeProfile(base, size, rangeName.get());
1052 void BaselineInterpreterPerfSpewer::recordOffset(MacroAssembler& masm,
1053 JSOp op) {
1054 if (!PerfEnabled()) {
1055 return;
1058 if (!opcodes_.emplaceBack(masm.currentOffset(), unsigned(op))) {
1059 opcodes_.clear();
1060 AutoLockPerfSpewer lock;
1061 DisablePerfSpewer(lock);
1065 void BaselineInterpreterPerfSpewer::recordOffset(MacroAssembler& masm,
1066 const char* name) {
1067 if (!PerfEnabled()) {
1068 return;
1071 UniqueChars desc = DuplicateString(name);
1072 if (!opcodes_.emplaceBack(masm.currentOffset(), desc)) {
1073 opcodes_.clear();
1074 AutoLockPerfSpewer lock;
1075 DisablePerfSpewer(lock);
1079 void IonPerfSpewer::saveProfile(JSContext* cx, JSScript* script,
1080 JitCode* code) {
1081 if (!PerfEnabled()) {
1082 return;
1084 UniqueChars desc = GetFunctionDesc("Ion", cx, script);
1085 PerfSpewer::saveProfile(code, desc, script);
1088 void js::jit::CollectPerfSpewerJitCodeProfile(JitCode* code, const char* msg) {
1089 if (!code || !PerfEnabled()) {
1090 return;
1093 size_t size = code->instructionsSize();
1094 if (size > 0) {
1095 AutoLockPerfSpewer lock;
1097 JS::JitCodeRecord* profilerRecord = CreateProfilerEntry(lock);
1098 UniqueChars desc = JS_smprintf("%s", msg);
1099 PerfSpewer::CollectJitCodeInfo(desc, code, profilerRecord, lock);
1103 void js::jit::CollectPerfSpewerJitCodeProfile(uintptr_t base, uint64_t size,
1104 const char* msg) {
1105 if (!PerfEnabled()) {
1106 return;
1109 if (size > 0) {
1110 AutoLockPerfSpewer lock;
1112 JS::JitCodeRecord* profilerRecord = CreateProfilerEntry(lock);
1113 UniqueChars desc = JS_smprintf("%s", msg);
1114 PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast<void*>(base), size,
1115 profilerRecord, lock);
1119 void js::jit::CollectPerfSpewerWasmMap(uintptr_t base, uintptr_t size,
1120 const char* filename,
1121 const char* annotation) {
1122 if (size == 0U || !PerfEnabled()) {
1123 return;
1125 AutoLockPerfSpewer lock;
1127 JS::JitCodeRecord* profilerRecord = CreateProfilerEntry(lock);
1128 UniqueChars desc = JS_smprintf("%s: Function %s", filename, annotation);
1129 PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast<void*>(base),
1130 uint64_t(size), profilerRecord, lock);
1133 void js::jit::CollectPerfSpewerWasmFunctionMap(uintptr_t base, uintptr_t size,
1134 const char* filename,
1135 unsigned lineno,
1136 const char* funcName) {
1137 if (size == 0U || !PerfEnabled()) {
1138 return;
1140 AutoLockPerfSpewer lock;
1142 JS::JitCodeRecord* profilerRecord = CreateProfilerEntry(lock);
1143 UniqueChars desc =
1144 JS_smprintf("%s:%u: Function %s", filename, lineno, funcName);
1145 PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast<void*>(base),
1146 uint64_t(size), profilerRecord, lock);
1149 void js::jit::PerfSpewerRangeRecorder::appendEntry(UniqueChars& desc) {
1150 if (!ranges.append(std::make_pair(masm.currentOffset(), std::move(desc)))) {
1151 AutoLockPerfSpewer lock;
1152 DisablePerfSpewer(lock);
1153 ranges.clear();
1157 void js::jit::PerfSpewerRangeRecorder::recordOffset(const char* name) {
1158 if (!PerfEnabled()) {
1159 return;
1161 UniqueChars desc = DuplicateString(name);
1162 appendEntry(desc);
1165 void js::jit::PerfSpewerRangeRecorder::recordVMWrapperOffset(const char* name) {
1166 if (!PerfEnabled()) {
1167 return;
1170 UniqueChars desc = JS_smprintf("VMWrapper: %s", name);
1171 appendEntry(desc);
1174 void js::jit::PerfSpewerRangeRecorder::recordOffset(const char* name,
1175 JSContext* cx,
1176 JSScript* script) {
1177 if (!PerfEnabled()) {
1178 return;
1180 UniqueChars desc = GetFunctionDesc(name, cx, script);
1181 appendEntry(desc);
1184 void js::jit::PerfSpewerRangeRecorder::collectRangesForJitCode(JitCode* code) {
1185 if (!PerfEnabled() || ranges.empty()) {
1186 return;
1189 uintptr_t basePtr = uintptr_t(code->raw());
1190 uintptr_t offsetStart = 0;
1192 for (OffsetPair& pair : ranges) {
1193 uint32_t offsetEnd = std::get<0>(pair);
1194 uintptr_t rangeSize = uintptr_t(offsetEnd - offsetStart);
1195 const char* rangeName = std::get<1>(pair).get();
1197 CollectPerfSpewerJitCodeProfile(basePtr + offsetStart, rangeSize,
1198 rangeName);
1199 offsetStart = offsetEnd;
1202 MOZ_ASSERT(offsetStart <= code->instructionsSize());
1203 ranges.clear();