Bug 1181175 - Telemetry for finding out how often our process is rescheduled to another CPU. r=jandem, r=bsmedberg
☠☠ backed out by 3ccebe8a8e61 ☠ ☠
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Wed, 29 Jul 2015 19:01:05 +0200
changeset 288460 21b660154b4a7388fdb68a456f9b5c7b874d1000
parent 288459 0a92886f497a3f79023195fb9e35a0a302c0f285
child 288461 3ccebe8a8e612e96054f56d4b10e55a054fc1b65
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, bsmedberg
bugs1181175
milestone42.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 1181175 - Telemetry for finding out how often our process is rescheduled to another CPU. r=jandem, r=bsmedberg CLOSED TREE
js/src/jsapi.h
js/src/vm/Interpreter.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/nsPerformanceStats.h
toolkit/components/telemetry/Histograms.json
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5732,16 +5732,21 @@ GetStopwatchIsMonitoringJank(JSRuntime*)
 extern JS_PUBLIC_API(bool)
 SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool);
 extern JS_PUBLIC_API(bool)
 GetStopwatchIsMonitoringPerCompartment(JSRuntime*);
 
 extern JS_PUBLIC_API(bool)
 IsStopwatchActive(JSRuntime*);
 
+// Extract the CPU rescheduling data.
+extern JS_PUBLIC_API(void)
+GetPerfMonitoringTestCpuRescheduling(JSRuntime*, uint64_t* stayed, uint64_t* moved);
+
+
 /**
  * Add a number of microseconds to the time spent waiting on CPOWs
  * since process start.
  */
 extern JS_PUBLIC_API(void)
 AddCPOWPerformanceDelta(JSRuntime*, uint64_t delta);
 
 typedef bool
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -52,18 +52,18 @@
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 #if defined(XP_WIN)
-#include <Windows.h>
-#include <Processthreadsapi.h>
+#include <processthreadsapi.h>
+#include <windows.h>
 #endif // defined(XP_WIN)
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::NumberEqualsInt32;
@@ -391,17 +391,17 @@ class AutoStopwatch final
     bool isMonitoringCPOW_;
 
     // Timestamps captured while starting the stopwatch.
     uint64_t cyclesStart_;
     uint64_t CPOWTimeStart_;
 
     // The CPU on which we started the measure. Defined only
     // if `isMonitoringJank_` is `true`.
-#if defined(XP_WIN) && _WIN32_WINNT >= 0x0601
+#if defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA
     struct cpuid_t {
         WORD group_;
         BYTE number_;
         cpuid_t(WORD group, BYTE number)
           : group_(group),
             number_(number)
         { }
         cpuid_t()
@@ -526,16 +526,22 @@ class AutoStopwatch final
             // `getCPU()`.  We hope that this will happen rarely
             // enough that the impact on our statistics will remain
             // limited.
             const cpuid_t cpuEnd = this->getCPU();
             if (isSameCPU(cpuStart_, cpuEnd)) {
                 const uint64_t cyclesEnd = getCycles();
                 cyclesDelta = getDelta(cyclesEnd, cyclesStart_);
             }
+#if (defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA) || defined(XP_LINUX)
+            if (isSameCPU(cpuStart_, cpuEnd))
+                runtime->stopwatch.testCpuRescheduling.stayed += 1;
+            else
+                runtime->stopwatch.testCpuRescheduling.moved += 1;
+#endif // defined(XP_WIN) || defined(XP_LINUX)
         }
 
         uint64_t CPOWTimeDelta = 0;
         if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) {
             // We were monitoring CPOW when we entered and we still are.
             const uint64_t CPOWTimeEnd = runtime->stopwatch.totalCPOWTime;
             CPOWTimeDelta = getDelta(CPOWTimeEnd, CPOWTimeStart_);
 
@@ -616,33 +622,33 @@ class AutoStopwatch final
 #endif // defined(MOZ_HAVE_RDTSC)
     }
 
 
     // Return the identifier of the current CPU, on platforms for which we have
     // access to the current CPU.
     cpuid_t inline getCPU() const
     {
-#if defined(XP_WIN)
+#if defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA
         PROCESSOR_NUMBER proc;
         GetCurrentProcessorNumberEx(&proc);
 
         cpuid_t result(proc.Group, proc.Number);
         return result;
 #elif defined(XP_LINUX)
         return sched_getcpu();
 #else
         return {};
 #endif // defined(XP_WIN) || defined(XP_LINUX)
     }
 
     // Compare two CPU identifiers.
     bool inline isSameCPU(const cpuid_t& a, const cpuid_t& b) const
     {
-#if defined(XP_WIN)
+#if defined(XP_WIN)  && WINVER >= _WIN32_WINNT_VISTA
         return a.group_ == b.group_ && a.number_ == b.number_;
 #elif defined(XP_LINUX)
         return a == b;
 #else
         return true;
 #endif
     }
  private:
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -1203,16 +1203,23 @@ js::SetStopwatchIsMonitoringPerCompartme
     return rt->stopwatch.setIsMonitoringPerCompartment(value);
 }
 bool
 js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt)
 {
     return rt->stopwatch.isMonitoringPerCompartment();
 }
 
+void
+js::GetPerfMonitoringTestCpuRescheduling(JSRuntime* rt, uint64_t* stayed, uint64_t* moved)
+{
+    *stayed = rt->stopwatch.testCpuRescheduling.stayed;
+    *moved = rt->stopwatch.testCpuRescheduling.moved;
+}
+
 js::PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
 void*
 js::PerformanceGroupHolder::getHashKey(JSContext* cx)
 {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1675,16 +1675,43 @@ struct JSRuntime : public JS::shadow::Ru
         void addChangedGroup(js::PerformanceGroup* group) {
             MOZ_ASSERT(group->recentTicks == 0);
             touchedGroups.append(group);
         }
 
         // The total amount of time spent waiting on CPOWs since the
         // start of the process, in microseconds.
         uint64_t totalCPOWTime;
+
+        // Data extracted by the AutoStopwatch to determine how often
+        // we reschedule the process to a different CPU during the
+        // execution of JS.
+        //
+        // Warning: These values are incremented *only* on platforms
+        // that offer a syscall/libcall to check on which CPU a
+        // process is currently executed.
+        struct TestCpuRescheduling
+        {
+            // Incremented once we have finished executing code
+            // in a group, if the CPU on which we started
+            // execution is the same as the CPU on which
+            // we finished.
+            uint64_t stayed;
+            // Incremented once we have finished executing code
+            // in a group, if the CPU on which we started
+            // execution is different from the CPU on which
+            // we finished.
+            uint64_t moved;
+            TestCpuRescheduling()
+              : stayed(0),
+                moved(0)
+            { }
+        };
+        TestCpuRescheduling testCpuRescheduling;
+
     private:
         Stopwatch(const Stopwatch&) = delete;
         Stopwatch& operator=(const Stopwatch&) = delete;
 
         // Commit a piece of data to a single group.
         // `totalUserTimeDelta`, `totalSystemTimeDelta`, `totalCyclesDelta`
         // represent the outer measures, taken for the entire runtime.
         void transferDeltas(uint64_t totalUserTimeDelta,
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -16,16 +16,20 @@
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "xpcpublic.h"
 #include "jspubtd.h"
 
 #include "nsIDOMWindow.h"
 #include "nsGlobalWindow.h"
 
+#include "mozilla/unused.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+
 #if defined(XP_WIN)
 #include "windows.h"
 #else
 #include <unistd.h>
 #endif
 
 class nsPerformanceStats: public nsIPerformanceStats {
 public:
@@ -435,25 +439,31 @@ NS_IMETHODIMP nsPerformanceSnapshot::Get
 
 NS_IMETHODIMP nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess)
 {
   NS_IF_ADDREF(*aProcess = mProcessData);
   return NS_OK;
 }
 
 
-NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService)
+NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService, nsIObserver)
 
 nsPerformanceStatsService::nsPerformanceStatsService()
 #if defined(XP_WIN)
   : mProcessId(GetCurrentProcessId())
 #else
   : mProcessId(getpid())
 #endif
+  , mProcessStayed(0)
+  , mProcessMoved(0)
 {
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    mozilla::unused << obs->AddObserver(this, "profile-before-shutdown", false);
+  }
 }
 
 nsPerformanceStatsService::~nsPerformanceStatsService()
 {
 }
 
 //[implicit_jscontext] attribute bool isMonitoringCPOW;
 NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
@@ -502,13 +512,33 @@ NS_IMETHODIMP nsPerformanceStatsService:
 NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
 {
   nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
   nsresult rv = snapshot->Init(cx, mProcessId);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  js::GetPerfMonitoringTestCpuRescheduling(JS_GetRuntime(cx), &mProcessStayed, &mProcessMoved);
   snapshot.forget(aSnapshot);
+
   return NS_OK;
 }
 
 
+/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
+NS_IMETHODIMP nsPerformanceStatsService::Observe(nsISupports *, const char *, const char16_t *)
+{
+  // Upload telemetry
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    mozilla::unused << obs->RemoveObserver(this, "profile-before-shutdown");
+  }
+
+  if (mProcessStayed + mProcessMoved == 0) {
+    // Nothing to report.
+    return NS_OK;
+  }
+  const uint32_t proportion = ( 100 * mProcessStayed ) / ( mProcessStayed + mProcessMoved );
+  mozilla::Telemetry::Accumulate("PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED", proportion);
+
+  return NS_OK;
+}
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.h
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.h
@@ -1,26 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #ifndef nsPerformanceStats_h
 #define nsPerformanceStats_h
 
+#include "nsIObserver.h"
+
 #include "nsIPerformanceStats.h"
 
-class nsPerformanceStatsService : public nsIPerformanceStatsService
+class nsPerformanceStatsService : public nsIPerformanceStatsService, nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERFORMANCESTATSSERVICE
+  NS_DECL_NSIOBSERVER
 
   nsPerformanceStatsService();
 
 private:
   virtual ~nsPerformanceStatsService();
 
   const uint64_t mProcessId;
+  uint64_t mProcessStayed;
+  uint64_t mProcessMoved;
 protected:
 };
 
 #endif
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8666,10 +8666,18 @@
     "n_values": 10,
     "description": "Reports a comparison between row count of original and re-migration of the v7 permissions DB. 0=New == 0, 1=New < Old, 2=New == Old, 3=New > Old"
   },
   "PERMISSIONS_MIGRATION_7_ERROR": {
     "alert_emails": ["michael@thelayzells.com"],
     "expires_in_version": "44",
     "kind": "boolean",
     "description": "Was there an error while performing the v7 permissions DB migration?"
+  },
+  "PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED": {
+    "alert_emails": ["dteller@mozilla.com"],
+    "expires_in_version": "44",
+    "kind": "linear",
+    "high": "100",
+    "n_buckets": "20",
+    "description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank."
   }
 }