about:startup - record results in database after startup has finished, and show the records in the about:startup page as a chart
authorDaniel Brooks <db48x@db48x.net>
Sun, 05 Sep 2010 16:09:55 -0400
changeset 58825 4b7fd492029fdc689fa099b1efec6e1c719401d6
parent 58824 09d8fea4df6b0caa88bf191333f707d5919d0c02
child 58826 80da5e79c398bef20e93d1677ff6e6eac9c93d03
push id17440
push userdb48x@yahoo.com
push dateWed, 08 Dec 2010 04:15:54 +0000
treeherdermozilla-central@a89f24bf1798 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b6pre
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
about:startup - record results in database after startup has finished, and show the records in the about:startup page as a chart
browser/base/content/aboutStartup.html
browser/base/content/aboutStartup.js
toolkit/components/startup/src/nsAppStartup.cpp
--- a/browser/base/content/aboutStartup.html
+++ b/browser/base/content/aboutStartup.html
@@ -7,11 +7,20 @@
 <body>
   <h1>App Startup Timing</h1>
   <table>
     <tr><th>Event</th><th>Timestamp</th><th>Elapsed Time</th></tr>
     <tr><td>Application Launched</td><td id="launched">—</td><td>—</td></tr>
     <tr><td>Application Started</td><td id="started">—</td><td>—</td></tr>
     <tr><td>Session Restored</td><td id="restored">—</td><td>—</td></tr>
   </table>
+
+  <h1>Chart</h1>
+  <table>
+    <tr><th>Timestamp</th><th>Launch Duration</th><th>Startup Duration</th><th>Total Duration</th></tr>
+  </table>
+  <table>
+    <tr><th>Timestamp</th><th>Event Description</th></tr>
+    <tr><td colspan="2"><i>No Events Recorded</i></td></tr>
+  </table>
   <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutStartup.js"></script>
 </body>
 </html>
--- a/browser/base/content/aboutStartup.js
+++ b/browser/base/content/aboutStartup.js
@@ -14,15 +14,68 @@ if (launched)
   displayDuration("started", startup - launched);
 
 let ss = Cc["@mozilla.org/browser/sessionstartup;1"].getService(Ci.nsISessionStartup);
 displayTimestamp("restored", restored = ss.restoredTimestamp);
 displayDuration("restored", restored - startup);
 
 function displayTimestamp(id, µs)
 {
-  document.getElementById(id).textContent = new Date(µs/1000) +" ("+ µs +" µs)";
+  document.getElementById(id).textContent = formatstamp(µs);
 }
 
 function displayDuration(id, µs)
 {
-  document.getElementById(id).nextSibling.textContent = µs +" µs";
+  document.getElementById(id).nextSibling.textContent = formatµs(µs);
+}
+
+function formatstamp(µs)
+{
+  return new Date(µs/1000) +" ("+ formatµs(µs) +")";
+}
+
+function formatµs(µs)
+{
+  return µs + " µs";
 }
+
+var table = document.getElementsByTagName("table")[1];
+
+var file = Components.classes["@mozilla.org/file/directory_service;1"]
+                     .getService(Components.interfaces.nsIProperties)
+                     .get("ProfD", Components.interfaces.nsIFile);
+file.append("startup.sqlite");
+
+var svc = Components.classes["@mozilla.org/storage/service;1"]
+                    .getService(Components.interfaces.mozIStorageService);
+var db = svc.openDatabase(file);
+var query = db.createStatement("SELECT timestamp, launch, startup FROM duration");
+query.executeAsync({
+  handleResult: function(results)
+  {
+    for (let row = results.getNextRow(); row; row = results.getNextRow())
+    {
+        table.appendChild(tr(td(formatstamp(row.getResultByName("timestamp"))),
+                             td(formatµs(l = row.getResultByName("launch"))),
+                             td(formatµs(s = row.getResultByName("startup"))),
+                             td(formatµs(l + s))));
+    }
+  },
+  handleError: function(error)
+  {
+      table.appendChild(tr(td("Error: "+ error.message +" ("+ error.result +")")));
+  },
+  handleCompletion: function() { },
+});
+
+function td(str)
+{
+  let cell = document.createElement("td");
+  cell.innerHTML = str;
+  return cell;
+}
+
+function tr()
+{
+  let row = document.createElement("tr");
+  Array.forEach(arguments, function(cell) { row.appendChild(cell); });
+  return row;
+}
--- a/toolkit/components/startup/src/nsAppStartup.cpp
+++ b/toolkit/components/startup/src/nsAppStartup.cpp
@@ -64,18 +64,21 @@
 #include "nsStringGlue.h"
 
 #include "prprf.h"
 #include "nsCRT.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsAppShellCID.h"
 #include "mozilla/Services.h"
-
+#include "mozilla/storage.h"
 #include "mozilla/FunctionTimer.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIXULRuntime.h"
+#include "nsXPCOMCIDInternal.h"
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 class nsAppExitEvent : public nsRunnable {
 private:
   nsRefPtr<nsAppStartup> mService;
 
 public:
@@ -87,16 +90,20 @@ public:
 
     // We're done "shutting down".
     mService->mShuttingDown = PR_FALSE;
     mService->mRunning = PR_FALSE;
     return NS_OK;
   }
 };
 
+nsresult RecordStartupDuration();
+nsresult OpenStartupDatabase(mozIStorageConnection **db);
+nsresult EnsureTable(mozIStorageConnection *db, const nsACString &table, const nsACString &schema);
+
 //
 // nsAppStartup
 //
 
 nsAppStartup::nsAppStartup() :
   mConsiderQuitStopper(0),
   mRunning(PR_FALSE),
   mShuttingDown(PR_FALSE),
@@ -120,16 +127,18 @@ nsAppStartup::Init()
   nsCOMPtr<nsIObserverService> os =
     mozilla::services::GetObserverService();
   if (!os)
     return NS_ERROR_FAILURE;
 
   NS_TIME_FUNCTION_MARK("Got Observer service");
 
   os->AddObserver(this, "quit-application-forced", PR_TRUE);
+  os->AddObserver(this, "sessionstore-browser-state-restored", PR_TRUE);
+  os->AddObserver(this, "sessionstore-windows-restored", PR_TRUE);
   os->AddObserver(this, "profile-change-teardown", PR_TRUE);
   os->AddObserver(this, "xul-window-registered", PR_TRUE);
   os->AddObserver(this, "xul-window-destroyed", PR_TRUE);
 
   return NS_OK;
 }
 
 
@@ -462,17 +471,17 @@ nsAppStartup::CreateChromeWindow2(nsIWeb
       But unparented modal (and therefore dependent) windows happen
       in our codebase, so we allow it after some bellyaching: */
     if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
       NS_WARNING("dependent window created without a parent");
 
     nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
     if (!appShell)
       return NS_ERROR_FAILURE;
-    
+
     appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
                                    nsIAppShellService::SIZE_TO_CONTENT,
                                    nsIAppShellService::SIZE_TO_CONTENT,
                                    mAppShell, getter_AddRefs(newWindow));
   }
 
   // if anybody gave us anything to work with, use it
   if (newWindow) {
@@ -503,14 +512,89 @@ nsAppStartup::Observe(nsISupports *aSubj
       EnterLastWindowClosingSurvivalArea();
       CloseAllWindows();
       ExitLastWindowClosingSurvivalArea();
     }
   } else if (!strcmp(aTopic, "xul-window-registered")) {
     EnterLastWindowClosingSurvivalArea();
   } else if (!strcmp(aTopic, "xul-window-destroyed")) {
     ExitLastWindowClosingSurvivalArea();
+  } else if ((!strcmp(aTopic, "sessionstore-browser-state-restored")) ||
+             (!strcmp(aTopic, "sessionstore-windows-restored"))) {
+    RecordStartupDuration();
   } else {
     NS_ERROR("Unexpected observer topic.");
   }
 
   return NS_OK;
 }
+
+nsresult RecordStartupDuration()
+{
+  nsresult rv;
+  nsCOMPtr<nsIXULRuntime> runtime = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
+
+  PRTime launched, started, finished;
+  runtime->GetLaunchTimestamp((PRUint64*)&launched);
+  runtime->GetStartupTimestamp((PRUint64*)&started);
+
+  nsCOMPtr<mozIStorageConnection> db;
+  rv = OpenStartupDatabase(getter_AddRefs(db));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = EnsureTable(db, NS_LITERAL_CSTRING("duration"),
+                   NS_LITERAL_CSTRING("timestamp INTEGER, launch INTEGER, startup INTEGER"));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = EnsureTable(db, NS_LITERAL_CSTRING("events"),
+                   NS_LITERAL_CSTRING("timestamp INTEGER, description TEXT"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<mozIStorageStatement> statement;
+  rv = db->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO duration VALUES (?1, ?2, ?3)"),
+                           getter_AddRefs(statement));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = statement->BindInt64Parameter(0, launched);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = statement->BindInt64Parameter(1, started - launched);
+  NS_ENSURE_SUCCESS(rv, rv);
+  finished = PR_Now();
+  rv = statement->BindInt64Parameter(2, finished - started);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  statement->Execute();
+  return NS_OK;
+}
+
+nsresult OpenStartupDatabase(mozIStorageConnection **db)
+{
+  nsresult rv;
+  nsCOMPtr<nsIFile> file;
+  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = file->Append(NS_LITERAL_STRING("startup.sqlite"));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<mozIStorageService> svc = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = svc->OpenDatabase(file, db);
+  if (NS_ERROR_FILE_CORRUPTED == rv)
+  {
+    svc->BackupDatabaseFile(file, NS_LITERAL_STRING("startup.sqlite.backup"), nsnull, nsnull);
+    rv = svc->OpenDatabase(file, db);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+nsresult EnsureTable(mozIStorageConnection *db, const nsACString &table, const nsACString &schema)
+{
+  nsresult rv;
+  PRBool exists = false;
+  rv = db->TableExists(table, &exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!exists)
+    rv = db->CreateTable(PromiseFlatCString(table).get(),
+                         PromiseFlatCString(schema).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}