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