diff options
Diffstat (limited to 'js/src/vtune/VTuneWrapper.cpp')
-rw-r--r-- | js/src/vtune/VTuneWrapper.cpp | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/js/src/vtune/VTuneWrapper.cpp b/js/src/vtune/VTuneWrapper.cpp new file mode 100644 index 0000000000..d677b64fff --- /dev/null +++ b/js/src/vtune/VTuneWrapper.cpp @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vtune/VTuneWrapper.h" + +#include "mozilla/Sprintf.h" + +#include "threading/LockGuard.h" +#include "threading/Mutex.h" +#include "vm/JSContext.h" +#include "vm/MutexIDs.h" +#include "vm/Realm.h" +#include "vm/Shape.h" + +namespace js::vtune { + +// VTune internals are not known to be threadsafe. +static Mutex* VTuneMutex = nullptr; + +// Firefox must be launched from within VTune. Then the profiler +// status never changes, and we can avoid shared library checks. +static bool VTuneLoaded(false); + +// Initialization is called from a single-threaded context. +bool Initialize() { + VTuneMutex = js_new<Mutex>(mutexid::VTuneLock); + if (!VTuneMutex) return false; + + // Load the VTune shared library, if present. + int loaded = loadiJIT_Funcs(); + if (loaded == 1) VTuneLoaded = true; + + return true; +} + +// Shutdown is called froma single-threaded context. +void Shutdown() { + js_delete(VTuneMutex); + VTuneMutex = nullptr; +} + +bool IsProfilingActive() { + // Checking VTuneLoaded guards against VTune internals attempting + // to load the VTune library upon their invocation. + return VTuneLoaded && iJIT_IsProfilingActive() == iJIT_SAMPLING_ON; +} + +uint32_t GenerateUniqueMethodID() { + // iJIT_GetNewMethodID() is explicitly not threadsafe. + MOZ_ASSERT(VTuneMutex); + LockGuard<Mutex> guard(*VTuneMutex); + return (uint32_t)iJIT_GetNewMethodID(); +} + +static int SafeNotifyEvent(iJIT_JVM_EVENT event_type, void* data) { + MOZ_ASSERT(VTuneMutex); + LockGuard<Mutex> guard(*VTuneMutex); + return iJIT_NotifyEvent(event_type, data); +} + +// Stubs and trampolines are created on engine initialization and are never +// unloaded. +void MarkStub(const js::jit::JitCode* code, const char* name) { + if (!IsProfilingActive()) return; + + iJIT_Method_Load_V2 method = {0}; + method.method_id = GenerateUniqueMethodID(); + method.method_name = const_cast<char*>(name); + method.method_load_address = code->raw(); + method.method_size = code->instructionsSize(); + method.module_name = const_cast<char*>("jitstubs"); + + int ok = + SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); + if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); +} + +void MarkRegExp(const js::jit::JitCode* code, bool match_only) { + if (!IsProfilingActive()) return; + + iJIT_Method_Load_V2 method = {0}; + method.method_id = GenerateUniqueMethodID(); + method.method_load_address = code->raw(); + method.method_size = code->instructionsSize(); + + if (match_only) + method.method_name = const_cast<char*>("regexp (match-only)"); + else + method.method_name = const_cast<char*>("regexp (normal)"); + + method.module_name = const_cast<char*>("irregexp"); + + int ok = + SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); + if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); +} + +void MarkScript(const js::jit::JitCode* code, JSScript* script, + const char* module) { + if (!IsProfilingActive()) return; + + iJIT_Method_Load_V2 method = {0}; + method.method_id = script->vtuneMethodID(); + method.method_load_address = code->raw(); + method.method_size = code->instructionsSize(); + method.module_name = const_cast<char*>(module); + + // Line numbers begin at 1, but columns begin at 0. + // Text editors start at 1,1 so fixup is performed to match. + char namebuf[512]; + SprintfLiteral(namebuf, "%s:%u:%u", script->filename(), script->lineno(), + script->column() + 1); + + method.method_name = &namebuf[0]; + + int ok = + SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); + if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); +} + +void MarkWasm(unsigned methodId, const char* name, void* start, + uintptr_t size) { + if (!IsProfilingActive()) return; + + iJIT_Method_Load_V2 method = {0}; + method.method_id = methodId; + method.method_name = const_cast<char*>(name); + method.method_load_address = start; + method.method_size = (unsigned)size; + method.module_name = const_cast<char*>("wasm"); + + int ok = + SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method); + if (ok != 1) printf("[!] VTune Integration: Failed to load method.\n"); +} + +void UnmarkCode(const js::jit::JitCode* code) { + UnmarkBytes(code->raw(), (unsigned)code->instructionsSize()); +} + +void UnmarkBytes(void* bytes, unsigned size) { + if (!IsProfilingActive()) return; + + // It appears that the method_id is not required for unloading. + iJIT_Method_Load method = {0}; + method.method_load_address = bytes; + method.method_size = size; + + // The iJVM_EVENT_TYPE_METHOD_UNLOAD_START event is undocumented. + // VTune appears to happily accept unload events even for untracked JitCode. + int ok = SafeNotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, (void*)&method); + + // Assertions aren't reported in VTune: instead, they immediately end + // profiling with no warning that a crash occurred. This can generate + // misleading profiles. So instead, print out a message to stdout (which VTune + // does not redirect). + if (ok != 1) printf("[!] VTune Integration: Failed to unload method.\n"); +} + +} // namespace js::vtune |