js/src/vm/SPSProfiler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial