Bug 793735 - Make StartupTimeline use TimeStamp instead of PRTime and adjust nsAppStartup to cope with it, r=nfroyd
authorGabriele Svelto <gsvelto@mozilla.com>
Thu, 28 Mar 2013 11:50:16 +0100
changeset 141880 ae636904cf3d0dbb744bf8fc12af2cc85f811bb5
parent 141879 dab96a9565165eeba6f36d6b2f557368228d9089
child 141881 a609b0f445f88513a93d9431ce0813e48c8c8464
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs793735
milestone23.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 793735 - Make StartupTimeline use TimeStamp instead of PRTime and adjust nsAppStartup to cope with it, r=nfroyd
toolkit/components/startup/StartupTimeline.cpp
toolkit/components/startup/StartupTimeline.h
toolkit/components/startup/nsAppStartup.cpp
--- a/toolkit/components/startup/StartupTimeline.cpp
+++ b/toolkit/components/startup/StartupTimeline.cpp
@@ -2,27 +2,29 @@
  * 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 "StartupTimeline.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 
-PRTime StartupTimeline::sStartupTimeline[StartupTimeline::MAX_EVENT_ID];
+TimeStamp StartupTimeline::sStartupTimeline[StartupTimeline::MAX_EVENT_ID];
 const char *StartupTimeline::sStartupTimelineDesc[StartupTimeline::MAX_EVENT_ID] = {
 #define mozilla_StartupTimeline_Event(ev, desc) desc,
 #include "StartupTimeline.h"
 #undef mozilla_StartupTimeline_Event
 };
 
 } /* namespace mozilla */
 
 /**
  * The XRE_StartupTimeline_Record function is to be used by embedding applications
  * that can't use mozilla::StartupTimeline::Record() directly.
  */
 void
 XRE_StartupTimelineRecord(int aEvent, PRTime aWhen)
 {
-  mozilla::StartupTimeline::Record((mozilla::StartupTimeline::Event) aEvent, aWhen);
+  /* FIXME: This effectively becomes a no-op until we fix all the users to
+   * provide proper timestamps */
+  mozilla::StartupTimeline::Record((mozilla::StartupTimeline::Event) aEvent,
+    mozilla::TimeStamp());
 }
-
--- a/toolkit/components/startup/StartupTimeline.h
+++ b/toolkit/components/startup/StartupTimeline.h
@@ -16,17 +16,17 @@ mozilla_StartupTimeline_Event(CREATE_TOP
 mozilla_StartupTimeline_Event(LINKER_INITIALIZED, "linkerInitialized")
 mozilla_StartupTimeline_Event(LIBRARIES_LOADED, "librariesLoaded")
 mozilla_StartupTimeline_Event(FIRST_LOAD_URI, "firstLoadURI")
 #else
 
 #ifndef mozilla_StartupTimeline
 #define mozilla_StartupTimeline
 
-#include "prtime.h"
+#include "mozilla/TimeStamp.h"
 #include "nscore.h"
 #include "GeckoProfiler.h"
 
 #ifdef MOZ_LINKER
 extern "C" {
 /* This symbol is resolved by the custom linker. The function it resolves
  * to dumps some statistics about the linker at the key events recorded
  * by the startup timeline. */
@@ -46,48 +46,48 @@ class StartupTimeline {
 public:
   enum Event {
     #define mozilla_StartupTimeline_Event(ev, z) ev,
     #include "StartupTimeline.h"
     #undef mozilla_StartupTimeline_Event
     MAX_EVENT_ID
   };
 
-  static PRTime Get(Event ev) {
+  static TimeStamp Get(Event ev) {
     return sStartupTimeline[ev];
   }
 
   static const char *Describe(Event ev) {
     return sStartupTimelineDesc[ev];
   }
 
   static void Record(Event ev) {
     PROFILER_MARKER(Describe(ev));
-    Record(ev, PR_Now());
+    Record(ev, TimeStamp::Now());
   }
 
-  static void Record(Event ev, PRTime when) {
+  static void Record(Event ev, TimeStamp when) {
     sStartupTimeline[ev] = when;
 #ifdef MOZ_LINKER
     if (__moz_linker_stats)
       __moz_linker_stats(Describe(ev));
 #endif
   }
 
   static void RecordOnce(Event ev) {
     if (!HasRecord(ev))
       Record(ev);
   }
 
   static bool HasRecord(Event ev) {
-    return sStartupTimeline[ev];
+    return !sStartupTimeline[ev].IsNull();
   }
 
 private:
-  static NS_EXTERNAL_VIS_(PRTime) sStartupTimeline[MAX_EVENT_ID];
+  static NS_EXTERNAL_VIS_(TimeStamp) sStartupTimeline[MAX_EVENT_ID];
   static NS_EXTERNAL_VIS_(const char *) sStartupTimelineDesc[MAX_EVENT_ID];
 };
 
 }
 
 #endif /* mozilla_StartupTimeline */
 
 #endif /* mozilla_StartupTimeline_Event */
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -23,72 +23,35 @@
 #include "nsNativeCharsetUtils.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsStringGlue.h"
 #include "mozilla/Preferences.h"
 #include "GeckoProfiler.h"
 
 #include "prprf.h"
-#include "nsCRT.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsAppShellCID.h"
 #include "nsXPCOMCIDInternal.h"
 #include "mozilla/Services.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "prenv.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "mozilla/mozPoisonWrite.h"
 
 #if defined(XP_WIN)
-#include <windows.h>
-// windows.h can go to hell 
+// Prevent collisions with nsAppStartup::GetStartupInfo()
 #undef GetStartupInfo
-#elif defined(XP_UNIX)
-#include <unistd.h>
-#include <sys/syscall.h>
-#endif
-
-#if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
-  || defined(__NetBSD__) || defined(__OpenBSD__)
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#endif
-
-#if defined(__DragonFly__) || defined(__FreeBSD__)
-#include <sys/user.h>
 #endif
 
 #include "mozilla/Telemetry.h"
 #include "mozilla/StartupTimeline.h"
 
-#if defined(__NetBSD__)
-#undef KERN_PROC
-#define KERN_PROC KERN_PROC2
-#define KINFO_PROC struct kinfo_proc2
-#else
-#define KINFO_PROC struct kinfo_proc
-#endif
-
-#if defined(XP_MACOSX)
-#define KP_START_SEC kp_proc.p_un.__p_starttime.tv_sec
-#define KP_START_USEC kp_proc.p_un.__p_starttime.tv_usec
-#elif defined(__DragonFly__)
-#define KP_START_SEC kp_start.tv_sec
-#define KP_START_USEC kp_start.tv_usec
-#elif defined(__FreeBSD__)
-#define KP_START_SEC ki_start.tv_sec
-#define KP_START_USEC ki_start.tv_usec
-#else
-#define KP_START_SEC p_ustart_sec
-#define KP_START_USEC p_ustart_usec
-#endif
-
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #define kPrefLastSuccess "toolkit.startup.last_success"
 #define kPrefMaxResumedCrashes "toolkit.startup.max_resumed_crashes"
 #define kPrefRecentCrashes "toolkit.startup.recent_crashes"
 #define kPrefAlwaysUseSafeMode "toolkit.startup.always_use_safe_mode"
 
 #if defined(XP_WIN)
@@ -146,16 +109,33 @@ public:
 
     // We're done "shutting down".
     mService->mShuttingDown = false;
     mService->mRunning = false;
     return NS_OK;
   }
 };
 
+/**
+ * Computes an approximation of the absolute time represented by @a stamp
+ * which is comparable to those obtained via PR_Now(). If the current absolute
+ * time varies a lot (e.g. DST adjustments) since the first call then the
+ * resulting times may be inconsistent.
+ *
+ * @param stamp The timestamp to be converted
+ * @returns The converted timestamp
+ */
+uint64_t ComputeAbsoluteTimestamp(PRTime prnow, TimeStamp now, TimeStamp stamp)
+{
+  static PRTime sAbsoluteNow = PR_Now();
+  static TimeStamp sMonotonicNow = TimeStamp::Now();
+
+  return sAbsoluteNow - (sMonotonicNow - stamp).ToMicroseconds();
+}
+
 //
 // nsAppStartup
 //
 
 nsAppStartup::nsAppStartup() :
   mConsiderQuitStopper(0),
   mRunning(false),
   mShuttingDown(false),
@@ -378,18 +358,19 @@ nsAppStartup::Quit(uint32_t aMode)
     mozilla::RecordShutdownStartTimeStamp();
     mShuttingDown = true;
     if (!mRestart) {
       mRestart = (aMode & eRestart) != 0;
       gRestartMode = (aMode & 0xF0);
     }
 
     if (mRestart) {
-      // Firefox-restarts reuse the process. Process start-time isn't a useful indicator of startup time
-      PR_SetEnv(PR_smprintf("MOZ_APP_RESTART=%lld", (int64_t) PR_Now() / PR_USEC_PER_MSEC));
+      /* Firefox-restarts reuse the process so regular process start-time isn't
+         a useful indicator of startup time anymore. */
+      TimeStamp::RecordProcessRestart();
     }
 
     obsService = mozilla::services::GetObserverService();
 
     if (!mAttemptingQuit) {
       mAttemptingQuit = true;
 #ifdef XP_MACOSX
       // now even the Mac wants to quit when the last window is closed
@@ -661,170 +642,63 @@ nsAppStartup::Observe(nsISupports *aSubj
 #endif //defined(XP_WIN)
   } else {
     NS_ERROR("Unexpected observer topic.");
   }
 
   return NS_OK;
 }
 
-#if defined(LINUX) || defined(ANDROID)
-static uint64_t 
-JiffiesSinceBoot(const char *file)
-{
-  char stat[512];
-  FILE *f = fopen(file, "r");
-  if (!f)
-    return 0;
-  int n = fread(&stat, 1, sizeof(stat) - 1, f);
-  fclose(f);
-  if (n <= 0)
-    return 0;
-  stat[n] = 0;
-  
-  long long unsigned starttime = 0; // instead of uint64_t to keep GCC quiet
-  
-  char *s = strrchr(stat, ')');
-  if (!s)
-    return 0;
-  int ret = sscanf(s + 2,
-                   "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
-                   "%*u %*u %*u %*u %*u %*d %*d %*d %*d %llu",
-                   &starttime);
-  if (ret != 1 || !starttime)
-    return 0;
-  return starttime;
-}
-
-static void
-ThreadedCalculateProcessCreationTimestamp(void *aClosure)
-{
-  PR_SetCurrentThreadName("Startup Timer");
-
-  PRTime now = PR_Now();
-  long hz = sysconf(_SC_CLK_TCK);
-  if (!hz)
-    return;
-
-  char thread_stat[40];
-  sprintf(thread_stat, "/proc/self/task/%d/stat", (pid_t) syscall(__NR_gettid));
-  
-  uint64_t thread_jiffies = JiffiesSinceBoot(thread_stat);
-  uint64_t self_jiffies = JiffiesSinceBoot("/proc/self/stat");
-  
-  if (!thread_jiffies || !self_jiffies)
-    return;
-
-  PRTime interval = (thread_jiffies - self_jiffies) * PR_USEC_PER_SEC / hz;
-  StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, now - interval);
-}
-
-static PRTime
-CalculateProcessCreationTimestamp()
-{
- PRThread *thread = PR_CreateThread(PR_USER_THREAD,
-                                    ThreadedCalculateProcessCreationTimestamp,
-                                    NULL,
-                                    PR_PRIORITY_NORMAL,
-                                    PR_LOCAL_THREAD,
-                                    PR_JOINABLE_THREAD,
-                                    0);
-
-  PR_JoinThread(thread);
-  return StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
-}
-#elif defined(XP_WIN)
-static PRTime
-CalculateProcessCreationTimestamp()
-{
-  FILETIME start, foo, bar, baz;
-  bool success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
-  if (!success)
-    return 0;
-  // copied from NSPR _PR_FileTimeToPRTime
-  uint64_t timestamp = 0;
-  CopyMemory(&timestamp, &start, sizeof(PRTime));
-#ifdef __GNUC__
-  timestamp = (timestamp - 116444736000000000LL) / 10LL;
-#else
-  timestamp = (timestamp - 116444736000000000i64) / 10i64;
-#endif
-  return timestamp;
-}
-#elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
-  || defined(__NetBSD__) || defined(__OpenBSD__)
-static PRTime
-CalculateProcessCreationTimestamp()
-{
-  int mib[] = {
-    CTL_KERN,
-    KERN_PROC,
-    KERN_PROC_PID,
-    getpid(),
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-    sizeof(KINFO_PROC),
-    1,
-#endif
-  };
-  u_int miblen = sizeof(mib) / sizeof(mib[0]);
-
-  KINFO_PROC proc;
-  size_t buffer_size = sizeof(proc);
-  if (sysctl(mib, miblen, &proc, &buffer_size, NULL, 0))
-    return 0;
-
-  PRTime starttime = static_cast<PRTime>(proc.KP_START_SEC) * PR_USEC_PER_SEC;
-  starttime += proc.KP_START_USEC;
-  return starttime;
-}
-#else
-static PRTime
-CalculateProcessCreationTimestamp()
-{
-  return 0;
-}
-#endif
-
 NS_IMETHODIMP
 nsAppStartup::GetStartupInfo(JSContext* aCx, JS::Value* aRetval)
 {
   JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, NULL, NULL, NULL));
   *aRetval = OBJECT_TO_JSVAL(obj);
 
-  PRTime ProcessCreationTimestamp = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
+  TimeStamp procTime = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
+  TimeStamp now = TimeStamp::Now();
+  PRTime absNow = PR_Now();
+
+  if (procTime.IsNull()) {
+    bool error = false;
 
-  if (!ProcessCreationTimestamp) {
-    PRTime MainTimestamp = StartupTimeline::Get(StartupTimeline::MAIN);
-    char *moz_app_restart = PR_GetEnv("MOZ_APP_RESTART");
-    if (moz_app_restart) {
-      ProcessCreationTimestamp = nsCRT::atoll(moz_app_restart) * PR_USEC_PER_MSEC;
-    } else {
-      ProcessCreationTimestamp = CalculateProcessCreationTimestamp();
+    procTime = TimeStamp::ProcessCreation(error);
+
+    if (error) {
+      Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
+        StartupTimeline::PROCESS_CREATION);
     }
-    // Bug 670008 & 689256: Avoid obviously invalid process creation times
-    if ((PR_Now() <= ProcessCreationTimestamp) ||
-        (MainTimestamp && (ProcessCreationTimestamp > MainTimestamp)))
-    {
-      ProcessCreationTimestamp = MainTimestamp ? MainTimestamp : -1;
-      Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, StartupTimeline::PROCESS_CREATION);
-    }
-    StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, ProcessCreationTimestamp);
+
+    StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, procTime);
   }
 
-  for (int i = StartupTimeline::PROCESS_CREATION; i < StartupTimeline::MAX_EVENT_ID; ++i) {
+  for (int i = StartupTimeline::PROCESS_CREATION;
+       i < StartupTimeline::MAX_EVENT_ID;
+       ++i)
+  {
     StartupTimeline::Event ev = static_cast<StartupTimeline::Event>(i);
-    if (StartupTimeline::Get(ev) > 0) {
-      // always define main to aid with bug 689256
-      if ((ev != StartupTimeline::MAIN) &&
-          (StartupTimeline::Get(ev) < StartupTimeline::Get(StartupTimeline::PROCESS_CREATION))) {
-        Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, i);
-        StartupTimeline::Record(ev, -1);
+    TimeStamp stamp = StartupTimeline::Get(ev);
+
+    if (stamp.IsNull() && (ev == StartupTimeline::MAIN)) {
+      // Always define main to aid with bug 689256.
+      stamp = procTime;
+      MOZ_ASSERT(!stamp.IsNull());
+      Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
+        StartupTimeline::MAIN);
+    }
+
+    if (!stamp.IsNull()) {
+      if (stamp >= procTime) {
+        PRTime prStamp = ComputeAbsoluteTimestamp(absNow, now, stamp)
+          / PR_USEC_PER_MSEC;
+        JS::Rooted<JSObject*> date(aCx, JS_NewDateObjectMsec(aCx, prStamp));
+        JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev),
+          OBJECT_TO_JSVAL(date), NULL, NULL, JSPROP_ENUMERATE);
       } else {
-        JSObject *date = JS_NewDateObjectMsec(aCx, StartupTimeline::Get(ev) / PR_USEC_PER_MSEC);
-        JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), OBJECT_TO_JSVAL(date), NULL, NULL, JSPROP_ENUMERATE);
+        Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, ev);
       }
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -962,24 +836,31 @@ nsAppStartup::TrackStartupCrashEnd()
   if (mStartupCrashTrackingEnded || (mIsSafeModeNecessary && !inSafeMode))
     return NS_OK;
   mStartupCrashTrackingEnded = true;
 
   StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_END);
 
   // Use the timestamp of XRE_main as an approximation for the lock file timestamp.
   // See MAX_STARTUP_BUFFER for the buffer time period.
+  TimeStamp mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
+  TimeStamp now = TimeStamp::Now();
+  PRTime prNow = PR_Now();
   nsresult rv;
-  PRTime mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
-  if (mainTime <= 0) {
+
+  if (mainTime.IsNull()) {
     NS_WARNING("Could not get StartupTimeline::MAIN time.");
   } else {
-    int32_t lockFileTime = (int32_t)(mainTime / PR_USEC_PER_SEC);
-    rv = Preferences::SetInt(kPrefLastSuccess, lockFileTime);
-    if (NS_FAILED(rv)) NS_WARNING("Could not set startup crash detection pref.");
+    uint64_t lockFileTime = ComputeAbsoluteTimestamp(prNow, now, mainTime);
+
+    rv = Preferences::SetInt(kPrefLastSuccess,
+      (int32_t)(lockFileTime / PR_USEC_PER_SEC));
+
+    if (NS_FAILED(rv))
+      NS_WARNING("Could not set startup crash detection pref.");
   }
 
   if (inSafeMode && mIsSafeModeNecessary) {
     // On a successful startup in automatic safe mode, allow the user one more crash
     // in regular mode before returning to safe mode.
     int32_t maxResumedCrashes = 0;
     int32_t prefType;
     rv = Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes, &prefType);