VTuneWrapper.cpp (5441B)
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/. */ 6 7 #include "vtune/VTuneWrapper.h" 8 9 #include "mozilla/Sprintf.h" 10 11 #include "jit/JitCode.h" 12 #include "js/Utility.h" 13 #include "threading/LockGuard.h" 14 #include "threading/Mutex.h" 15 #include "vm/JSScript.h" 16 #include "vm/MutexIDs.h" 17 #include "vtune/jitprofiling.h" 18 19 namespace js::vtune { 20 21 // VTune internals are not known to be threadsafe. 22 static Mutex* VTuneMutex = nullptr; 23 24 // Firefox must be launched from within VTune. Then the profiler 25 // status never changes, and we can avoid shared library checks. 26 static bool VTuneLoaded(false); 27 28 // Initialization is called from a single-threaded context. 29 bool Initialize() { 30 VTuneMutex = js_new<Mutex>(mutexid::VTuneLock); 31 if (!VTuneMutex) return false; 32 33 if (getenv("JS_LOAD_VTUNE_LIB")) { 34 // Load the VTune shared library, if present. 35 int loaded = loadiJIT_Funcs(); 36 if (loaded == 1) VTuneLoaded = true; 37 } 38 39 return true; 40 } 41 42 // Shutdown is called froma single-threaded context. 43 void Shutdown() { 44 js_delete(VTuneMutex); 45 VTuneMutex = nullptr; 46 } 47 48 bool IsProfilingActive() { 49 // Checking VTuneLoaded guards against VTune internals attempting 50 // to load the VTune library upon their invocation. 51 return VTuneLoaded && iJIT_IsProfilingActive() == iJIT_SAMPLING_ON; 52 } 53 54 uint32_t GenerateUniqueMethodID() { 55 // iJIT_GetNewMethodID() is explicitly not threadsafe. 56 MOZ_ASSERT(VTuneMutex); 57 LockGuard<Mutex> guard(*VTuneMutex); 58 return (uint32_t)iJIT_GetNewMethodID(); 59 } 60 61 static int SafeNotifyEvent(iJIT_JVM_EVENT event_type, void* data) { 62 MOZ_ASSERT(VTuneMutex); 63 LockGuard<Mutex> guard(*VTuneMutex); 64 return iJIT_NotifyEvent(event_type, data); 65 } 66 67 // Stubs and trampolines are created on engine initialization and are never 68 // unloaded. 69 void MarkStub(const js::jit::JitCode* code, const char* name) { 70 if (!IsProfilingActive()) return; 71 72 iJIT_Method_Load_V2 method = {0}; 73 method.method_id = GenerateUniqueMethodID(); 74 method.method_name = const_cast<char*>(name); 75 method.method_load_address = code->raw(); 76 method.method_size = code->instructionsSize(); 77 method.module_name = const_cast<char*>("jitstubs"); 78 79 int ok = 80 SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); 81 if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); 82 } 83 84 void MarkRegExp(const js::jit::JitCode* code, bool match_only) { 85 if (!IsProfilingActive()) return; 86 87 iJIT_Method_Load_V2 method = {0}; 88 method.method_id = GenerateUniqueMethodID(); 89 method.method_load_address = code->raw(); 90 method.method_size = code->instructionsSize(); 91 92 if (match_only) 93 method.method_name = const_cast<char*>("regexp (match-only)"); 94 else 95 method.method_name = const_cast<char*>("regexp (normal)"); 96 97 method.module_name = const_cast<char*>("irregexp"); 98 99 int ok = 100 SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); 101 if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); 102 } 103 104 void MarkScript(const js::jit::JitCode* code, JSScript* script, 105 const char* module) { 106 if (!IsProfilingActive()) return; 107 108 iJIT_Method_Load_V2 method = {0}; 109 method.method_id = script->vtuneMethodID(); 110 method.method_load_address = code->raw(); 111 method.method_size = code->instructionsSize(); 112 method.module_name = const_cast<char*>(module); 113 114 char namebuf[512]; 115 SprintfLiteral(namebuf, "%s:%u:%u", script->filename(), script->lineno(), 116 script->column().oneOriginValue()); 117 118 method.method_name = &namebuf[0]; 119 120 int ok = 121 SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); 122 if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); 123 } 124 125 void MarkWasm(unsigned methodId, const char* name, void* start, 126 uintptr_t size) { 127 if (!IsProfilingActive()) return; 128 129 iJIT_Method_Load_V2 method = {0}; 130 method.method_id = methodId; 131 method.method_name = const_cast<char*>(name); 132 method.method_load_address = start; 133 method.method_size = (unsigned)size; 134 method.module_name = const_cast<char*>("wasm"); 135 136 int ok = 137 SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); 138 if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); 139 } 140 141 void UnmarkCode(const js::jit::JitCode* code) { 142 UnmarkBytes(code->raw(), (unsigned)code->instructionsSize()); 143 } 144 145 void UnmarkBytes(void* bytes, unsigned size) { 146 if (!IsProfilingActive()) return; 147 148 // It appears that the method_id is not required for unloading. 149 iJIT_Method_Load method = {0}; 150 method.method_load_address = bytes; 151 method.method_size = size; 152 153 // The iJVM_EVENT_TYPE_METHOD_UNLOAD_START event is undocumented. 154 // VTune appears to happily accept unload events even for untracked JitCode. 155 int ok = SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, (void*)&method); 156 157 // Assertions aren't reported in VTune: instead, they immediately end 158 // profiling with no warning that a crash occurred. This can generate 159 // misleading profiles. So instead, print out a message to stdout (which VTune 160 // does not redirect). 161 if (ok != 1) printf("[!] VTune Integration: Failed to unload method.\n"); 162 } 163 164 } // namespace js::vtune