Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "vm/SPSProfiler.h"
9 #include "mozilla/DebugOnly.h"
11 #include "jsnum.h"
12 #include "jsprf.h"
13 #include "jsscript.h"
15 #include "jit/BaselineJIT.h"
16 #include "vm/StringBuffer.h"
18 using namespace js;
20 using mozilla::DebugOnly;
22 SPSProfiler::SPSProfiler(JSRuntime *rt)
23 : rt(rt),
24 stack_(nullptr),
25 size_(nullptr),
26 max_(0),
27 slowAssertions(false),
28 enabled_(false),
29 lock_(nullptr),
30 eventMarker_(nullptr)
31 {
32 JS_ASSERT(rt != nullptr);
33 }
35 bool
36 SPSProfiler::init()
37 {
38 #ifdef JS_THREADSAFE
39 lock_ = PR_NewLock();
40 if (lock_ == nullptr)
41 return false;
42 #endif
43 return true;
44 }
46 SPSProfiler::~SPSProfiler()
47 {
48 if (strings.initialized()) {
49 for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
50 js_free(const_cast<char *>(e.front().value()));
51 }
52 #ifdef JS_THREADSAFE
53 if (lock_)
54 PR_DestroyLock(lock_);
55 #endif
56 }
58 void
59 SPSProfiler::setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max)
60 {
61 AutoSPSLock lock(lock_);
62 JS_ASSERT_IF(size_ && *size_ != 0, !enabled());
63 if (!strings.initialized())
64 strings.init();
65 stack_ = stack;
66 size_ = size;
67 max_ = max;
68 }
70 void
71 SPSProfiler::setEventMarker(void (*fn)(const char *))
72 {
73 eventMarker_ = fn;
74 }
76 void
77 SPSProfiler::enable(bool enabled)
78 {
79 JS_ASSERT(installed());
81 if (enabled_ == enabled)
82 return;
84 /*
85 * Ensure all future generated code will be instrumented, or that all
86 * currently instrumented code is discarded
87 */
88 ReleaseAllJITCode(rt->defaultFreeOp());
90 enabled_ = enabled;
92 #ifdef JS_ION
93 /* Toggle SPS-related jumps on baseline jitcode.
94 * The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not
95 * jitcode for scripts with active frames on the stack. These scripts need to have
96 * their profiler state toggled so they behave properly.
97 */
98 jit::ToggleBaselineSPS(rt, enabled);
99 #endif
100 }
102 /* Lookup the string for the function/script, creating one if necessary */
103 const char*
104 SPSProfiler::profileString(JSScript *script, JSFunction *maybeFun)
105 {
106 AutoSPSLock lock(lock_);
107 JS_ASSERT(strings.initialized());
108 ProfileStringMap::AddPtr s = strings.lookupForAdd(script);
109 if (s)
110 return s->value();
111 const char *str = allocProfileString(script, maybeFun);
112 if (str == nullptr)
113 return nullptr;
114 if (!strings.add(s, script, str)) {
115 js_free(const_cast<char *>(str));
116 return nullptr;
117 }
118 return str;
119 }
121 void
122 SPSProfiler::onScriptFinalized(JSScript *script)
123 {
124 /*
125 * This function is called whenever a script is destroyed, regardless of
126 * whether profiling has been turned on, so don't invoke a function on an
127 * invalid hash set. Also, even if profiling was enabled but then turned
128 * off, we still want to remove the string, so no check of enabled() is
129 * done.
130 */
131 AutoSPSLock lock(lock_);
132 if (!strings.initialized())
133 return;
134 if (ProfileStringMap::Ptr entry = strings.lookup(script)) {
135 const char *tofree = entry->value();
136 strings.remove(entry);
137 js_free(const_cast<char *>(tofree));
138 }
139 }
141 void
142 SPSProfiler::markEvent(const char *event)
143 {
144 JS_ASSERT(enabled());
145 if (eventMarker_) {
146 JS::AutoAssertNoGC nogc;
147 eventMarker_(event);
148 }
149 }
151 bool
152 SPSProfiler::enter(JSScript *script, JSFunction *maybeFun)
153 {
154 const char *str = profileString(script, maybeFun);
155 if (str == nullptr)
156 return false;
158 #ifdef DEBUG
159 // In debug builds, assert the JS pseudo frames already on the stack
160 // have a non-null pc. Only look at the top frames to avoid quadratic
161 // behavior.
162 if (*size_ > 0 && *size_ - 1 < max_) {
163 size_t start = (*size_ > 4) ? *size_ - 4 : 0;
164 for (size_t i = start; i < *size_ - 1; i++)
165 MOZ_ASSERT_IF(stack_[i].js(), stack_[i].pc() != nullptr);
166 }
167 #endif
169 push(str, nullptr, script, script->code());
170 return true;
171 }
173 void
174 SPSProfiler::exit(JSScript *script, JSFunction *maybeFun)
175 {
176 pop();
178 #ifdef DEBUG
179 /* Sanity check to make sure push/pop balanced */
180 if (*size_ < max_) {
181 const char *str = profileString(script, maybeFun);
182 /* Can't fail lookup because we should already be in the set */
183 JS_ASSERT(str != nullptr);
185 // Bug 822041
186 if (!stack_[*size_].js()) {
187 fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
188 fprintf(stderr, " stack=%p size=%d/%d\n", (void*) stack_, *size_, max_);
189 for (int32_t i = *size_; i >= 0; i--) {
190 if (stack_[i].js())
191 fprintf(stderr, " [%d] JS %s\n", i, stack_[i].label());
192 else
193 fprintf(stderr, " [%d] C line %d %s\n", i, stack_[i].line(), stack_[i].label());
194 }
195 }
197 JS_ASSERT(stack_[*size_].js());
198 JS_ASSERT(stack_[*size_].script() == script);
199 JS_ASSERT(strcmp((const char*) stack_[*size_].label(), str) == 0);
200 stack_[*size_].setLabel(nullptr);
201 stack_[*size_].setPC(nullptr);
202 }
203 #endif
204 }
206 void
207 SPSProfiler::enterNative(const char *string, void *sp)
208 {
209 /* these operations cannot be re-ordered, so volatile-ize operations */
210 volatile ProfileEntry *stack = stack_;
211 volatile uint32_t *size = size_;
212 uint32_t current = *size;
214 JS_ASSERT(enabled());
215 if (current < max_) {
216 stack[current].setLabel(string);
217 stack[current].setStackAddress(sp);
218 stack[current].setScript(nullptr);
219 stack[current].setLine(0);
220 }
221 *size = current + 1;
222 }
224 void
225 SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc)
226 {
227 /* these operations cannot be re-ordered, so volatile-ize operations */
228 volatile ProfileEntry *stack = stack_;
229 volatile uint32_t *size = size_;
230 uint32_t current = *size;
232 JS_ASSERT(installed());
233 if (current < max_) {
234 stack[current].setLabel(string);
235 stack[current].setStackAddress(sp);
236 stack[current].setScript(script);
237 stack[current].setPC(pc);
238 }
239 *size = current + 1;
240 }
242 void
243 SPSProfiler::pop()
244 {
245 JS_ASSERT(installed());
246 (*size_)--;
247 JS_ASSERT(*(int*)size_ >= 0);
248 }
250 /*
251 * Serializes the script/function pair into a "descriptive string" which is
252 * allowed to fail. This function cannot trigger a GC because it could finalize
253 * some scripts, resize the hash table of profile strings, and invalidate the
254 * AddPtr held while invoking allocProfileString.
255 */
256 const char *
257 SPSProfiler::allocProfileString(JSScript *script, JSFunction *maybeFun)
258 {
259 // Note: this profiler string is regexp-matched by
260 // browser/devtools/profiler/cleopatra/js/parserWorker.js.
262 // Determine if the function (if any) has an explicit or guessed name.
263 bool hasAtom = maybeFun && maybeFun->displayAtom();
265 // Get the function name, if any, and its length.
266 const jschar *atom = nullptr;
267 size_t lenAtom = 0;
268 if (hasAtom) {
269 atom = maybeFun->displayAtom()->charsZ();
270 lenAtom = maybeFun->displayAtom()->length();
271 }
273 // Get the script filename, if any, and its length.
274 const char *filename = script->filename();
275 if (filename == nullptr)
276 filename = "<unknown>";
277 size_t lenFilename = strlen(filename);
279 // Get the line number and its length as a string.
280 uint64_t lineno = script->lineno();
281 size_t lenLineno = 1;
282 for (uint64_t i = lineno; i /= 10; lenLineno++);
284 // Determine the required buffer size.
285 size_t len = lenFilename + lenLineno + 1; // +1 for the ":" separating them.
286 if (hasAtom)
287 len += lenAtom + 3; // +3 for the " (" and ")" it adds.
289 // Allocate the buffer.
290 char *cstr = js_pod_malloc<char>(len + 1);
291 if (cstr == nullptr)
292 return nullptr;
294 // Construct the descriptive string.
295 DebugOnly<size_t> ret;
296 if (hasAtom)
297 ret = JS_snprintf(cstr, len + 1, "%hs (%s:%llu)", atom, filename, lineno);
298 else
299 ret = JS_snprintf(cstr, len + 1, "%s:%llu", filename, lineno);
301 MOZ_ASSERT(ret == len, "Computed length should match actual length!");
303 return cstr;
304 }
306 SPSEntryMarker::SPSEntryMarker(JSRuntime *rt
307 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
308 : profiler(&rt->spsProfiler)
309 {
310 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
311 if (!profiler->installed()) {
312 profiler = nullptr;
313 return;
314 }
315 size_before = *profiler->size_;
316 profiler->pushNoCopy("js::RunScript", this, nullptr, nullptr);
317 }
319 SPSEntryMarker::~SPSEntryMarker()
320 {
321 if (profiler != nullptr) {
322 profiler->pop();
323 JS_ASSERT(size_before == *profiler->size_);
324 }
325 }
327 JS_FRIEND_API(jsbytecode*)
328 ProfileEntry::pc() const volatile
329 {
330 return idx == NullPCIndex ? nullptr : script()->offsetToPC(idx);
331 }
333 JS_FRIEND_API(void)
334 ProfileEntry::setPC(jsbytecode *pc) volatile
335 {
336 idx = pc == nullptr ? NullPCIndex : script()->pcToOffset(pc);
337 }
339 JS_FRIEND_API(void)
340 js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max)
341 {
342 rt->spsProfiler.setProfilingStack(stack, size, max);
343 }
345 JS_FRIEND_API(void)
346 js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
347 {
348 rt->spsProfiler.enable(enabled);
349 }
351 JS_FRIEND_API(void)
352 js::RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *))
353 {
354 JS_ASSERT(rt->spsProfiler.enabled());
355 rt->spsProfiler.setEventMarker(fn);
356 }
358 JS_FRIEND_API(jsbytecode*)
359 js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip)
360 {
361 return rt->spsProfiler.ipToPC(script, size_t(ip));
362 }