Bug 793735 - Make XRE_StartupTimelineRecord() generate TimeStamps and modify its callers to use the appropriate timers, r=froydnj
authorGabriele Svelto <gsvelto@mozilla.com>
Tue, 26 Mar 2013 11:31:20 +0100
changeset 127944 61e1edc0b6bff52d690cbdca4e888f4ab38172a3
parent 127943 77014412cd4a112a68a6f30d4e840033710a561d
child 127945 0608d181dac61f93e7fe58d04574cb6621b73f14
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 XRE_StartupTimelineRecord() generate TimeStamps and modify its callers to use the appropriate timers, r=froydnj
browser/app/Makefile.in
browser/app/nsBrowserApp.cpp
mozglue/android/APKOpen.cpp
toolkit/components/startup/StartupTimeline.cpp
toolkit/components/startup/StartupTimeline.h
xpcom/ds/TimeStamp.h
xpcom/ds/TimeStamp_windows.h
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -61,16 +61,20 @@ LIBS += \
 	$(EXTRA_DSO_LIBS) \
 	$(XPCOM_STANDALONE_GLUE_LDOPTS) \
 	$(NULL)
 
 ifdef MOZ_LINKER
 LIBS += $(MOZ_ZLIB_LIBS)
 endif
 
+ifdef HAVE_CLOCK_MONOTONIC
+LIBS += $(REALTIME_LIBS)
+endif
+
 ifndef MOZ_WINCONSOLE
 ifdef MOZ_DEBUG
 MOZ_WINCONSOLE = 1
 else
 MOZ_WINCONSOLE = 0
 endif
 endif
 
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -8,22 +8,23 @@
 #include "application.ini.h"
 #include "nsXPCOMGlue.h"
 #if defined(XP_WIN)
 #include <windows.h>
 #include <stdlib.h>
 #include <io.h>
 #include <fcntl.h>
 #elif defined(XP_UNIX)
-#include <sys/time.h>
 #include <sys/resource.h>
+#include <time.h>
 #include <unistd.h>
 #endif
 
 #ifdef XP_MACOSX
+#include <mach/mach_time.h>
 #include "MacQuirks.h"
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <time.h>
 
 #include "nsCOMPtr.h"
@@ -383,35 +384,90 @@ static int do_main(int argc, char* argv[
   XRE_FreeAppData(appData);
   return result;
 #endif
 
   NS_NOTREACHED("browser do_main failed to pickup proper initialization");
   return 255;
 }
 
-/* Local implementation of PR_Now, since the executable can't depend on NSPR */
-static PRTime _PR_Now()
+#ifdef XP_WIN
+
+/**
+ * Used only when GetTickCount64 is not available on the platform.
+ * Last result of GetTickCount call. Kept in [ms].
+ */
+static DWORD sLastGTCResult = 0;
+
+/**
+ *  Higher part of the 64-bit value of MozGetTickCount64,
+ * incremented atomically.
+ */
+static DWORD sLastGTCRollover = 0;
+
+/**
+ * Function protecting GetTickCount result from rolling over. The original
+ * code comes from the Windows implementation of the TimeStamp class minus the
+ * locking harness which isn't needed here.
+ *
+ * @returns The current time in milliseconds
+ */
+static ULONGLONG WINAPI
+MozGetTickCount64()
+{
+  DWORD GTC = ::GetTickCount();
+
+  /* Pull the rollover counter forward only if new value of GTC goes way
+   * down under the last saved result */
+  if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30)))
+    ++sLastGTCRollover;
+
+  sLastGTCResult = GTC;
+  return (ULONGLONG)sLastGTCRollover << 32 | sLastGTCResult;
+}
+
+typedef ULONGLONG (WINAPI* GetTickCount64_t)();
+static GetTickCount64_t sGetTickCount64 = nullptr;
+
+#endif
+
+/**
+ * Local TimeStamp::Now()-compatible implementation used to record timestamps
+ * which will be passed to XRE_StartupTimelineRecord().
+ */
+static uint64_t
+TimeStamp_Now()
 {
 #ifdef XP_WIN
-  MOZ_STATIC_ASSERT(sizeof(PRTime) == sizeof(FILETIME), "PRTime must have the same size as FILETIME");
-  FILETIME ft;
-  GetSystemTimeAsFileTime(&ft);
-  PRTime now;
-  CopyMemory(&now, &ft, sizeof(PRTime));
-#ifdef __GNUC__
-  return (now - 116444736000000000LL) / 10LL;
-#else
-  return (now - 116444736000000000i64) / 10i64;
-#endif
+  LARGE_INTEGER freq;
+  ::QueryPerformanceFrequency(&freq);
+
+  HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
+  sGetTickCount64 = reinterpret_cast<GetTickCount64_t>
+    (GetProcAddress(kernelDLL, "GetTickCount64"));
+
+  if (!sGetTickCount64) {
+    /* If the platform does not support the GetTickCount64 (Windows XP doesn't),
+     * then use our fallback implementation based on GetTickCount. */
+    sGetTickCount64 = MozGetTickCount64;
+  }
 
-#else
-  struct timeval tm;
-  gettimeofday(&tm, 0);
-  return (((PRTime)tm.tv_sec * 1000000LL) + (PRTime)tm.tv_usec);
+  return sGetTickCount64() * freq.QuadPart;
+#elif defined(XP_MACOSX)
+  return mach_absolute_time();
+#elif defined(HAVE_CLOCK_MONOTONIC)
+  struct timespec ts;
+  int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+  if (rv != 0) {
+    return 0;
+  }
+
+  uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
+  return baseNs + (uint64_t)ts.tv_nsec;
 #endif
 }
 
 static bool
 FileExists(const char *path)
 {
 #ifdef XP_WIN
   wchar_t wideDir[MAX_PATH];
@@ -523,17 +579,17 @@ InitXPCOMGlue(const char *argv0, nsIFile
   return rv;
 }
 
 int main(int argc, char* argv[])
 {
 #ifdef DEBUG_delay_start_metro
   Sleep(5000);
 #endif
-  PRTime start = _PR_Now();
+  uint64_t start = TimeStamp_Now();
 
 #ifdef XP_MACOSX
   TriggerQuirks();
 #endif
 
   int gotCounters;
 #if defined(XP_UNIX)
   struct rusage initialRUsage;
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -6,25 +6,25 @@
  * This custom library loading code is only meant to be called
  * during initialization. As a result, it takes no special 
  * precautions to be threadsafe. Any of the library loading functions
  * like mozload should not be available to other code.
  */
 
 #include <jni.h>
 #include <android/log.h>
-#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <sys/limits.h>
 #include <errno.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <zlib.h>
 #include "dlfcn.h"
 #include "APKOpen.h"
 #include <sys/time.h>
 #include <sys/resource.h>
 #include "Zip.h"
@@ -51,32 +51,42 @@ extern "C" {
  * would fix the original problem. On older NDKs, it is not a problem
  * either because the way __dso_handle was used was already broken (and
  * the custom linker works around it).
  */
   NS_EXPORT __attribute__((weak)) void *__dso_handle;
 }
 
 typedef int mozglueresult;
-typedef int64_t MOZTime;
 
 enum StartupEvent {
 #define mozilla_StartupTimeline_Event(ev, z) ev,
 #include "StartupTimeline.h"
 #undef mozilla_StartupTimeline_Event
   MAX_STARTUP_EVENT_ID
 };
 
 using namespace mozilla;
 
-static MOZTime MOZ_Now()
+/**
+ * Local TimeStamp::Now()-compatible implementation used to record timestamps
+ * which will be passed to XRE_StartupTimelineRecord().
+ */
+
+static uint64_t TimeStamp_Now()
 {
-  struct timeval tm;
-  gettimeofday(&tm, 0);
-  return (((MOZTime)tm.tv_sec * 1000000LL) + (MOZTime)tm.tv_usec);
+  struct timespec ts;
+  int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+  if (rv != 0) {
+    return 0;
+  }
+
+  uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
+  return baseNs + (uint64_t)ts.tv_nsec;
 }
 
 static struct mapping_info * lib_mapping = NULL;
 
 NS_EXPORT const struct mapping_info *
 getLibraryMapping()
 {
   return lib_mapping;
@@ -209,17 +219,17 @@ report_mapping(char *name, void *base, u
     info->file_id = strndup(entry + strlen(name) + 1, 32);
 }
 
 static mozglueresult
 loadGeckoLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
-  MOZTime t0 = MOZ_Now();
+  uint64_t t0 = TimeStamp_Now();
   struct rusage usage1;
   getrusage(RUSAGE_THREAD, &usage1);
   
   RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
 
 #ifdef MOZ_CRASHREPORTER
   file_ids = (char *)extractBuf("lib.id", zip);
 #endif
@@ -238,20 +248,20 @@ loadGeckoLibs(const char *apkName)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
     return FAILURE;
   }
 
 #define JNI_BINDINGS
 #include "jni-stubs.inc"
 #undef JNI_BINDINGS
 
-  void (*XRE_StartupTimelineRecord)(int, MOZTime);
+  void (*XRE_StartupTimelineRecord)(int, uint64_t);
   xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord);
 
-  MOZTime t1 = MOZ_Now();
+  uint64_t t1 = TimeStamp_Now();
   struct rusage usage2;
   getrusage(RUSAGE_THREAD, &usage2);
 
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %lldms total, %ldms user, %ldms system, %ld faults",
                       (t1 - t0) / 1000,
                       (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000,
                       (usage2.ru_stime.tv_sec - usage1.ru_stime.tv_sec)*1000 + (usage2.ru_stime.tv_usec - usage1.ru_stime.tv_usec)/1000,
                       usage2.ru_majflt-usage1.ru_majflt);
--- a/toolkit/components/startup/StartupTimeline.cpp
+++ b/toolkit/components/startup/StartupTimeline.cpp
@@ -1,30 +1,67 @@
 /* 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 "StartupTimeline.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 
 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
 };
 
+/**
+ * Implementation of XRE_StartupTimelineRecord()
+ *
+ * @param aEvent Same as XRE_StartupTimelineRecord() equivalent argument
+ * @param aWhen  Same as XRE_StartupTimelineRecord() equivalent argument
+ */
+void
+StartupTimelineRecordExternal(int aEvent, uint64_t aWhen)
+{
+#if XP_WIN
+  TimeStamp ts = TimeStampValue(aWhen, 0, 0);
+#else
+  TimeStamp ts = TimeStampValue(aWhen);
+#endif
+  bool error = false;
+
+  // Since the timestamp comes from an external source validate it before
+  // recording it and log a telemetry error if it appears inconsistent.
+  if (ts < TimeStamp::ProcessCreation(error)) {
+    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
+      (StartupTimeline::Event)aEvent);
+  } else {
+    StartupTimeline::Record((StartupTimeline::Event)aEvent, ts);
+  }
+}
+
 } /* namespace mozilla */
 
 /**
- * The XRE_StartupTimeline_Record function is to be used by embedding applications
- * that can't use mozilla::StartupTimeline::Record() directly.
+ * The XRE_StartupTimeline_Record function is to be used by embedding
+ * applications that can't use mozilla::StartupTimeline::Record() directly.
+ *
+ * It can create timestamps from arbitrary time values and sanitizies them to
+ * ensure that they are not inconsistent with those captured using monotonic
+ * timers. The value of aWhen must have been captured using the same timer
+ * used by the platform's mozilla::TimeStamp implementation. Erroneous values
+ * will be flagged as telemetry errors.
+ *
+ * @param aEvent The event to be recorded, must correspond to an element of the
+ *               mozilla::StartupTimeline::Event enumartion
+ * @param aWhen  The time at which the event happened, must have been recorded
+ *               using the same timer as the platform's mozilla::TimeStamp
+ *               implementation
  */
 void
 XRE_StartupTimelineRecord(int aEvent, PRTime 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());
+  mozilla::StartupTimelineRecordExternal(aEvent, aWhen);
 }
--- a/toolkit/components/startup/StartupTimeline.h
+++ b/toolkit/components/startup/StartupTimeline.h
@@ -36,16 +36,17 @@ NS_VISIBILITY_DEFAULT __attribute__((wea
 #else
 
 #endif
 
 namespace mozilla {
 
 void RecordShutdownEndTimeStamp();
 void RecordShutdownStartTimeStamp();
+void StartupTimelineRecordExternal(int, uint64_t);
 
 class StartupTimeline {
 public:
   enum Event {
     #define mozilla_StartupTimeline_Event(ev, z) ev,
     #include "StartupTimeline.h"
     #undef mozilla_StartupTimeline_Event
     MAX_EVENT_ID
--- a/xpcom/ds/TimeStamp.h
+++ b/xpcom/ds/TimeStamp.h
@@ -328,16 +328,17 @@ public:
   // two TimeStamps, or scaling TimeStamps, is nonsense and must never
   // be allowed.
 
   static NS_HIDDEN_(nsresult) Startup();
   static NS_HIDDEN_(void) Shutdown();
 
 private:
   friend struct IPC::ParamTraits<mozilla::TimeStamp>;
+  friend void StartupTimelineRecordExternal(int, uint64_t);
 
   TimeStamp(TimeStampValue aValue) : mValue(aValue) {}
 
   static TimeStamp Now(bool aHighResolution);
 
   /**
    * When built with PRIntervalTime, a value of 0 means this instance
    * is "null". Otherwise, the low 32 bits represent a PRIntervalTime,
--- a/xpcom/ds/TimeStamp_windows.h
+++ b/xpcom/ds/TimeStamp_windows.h
@@ -10,16 +10,17 @@
 namespace mozilla {
 
 class TimeStamp;
 
 class TimeStampValue
 {
   friend struct IPC::ParamTraits<mozilla::TimeStampValue>;
   friend class TimeStamp;
+  friend void StartupTimelineRecordExternal(int, uint64_t);
 
   // Both QPC and GTC are kept in [mt] units.
   uint64_t mGTC;
   uint64_t mQPC;
   bool mHasQPC;
   bool mIsNull;
 
   TimeStampValue(uint64_t GTC, uint64_t QPC, bool hasQPC);