Bug 702764 part 2 - Make nsAppStartup::GetStartupInfo more flexible. r=tglek
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 18 Nov 2011 08:54:27 +0100
changeset 80428 f9f4594a621b5efd61589eba55ddd5b361804fe4
parent 80427 80ddfdb7e5757317015b51aa50ab6c931b660798
child 80429 c744ccd70348c9667362ca18f86e9196e9081600
push id3446
push usermh@glandium.org
push dateFri, 18 Nov 2011 07:56:08 +0000
treeherdermozilla-inbound@109b2416d243 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstglek
bugs702764
milestone11.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 702764 part 2 - Make nsAppStartup::GetStartupInfo more flexible. r=tglek
toolkit/components/startup/Makefile.in
toolkit/components/startup/StartupTimeline.cpp
toolkit/components/startup/StartupTimeline.h
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/telemetry/Telemetry.h
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/xre/nsAppRunner.cpp
view/src/nsViewManager.cpp
xpfe/appshell/src/nsAppShellService.cpp
--- a/toolkit/components/startup/Makefile.in
+++ b/toolkit/components/startup/Makefile.in
@@ -45,18 +45,22 @@ DIRS = public
 
 MODULE = toolkitcomps
 LIBRARY_NAME = appstartup_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 
 CPPSRCS = \
   nsAppStartup.cpp \
+  StartupTimeline.cpp \
   $(NULL)
 
+EXPORTS_NAMESPACES = mozilla
+EXPORTS_mozilla = StartupTimeline.h
+
 ifeq (os2,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += nsUserInfoOS2.cpp
 else
 ifeq ($(OS_ARCH),WINNT)
 CPPSRCS += nsUserInfoWin.cpp
 else
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CMMSRCS += nsUserInfoMac.mm
new file mode 100644
--- /dev/null
+++ b/toolkit/components/startup/StartupTimeline.cpp
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "StartupTimeline.h"
+
+namespace mozilla {
+
+PRTime 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
+};
+
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/startup/StartupTimeline.h
@@ -0,0 +1,89 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef mozilla_StartupTimeline_Event
+mozilla_StartupTimeline_Event(PROCESS_CREATION, "process")
+mozilla_StartupTimeline_Event(MAIN, "main")
+mozilla_StartupTimeline_Event(FIRST_PAINT, "firstPaint")
+mozilla_StartupTimeline_Event(SESSION_RESTORED, "sessionRestored")
+mozilla_StartupTimeline_Event(CREATE_TOP_LEVEL_WINDOW, "createTopLevelWindow")
+#else
+
+#ifndef mozilla_StartupTimeline
+#define mozilla_StartupTimeline
+
+#include "prtime.h"
+#include "nscore.h"
+
+namespace mozilla {
+
+class StartupTimeline {
+public:
+  enum Event {
+    #define mozilla_StartupTimeline_Event(ev, z) ev,
+    #include __FILE__
+    #undef mozilla_StartupTimeline_Event
+    MAX_EVENT_ID
+  };
+
+  static PRTime Get(Event ev) {
+    return sStartupTimeline[ev];
+  }
+
+  static const char *Describe(Event ev) {
+    return sStartupTimelineDesc[ev];
+  }
+
+  static void Record(Event ev, PRTime when = PR_Now()) {
+    sStartupTimeline[ev] = when;
+  }
+
+  static void RecordOnce(Event ev, PRTime when = PR_Now()) {
+    if (!sStartupTimeline[ev])
+      sStartupTimeline[ev] = when;
+  }
+
+private:
+  static NS_EXTERNAL_VIS_(PRTime) 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
@@ -90,28 +90,21 @@
 #endif
 
 #ifdef __OpenBSD__
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #endif
 
 #include "mozilla/Telemetry.h"
+#include "mozilla/StartupTimeline.h"
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 using namespace mozilla;
-extern PRTime gXRE_mainTimestamp;
-// The following tracks our overhead between reaching XRE_main and loading any XUL
-extern PRTime gCreateTopLevelWindowTimestamp;// Timestamp of the first call to
-                                             // nsAppShellService::CreateTopLevelWindow
-extern PRTime gFirstPaintTimestamp;
-// mfinklesessionstore-browser-state-restored might be a better choice than the one below
-static PRTime gRestoredTimestamp = 0;       // Timestamp of sessionstore-windows-restored
-static PRTime gProcessCreationTimestamp = 0;// Timestamp of sessionstore-windows-restored
 
 PRUint32 gRestartMode = 0;
 
 class nsAppExitEvent : public nsRunnable {
 private:
   nsRefPtr<nsAppStartup> mService;
 
 public:
@@ -562,17 +555,17 @@ nsAppStartup::Observe(nsISupports *aSubj
       CloseAllWindows();
       ExitLastWindowClosingSurvivalArea();
     }
   } else if (!strcmp(aTopic, "xul-window-registered")) {
     EnterLastWindowClosingSurvivalArea();
   } else if (!strcmp(aTopic, "xul-window-destroyed")) {
     ExitLastWindowClosingSurvivalArea();
   } else if (!strcmp(aTopic, "sessionstore-windows-restored")) {
-    gRestoredTimestamp = PR_Now();
+    StartupTimeline::Record(StartupTimeline::SESSION_RESTORED);
   } else {
     NS_ERROR("Unexpected observer topic.");
   }
 
   return NS_OK;
 }
 
 #if defined(LINUX) || defined(ANDROID)
@@ -602,47 +595,46 @@ JiffiesSinceBoot(const char *file)
     return 0;
   return starttime;
 }
 
 static void
 ThreadedCalculateProcessCreationTimestamp(void *aClosure)
 {
   PRTime now = PR_Now();
-  gProcessCreationTimestamp = 0;
   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));
   
   PRUint64 thread_jiffies = JiffiesSinceBoot(thread_stat);
   PRUint64 self_jiffies = JiffiesSinceBoot("/proc/self/stat");
   
   if (!thread_jiffies || !self_jiffies)
     return;
 
   PRTime interval = (thread_jiffies - self_jiffies) * PR_USEC_PER_SEC / hz;
-  gProcessCreationTimestamp = now - interval;
+  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 gProcessCreationTimestamp;
+  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)
@@ -698,33 +690,16 @@ CalculateProcessCreationTimestamp()
 #else
 static PRTime
 CalculateProcessCreationTimestamp()
 {
   return 0;
 }
 #endif
  
-static void
-MaybeDefineProperty(JSContext *cx, JSObject *obj, const char *name, PRTime timestamp)
-{
-  if (!timestamp)
-    return;
-  JSObject *date = js_NewDateObjectMsec(cx, timestamp/PR_USEC_PER_MSEC);
-  JS_DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(date), NULL, NULL, JSPROP_ENUMERATE);     
-}
-
-enum {
-  INVALID_PROCESS_CREATION = 0,
-  INVALID_MAIN,
-  INVALID_FIRST_PAINT,
-  INVALID_SESSION_RESTORED,
-  INVALID_CREATE_TOP_LEVEL_WINDOW
-};
-
 NS_IMETHODIMP
 nsAppStartup::GetStartupInfo()
 {
   nsAXPCNativeCallContext *ncc = nsnull;
   nsresult rv;
   nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID(), &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -743,45 +718,42 @@ nsAppStartup::GetStartupInfo()
   JSContext *cx = nsnull;
   rv = ncc->GetJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
   *retvalPtr = OBJECT_TO_JSVAL(obj);
   ncc->SetReturnValueWasSet(true);
 
-  char *moz_app_restart = PR_GetEnv("MOZ_APP_RESTART");
-  if (moz_app_restart) {
-    gProcessCreationTimestamp = nsCRT::atoll(moz_app_restart) * PR_USEC_PER_MSEC;
-  } else if (!gProcessCreationTimestamp) {
-    gProcessCreationTimestamp = CalculateProcessCreationTimestamp();
-  }
-  // Bug 670008: Avoid obviously invalid process creation times
-  if (PR_Now() <= gProcessCreationTimestamp) {
-    gProcessCreationTimestamp = 0;
-    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, INVALID_PROCESS_CREATION);
+  PRTime ProcessCreationTimestamp = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
+
+  if (!ProcessCreationTimestamp) {
+    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();
+    }
+    // Bug 670008: Avoid obviously invalid process creation times
+    if (PR_Now() <= ProcessCreationTimestamp) {
+      ProcessCreationTimestamp = -1;
+      Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, StartupTimeline::PROCESS_CREATION);
+    }
+    StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, ProcessCreationTimestamp);
   }
 
-  MaybeDefineProperty(cx, obj, "process", gProcessCreationTimestamp);
-
-  if (gXRE_mainTimestamp < gProcessCreationTimestamp)
-    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, INVALID_MAIN);
-
-  // always define main to aid with bug 689256
-  MaybeDefineProperty(cx, obj, "main", gXRE_mainTimestamp);
-  
-  if (gCreateTopLevelWindowTimestamp >= gProcessCreationTimestamp)
-    MaybeDefineProperty(cx, obj, "createTopLevelWindow", gCreateTopLevelWindowTimestamp);
-  else
-    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, INVALID_CREATE_TOP_LEVEL_WINDOW);
-
-  if (gFirstPaintTimestamp >= gXRE_mainTimestamp)
-    MaybeDefineProperty(cx, obj, "firstPaint", gFirstPaintTimestamp);
-  else
-    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, INVALID_FIRST_PAINT);
-
-  if (gRestoredTimestamp >= gXRE_mainTimestamp)
-    MaybeDefineProperty(cx, obj, "sessionRestored", gRestoredTimestamp);
-  else
-    Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, INVALID_SESSION_RESTORED);
+  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);
+      } else {
+        JSObject *date = js_NewDateObjectMsec(cx, StartupTimeline::Get(ev)/PR_USEC_PER_MSEC);
+        JS_DefineProperty(cx, obj, StartupTimeline::Describe(ev), OBJECT_TO_JSVAL(date), NULL, NULL, JSPROP_ENUMERATE);
+      }
+    }
+  }
 
   return NS_OK;
 }
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef Telemetry_h__
 #define Telemetry_h__
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/StartupTimeline.h"
 
 namespace base {
   class Histogram;
 }
 
 namespace mozilla {
 namespace Telemetry {
 
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -201,17 +201,17 @@ HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_READ_
 HISTOGRAM(MOZ_SQLITE_WEBAPPS_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
 HISTOGRAM(MOZ_SQLITE_PLACES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_COOKIES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_WEBAPPS_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_MS, 1, 32768, 20, EXPONENTIAL, "mozStorage async requests completion (ms)")
 HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, 0, 1, 2, BOOLEAN, "mozStorage async requests success")
-HISTOGRAM(STARTUP_MEASUREMENT_ERRORS, 1, 4, 5, LINEAR, "Flags errors in startup calculation()")
+HISTOGRAM(STARTUP_MEASUREMENT_ERRORS, 1, mozilla::StartupTimeline::MAX_EVENT_ID, mozilla::StartupTimeline::MAX_EVENT_ID + 1, LINEAR, "Flags errors in startup calculation()")
 HISTOGRAM(NETWORK_DISK_CACHE_OPEN, 1, 10000, 10, EXPONENTIAL, "Time spent opening disk cache (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_TRASHRENAME, 1, 10000, 10, EXPONENTIAL, "Time spent renaming bad Cache to Cache.Trash (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_DELETEDIR, 1, 10000, 10, EXPONENTIAL, "Time spent deleting disk cache (ms)")
 
 /**
  * Url-Classifier telemetry
  */
 #ifdef MOZ_URL_CLASSIFIER
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -155,16 +155,17 @@ using mozilla::unused;
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsXULAppAPI.h"
 #include "nsXREDirProvider.h"
 #include "nsToolkitCompsCID.h"
 
 #include "nsINIParser.h"
 #include "mozilla/Omnijar.h"
+#include "mozilla/StartupTimeline.h"
 
 #include <stdlib.h>
 
 #ifdef XP_UNIX
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
 #endif
@@ -2599,34 +2600,32 @@ static DWORD InitDwriteBG(LPVOID lpdwThr
       }
     }
   }
   SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
   return 0;
 }
 #endif
 
-PRTime gXRE_mainTimestamp = 0;
-
 #ifdef MOZ_X11
 #ifndef MOZ_PLATFORM_MAEMO
 bool fire_glxtest_process();
 #endif
 #endif
 
 #include "sampler.h"
 
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
 {
   NS_TIME_FUNCTION;
   SAMPLER_INIT();
   SAMPLE_CHECKPOINT("Startup", "XRE_Main");
 
-  gXRE_mainTimestamp = PR_Now();
+  StartupTimeline::Record(StartupTimeline::MAIN);
 
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
     NS_BREAK();
 #endif
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -54,18 +54,18 @@
 #include "nsCOMArray.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsIPluginWidget.h"
 #include "nsXULPopupManager.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsEventStateManager.h"
+#include "mozilla/StartupTimeline.h"
 
-PRTime gFirstPaintTimestamp = 0; // Timestamp of the first paint event
 /**
    XXX TODO XXX
 
    DeCOMify newly private methods
    Optimize view storage
 */
 
 /**
@@ -408,18 +408,17 @@ void nsViewManager::RenderViews(nsView *
                                 bool aWillSendDidPaint)
 {
   NS_ASSERTION(GetDisplayRootFor(aView) == aView,
                "Widgets that we paint must all be display roots");
 
   if (mObserver) {
     mObserver->Paint(aView, aWidget, aRegion, aIntRegion,
                      aPaintDefaultBackground, aWillSendDidPaint);
-    if (!gFirstPaintTimestamp)
-      gFirstPaintTimestamp = PR_Now();
+    mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
   }
 }
 
 void nsViewManager::ProcessPendingUpdates(nsView* aView, bool aDoInvalidate)
 {
   NS_ASSERTION(IsRootVM(), "Updates will be missed");
 
   // Protect against a null-view.
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -70,16 +70,17 @@
 #include "nsAppShellService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIPlatformCharset.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIChromeRegistry.h"
 
 #include "mozilla/Preferences.h"
+#include "mozilla/StartupTimeline.h"
 
 using namespace mozilla;
 
 // Default URL for the hidden window, can be overridden by a pref on Mac
 #define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
 
 class nsIAppShell;
 
@@ -151,34 +152,31 @@ nsAppShellService::DestroyHiddenWindow()
     mHiddenWindow->Destroy();
 
     mHiddenWindow = nsnull;
   }
 
   return NS_OK;
 }
 
-PRTime gCreateTopLevelWindowTimestamp = 0;
-
 /*
  * Create a new top level window and display the given URL within it...
  */
 NS_IMETHODIMP
 nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent,
                                         nsIURI *aUrl, 
                                         PRUint32 aChromeMask,
                                         PRInt32 aInitialWidth,
                                         PRInt32 aInitialHeight,
                                         nsIXULWindow **aResult)
 
 {
   nsresult rv;
 
-  if (!gCreateTopLevelWindowTimestamp)
-    gCreateTopLevelWindowTimestamp = PR_Now();
+  StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW);
 
   nsWebShellWindow *newWindow = nsnull;
   rv = JustCreateTopWindow(aParent, aUrl,
                            aChromeMask, aInitialWidth, aInitialHeight,
                            false, &newWindow);  // addrefs
 
   *aResult = newWindow; // transfer ref