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.

     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 }

mercurial