Bug 793735 - Make StartupTimeline use TimeStamp instead of PRTime and adjust nsAppStartup to cope with it, r=froydnj
authorGabriele Svelto <gsvelto@mozilla.com>
Thu, 28 Mar 2013 11:50:16 +0100
changeset 127943 77014412cd4a112a68a6f30d4e840033710a561d
parent 127942 8c5aa269c3cd94c773b5e95a0f886293ddbb637e
child 127944 61e1edc0b6bff52d690cbdca4e888f4ab38172a3
push id26047
push userphilringnalda@gmail.com
push dateSun, 07 Apr 2013 19:47:09 +0000
treeherdermozilla-inbound@61e1edc0b6bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
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=froydnj
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"
 
 #if defined(XP_WIN)
 #include "mozilla/perfprobe.h"
@@ -145,16 +108,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),
@@ -377,18 +357,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
@@ -660,170 +641,62 @@ 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)
 {
   JSObject *obj = 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);
+        JSObject *date = JS_NewDateObjectMsec(aCx, prStamp / PR_USEC_PER_MSEC);
+        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
@@ -950,24 +823,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);