Bug 976260 - Register javascript performance events with the profiler, second attempt at pushing. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Mon, 03 Mar 2014 14:36:08 -0500
changeset 189854 b37ed02f9f4b1da4fb0fcd183f4982045a5d7a44
parent 189853 06eb9d02fd2a9a7eb8fbeb875bc4298d38504b76
child 189855 dd4e1e3a72a5dcc42118356d0e676ea06842be33
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs976260
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 976260 - Register javascript performance events with the profiler, second attempt at pushing. r=jandem
js/public/ProfilingStack.h
js/src/jit/BaselineBailouts.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/IonTypes.h
js/src/shell/js.cpp
js/src/vm/SPSProfiler.cpp
js/src/vm/SPSProfiler.h
tools/profiler/ProfilerMarkers.cpp
tools/profiler/PseudoStack.h
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -86,14 +86,17 @@ class ProfileEntry
 
 JS_FRIEND_API(void)
 SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                          uint32_t max);
 
 JS_FRIEND_API(void)
 EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
 
+JS_FRIEND_API(void)
+RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *));
+
 JS_FRIEND_API(jsbytecode*)
 ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);
 
 } // namespace js
 
 #endif  /* js_ProfilingStack_h */
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jsprf.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CompileInfo.h"
 #include "jit/IonSpewer.h"
 #include "vm/ArgumentsObject.h"
 
 #include "jsscriptinlines.h"
@@ -970,24 +971,44 @@ InitFromBailout(JSContext *cx, HandleScr
                 }
             } else {
                 opReturnAddr = nativeCodeForPC;
             }
             builder.setResumeAddr(opReturnAddr);
             IonSpew(IonSpew_BaselineBailouts, "      Set resumeAddr=%p", opReturnAddr);
         }
 
-        if (cx->runtime()->spsProfiler.enabled() && blFrame->hasPushedSPSFrame()) {
-            // Set PC index to 0 for the innermost frame to match what the
-            // interpreter and Baseline do: they update the SPS pc for
-            // JSOP_CALL ops but set it to 0 when running other ops. Ion code
-            // can set the pc to NullPCIndex and this will confuse SPS when
-            // Baseline calls into the VM at non-CALL ops and re-enters JS.
-            IonSpew(IonSpew_BaselineBailouts, "      Setting PCidx for last frame to 0");
-            cx->runtime()->spsProfiler.updatePC(script, script->code());
+        if (cx->runtime()->spsProfiler.enabled()) {
+            if (blFrame->hasPushedSPSFrame()) {
+                // Set PC index to 0 for the innermost frame to match what the
+                // interpreter and Baseline do: they update the SPS pc for
+                // JSOP_CALL ops but set it to 0 when running other ops. Ion code
+                // can set the pc to NullPCIndex and this will confuse SPS when
+                // Baseline calls into the VM at non-CALL ops and re-enters JS.
+                IonSpew(IonSpew_BaselineBailouts, "      Setting PCidx for last frame to 0");
+                cx->runtime()->spsProfiler.updatePC(script, script->code());
+            }
+
+            // Register bailout with profiler.
+            const char *filename = script->filename();
+            if (filename == nullptr)
+                filename = "<unknown>";
+            unsigned len = strlen(filename) + 200;
+            char *buf = js_pod_malloc<char>(len);
+            if (buf == nullptr)
+                return false;
+            JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d",
+                                  BailoutKindString(bailoutKind),
+                                  resumeAfter ? "after" : "at",
+                                  js_CodeName[op],
+                                  int(PCToLineNumber(script, pc)),
+                                  filename,
+                                  int(script->lineno()));
+            cx->runtime()->spsProfiler.markEvent(buf);
+            js_free(buf);
         }
 
         return true;
     }
 
     *callPC = pc;
 
     // Write out descriptor of BaselineJS frame.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
+#include "jsprf.h"
 
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #ifdef JSGC_GENERATIONAL
 # include "gc/Nursery.h"
 #endif
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonCaches.h"
@@ -6221,16 +6222,29 @@ CodeGenerator::link(JSContext *cx, types
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
         ionScript->setHasSPSInstrumentation();
 
     SetIonScript(script, executionMode, ionScript);
 
+    if (cx->runtime()->spsProfiler.enabled()) {
+        const char *filename = script->filename();
+        if (filename == nullptr)
+            filename = "<unknown>";
+        unsigned len = strlen(filename) + 50;
+        char *buf = js_pod_malloc<char>(len);
+        if (!buf)
+            return false;
+        JS_snprintf(buf, len, "Ion compiled %s:%d", filename, (int) script->lineno());
+        cx->runtime()->spsProfiler.markEvent(buf);
+        js_free(buf);
+    }
+
     // In parallel execution mode, when we first compile a script, we
     // don't know that its potential callees are compiled, so set a
     // flag warning that the callees may not be fully compiled.
     if (!callTargets.empty())
         ionScript->setHasUncompiledCallTarget();
 
     invalidateEpilogueData_.fixup(&masm);
     Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsworkers.h"
 #if JS_TRACE_LOGGING
 #include "TraceLogging.h"
 #endif
 
+#include "jsprf.h"
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CodeGenerator.h"
@@ -2616,16 +2617,37 @@ jit::Invalidate(JSContext *cx, const Vec
 }
 
 bool
 jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
                 bool cancelOffThread)
 {
     JS_ASSERT(script->hasIonScript());
 
+    if (cx->runtime()->spsProfiler.enabled()) {
+        // Register invalidation with profiler.
+        // Format of event payload string:
+        //      "<filename>:<lineno>"
+
+        // Get the script filename, if any, and its length.
+        const char *filename = script->filename();
+        if (filename == nullptr)
+            filename = "<unknown>";
+
+        size_t len = strlen(filename) + 20;
+        char *buf = js_pod_malloc<char>(len);
+        if (!buf)
+            return false;
+
+        // Construct the descriptive string.
+        JS_snprintf(buf, len, "Invalidate %s:%llu", filename, script->lineno());
+        cx->runtime()->spsProfiler.markEvent(buf);
+        js_free(buf);
+    }
+
     Vector<types::RecompileInfo> scripts(cx);
 
     switch (mode) {
       case SequentialExecution:
         JS_ASSERT(script->hasIonScript());
         if (!scripts.append(script->ionScript()->recompileInfo()))
             return false;
         break;
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -47,17 +47,16 @@ enum BailoutKind
 
     // A bailout caused by invalid assumptions based on Baseline code.
     Bailout_BaselineInfo
 };
 
 static const uint32_t BAILOUT_KIND_BITS = 3;
 static const uint32_t BAILOUT_RESUME_BITS = 1;
 
-#ifdef DEBUG
 inline const char *
 BailoutKindString(BailoutKind kind)
 {
     switch (kind) {
       case Bailout_Normal:
         return "Bailout_Normal";
       case Bailout_ArgumentCheck:
         return "Bailout_ArgumentCheck";
@@ -66,17 +65,16 @@ BailoutKindString(BailoutKind kind)
       case Bailout_ShapeGuard:
         return "Bailout_ShapeGuard";
       case Bailout_BaselineInfo:
         return "Bailout_BaselineInfo";
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid BailoutKind");
     }
 }
-#endif
 
 static const uint32_t ELEMENT_TYPE_BITS = 4;
 static const uint32_t ELEMENT_TYPE_SHIFT = 0;
 static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1;
 static const uint32_t VECTOR_SCALE_BITS = 2;
 static const uint32_t VECTOR_SCALE_SHIFT = ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT;
 static const uint32_t VECTOR_SCALE_MASK = (1 << VECTOR_SCALE_BITS) - 1;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4276,16 +4276,32 @@ static bool
 SetCachingEnabled(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     jsCachingEnabled = ToBoolean(args.get(0));
     args.rval().setUndefined();
     return true;
 }
 
+static void
+PrintProfilerEvents_Callback(const char *msg)
+{
+    fprintf(stderr, "PROFILER EVENT: %s\n", msg);
+}
+
+static bool
+PrintProfilerEvents(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (cx->runtime()->spsProfiler.enabled())
+        js::RegisterRuntimeProfilingEventMarker(cx->runtime(), &PrintProfilerEvents_Callback);
+    args.rval().setUndefined();
+    return true;
+}
+
 static const JSFunctionSpecWithHelp shell_functions[] = {
     JS_FN_HELP("version", Version, 0, 0,
 "version([number])",
 "  Get or force a script compilation version number."),
 
     JS_FN_HELP("options", Options, 0, 0,
 "options([option ...])",
 "  Get or toggle JavaScript options."),
@@ -4616,16 +4632,21 @@ static const JSFunctionSpecWithHelp shel
 
     JS_FN_HELP("cacheEntry", CacheEntry, 1, 0,
 "cacheEntry(code)",
 "  Return a new opaque object which emulates a cache entry of a script.  This\n"
 "  object encapsulates the code and its cached content. The cache entry is filled\n"
 "  and read by the \"evaluate\" function by using it in place of the source, and\n"
 "  by setting \"saveBytecode\" and \"loadBytecode\" options."),
 
+    JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0,
+"printProfilerEvents()",
+"  Register a callback with the profiler that prints javascript profiler events\n"
+"  to stderr.  Callback is only registered if profiling is enabled."),
+
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
     JS_FN_HELP("clone", Clone, 1, 0,
 "clone(fun[, scope])",
 "  Clone function object."),
 
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -21,17 +21,18 @@ using mozilla::DebugOnly;
 
 SPSProfiler::SPSProfiler(JSRuntime *rt)
   : rt(rt),
     stack_(nullptr),
     size_(nullptr),
     max_(0),
     slowAssertions(false),
     enabled_(false),
-    lock_(nullptr)
+    lock_(nullptr),
+    eventMarker_(nullptr)
 {
     JS_ASSERT(rt != nullptr);
 }
 
 bool
 SPSProfiler::init()
 {
 #ifdef JS_THREADSAFE
@@ -62,16 +63,22 @@ SPSProfiler::setProfilingStack(ProfileEn
     if (!strings.initialized())
         strings.init();
     stack_ = stack;
     size_  = size;
     max_   = max;
 }
 
 void
+SPSProfiler::setEventMarker(void (*fn)(const char *))
+{
+    eventMarker_ = fn;
+}
+
+void
 SPSProfiler::enable(bool enabled)
 {
     JS_ASSERT(installed());
 
     if (enabled_ == enabled)
         return;
 
     /*
@@ -126,16 +133,26 @@ SPSProfiler::onScriptFinalized(JSScript 
         return;
     if (ProfileStringMap::Ptr entry = strings.lookup(script)) {
         const char *tofree = entry->value();
         strings.remove(entry);
         js_free(const_cast<char *>(tofree));
     }
 }
 
+void
+SPSProfiler::markEvent(const char *event)
+{
+    JS_ASSERT(enabled());
+    if (eventMarker_) {
+        JS::AutoAssertNoGC nogc;
+        eventMarker_(event);
+    }
+}
+
 bool
 SPSProfiler::enter(JSScript *script, JSFunction *maybeFun)
 {
     const char *str = profileString(script, maybeFun);
     if (str == nullptr)
         return false;
 
 #ifdef DEBUG
@@ -326,13 +343,20 @@ js::SetRuntimeProfilingStack(JSRuntime *
 }
 
 JS_FRIEND_API(void)
 js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
 {
     rt->spsProfiler.enable(enabled);
 }
 
+JS_FRIEND_API(void)
+js::RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *))
+{
+    JS_ASSERT(rt->spsProfiler.enabled());
+    rt->spsProfiler.setEventMarker(fn);
+}
+
 JS_FRIEND_API(jsbytecode*)
 js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip)
 {
     return rt->spsProfiler.ipToPC(script, size_t(ip));
 }
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -120,16 +120,17 @@ class SPSProfiler
     JSRuntime            *rt;
     ProfileStringMap     strings;
     ProfileEntry         *stack_;
     uint32_t             *size_;
     uint32_t             max_;
     bool                 slowAssertions;
     uint32_t             enabled_;
     PRLock               *lock_;
+    void                (*eventMarker_)(const char *);
 
     const char *allocProfileString(JSScript *script, JSFunction *function);
     void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
     void pushNoCopy(const char *string, void *sp,
                     JSScript *script, jsbytecode *pc) {
         push(string, reinterpret_cast<void*>(
             reinterpret_cast<uintptr_t>(sp) | ProfileEntry::NoCopyBit),
             script, pc);
@@ -186,19 +187,22 @@ class SPSProfiler
 
     /* Enter a C++ function. */
     void enterNative(const char *string, void *sp);
     void exitNative() { pop(); }
 
     jsbytecode *ipToPC(JSScript *script, size_t ip) { return nullptr; }
 
     void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
+    void setEventMarker(void (*fn)(const char *));
     const char *profileString(JSScript *script, JSFunction *maybeFun);
     void onScriptFinalized(JSScript *script);
 
+    void markEvent(const char *event);
+
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount();
     void stringsReset();
 
     uint32_t *addressOfEnabled() {
         return &enabled_;
     }
 };
--- a/tools/profiler/ProfilerMarkers.cpp
+++ b/tools/profiler/ProfilerMarkers.cpp
@@ -138,8 +138,13 @@ IOMarkerPayload::preparePayloadImp(Build
   return data;
 }
 
 template JSCustomObjectBuilder::Object
 IOMarkerPayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
 template JSObjectBuilder::Object
 IOMarkerPayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
 
+void
+ProfilerJSEventMarker(const char *event)
+{
+    PROFILER_MARKER(event);
+}
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -287,16 +287,19 @@ public:
     return &mPendingUWTBuffers;
   }
 
 private:
   UWTBufferLinkedList mPendingUWTBuffers;
   volatile bool       mSignalLock;
 };
 
+// Stub eventMarker function for js-engine event generation.
+void ProfilerJSEventMarker(const char *event);
+
 // the PseudoStack members are read by signal
 // handlers, so the mutation of them needs to be signal-safe.
 struct PseudoStack
 {
 public:
   PseudoStack()
     : mStackPointer(0)
     , mRuntime(nullptr)
@@ -393,16 +396,17 @@ public:
                                  (uint32_t*) &mStackPointer,
                                  uint32_t(mozilla::ArrayLength(mStack)));
     if (mStartJSSampling)
       enableJSSampling();
   }
   void enableJSSampling() {
     if (mRuntime) {
       js::EnableRuntimeProfilingStack(mRuntime, true);
+      js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker);
       mStartJSSampling = false;
     } else {
       mStartJSSampling = true;
     }
   }
   void jsOperationCallback() {
     if (mStartJSSampling)
       enableJSSampling();