Bug 976260 - Register javascript performance events with the profiler. r=jandem
☠☠ backed out by ae723aa8b528 ☠ ☠
authorKannan Vijayan <kvijayan@mozilla.com>
Fri, 28 Feb 2014 13:22:03 -0500
changeset 171598 84663a25b44e95e508832b3e4f1b79e84a1b4a0f
parent 171597 ebf4f12001ea43284cfc656934634eb6bc322d1f
child 171599 ae723aa8b5286abafb567e5254641af57d276e8a
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersjandem
bugs976260
milestone30.0a1
Bug 976260 - Register javascript performance events with the profiler. 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
@@ -970,24 +970,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
@@ -6221,16 +6221,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
@@ -4270,16 +4270,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."),
@@ -4610,16 +4626,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;
 
     /*
@@ -326,13 +333,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,26 @@ 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) {
+        JS_ASSERT(enabled());
+        if (eventMarker_)
+            eventMarker_(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();