1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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
|