bug 387551 - generate and send unique user id in crash reports. r=bsmedberg
authorted.mielczarek@gmail.com
Mon, 16 Jul 2007 10:07:20 -0700
changeset 3517 fb520714fc661b7803e7209563c05f3ac2ac85f7
parent 3516 c8e452f4668d2c756429162037f81db69883d44e
child 3518 42a778bf9aaa9d9b64a8fa859b687eac6dac1878
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs387551
milestone1.9a7pre
bug 387551 - generate and send unique user id in crash reports. r=bsmedberg
toolkit/airbag/nsAirbagExceptionHandler.cpp
toolkit/airbag/nsAirbagExceptionHandler.h
toolkit/xre/nsAppRunner.cpp
--- a/toolkit/airbag/nsAirbagExceptionHandler.cpp
+++ b/toolkit/airbag/nsAirbagExceptionHandler.cpp
@@ -62,16 +62,17 @@
 #endif // defined(XP_WIN32)
 
 #ifndef HAVE_CPP_2BYTE_WCHAR_T
 #error "This code expects a 2 byte wchar_t.  You should --disable-airbag."
 #endif
 
 #include <stdlib.h>
 #include <prenv.h>
+#include <prio.h>
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsILocalFile.h"
 #include "nsDataHashtable.h"
 
 namespace CrashReporter {
 
 #ifdef XP_WIN32
@@ -342,16 +343,141 @@ nsresult SetMinidumpPath(const nsAString
   if (!gExceptionHandler)
     return NS_ERROR_NOT_INITIALIZED;
 
   gExceptionHandler->set_dump_path(CONVERT_UTF16_TO_XP_CHAR(aPath).BeginReading());
 
   return NS_OK;
 }
 
+static nsresult
+WriteDataToFile(nsIFile* aFile, const nsACString& data)
+{
+  nsCAutoString filename;
+  nsresult rv = aFile->GetNativePath(filename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRFileDesc* fd = PR_Open(filename.get(), PR_WRONLY | PR_CREATE_FILE, 00600);
+  NS_ENSURE_TRUE(fd, NS_ERROR_FAILURE);
+
+  rv = NS_OK;
+  if (PR_Write(fd, data.Data(), data.Length()) == -1) {
+    rv = NS_ERROR_FAILURE;
+  }
+  PR_Close(fd);
+  return rv;
+}
+
+static nsresult
+GetFileContents(nsIFile* aFile, nsACString& data)
+{
+  nsCAutoString filename;
+  nsresult rv = aFile->GetNativePath(filename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRFileDesc* fd = PR_Open(filename.get(), PR_RDONLY, 0);
+  NS_ENSURE_TRUE(fd, NS_ERROR_FILE_NOT_FOUND);
+
+  rv = NS_OK;
+  PRInt32 filesize = PR_Available(fd);
+  if (filesize <= 0) {
+    rv = NS_ERROR_FILE_NOT_FOUND;
+  }
+  else {
+    data.SetLength(filesize);
+    if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
+      rv = NS_ERROR_FAILURE;
+    }
+  }
+  PR_Close(fd);
+  return rv;
+}
+
+// Function typedef for initializing a piece of data that we
+// don't already have.
+typedef nsresult (*InitDataFunc)(nsACString&);
+
+// Attempt to read aFile's contents into aContents, if aFile
+// does not exist, create it and initialize its contents
+// by calling aInitFunc for the data.
+static nsresult 
+GetOrInit(nsIFile* aFile, nsACString& aContents, InitDataFunc aInitFunc)
+{
+  PRBool exists;
+  nsresult rv = aFile->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!exists) {
+    // get the initial value and write it to the file
+    rv = aInitFunc(aContents);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return WriteDataToFile(aFile, aContents);
+  }
+
+  // just get the file's contents
+  return GetFileContents(aFile, aContents);
+}
+
+// Generate a unique user ID.  We're using a GUID form,
+// but not jumping through hoops to make it cryptographically
+// secure.  We just want it to distinguish unique users.
+static nsresult
+InitUserID(nsACString& aUserID)
+{
+  nsID id;
+
+  // copied shamelessly from nsUUIDGenerator.cpp
+#if defined(XP_WIN)
+  HRESULT hr = CoCreateGuid((GUID*)&id);
+  if (NS_FAILED(hr))
+    return NS_ERROR_FAILURE;
+#elif defined(XP_MACOSX)
+  CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+  if (!uuid)
+    return NS_ERROR_FAILURE;
+  
+  CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
+  memcpy(&id, &bytes, sizeof(nsID));
+
+  CFRelease(uuid);
+#else
+  // UNIX or some such thing
+  id.m0 = random();
+  id.m1 = random();
+  id.m2 = random();
+  *reinterpret_cast<PRUint32*>(&id.m3[0]) = random();
+  *reinterpret_cast<PRUint32*>(&id.m3[4]) = random();
+#endif
+
+  nsCAutoString id_str(id.ToString());
+  aUserID = Substring(id_str, 1, id_str.Length()-2);
+  
+  return NS_OK;
+}
+
+// Annotate the crash report with a Unique User ID.
+// TODO: also add time since install, and time since last crash.
+// (bug 376720 and bug 376721)
+// If any piece of data doesn't exist, initialize it first.
+nsresult SetupExtraData(nsILocalFile* aAppDataDirectory)
+{
+  nsresult rv = aAppDataDirectory->Append(NS_LITERAL_STRING("Crash Reports"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIFile> userIDfile;
+  if (NS_SUCCEEDED(aAppDataDirectory->Clone(getter_AddRefs(userIDfile)))
+      && NS_SUCCEEDED(userIDfile->Append(NS_LITERAL_STRING("UserID")))) {
+    nsCAutoString userID;
+    if (NS_SUCCEEDED(GetOrInit(userIDfile, userID, InitUserID))) {
+      AnnotateCrashReport(NS_LITERAL_CSTRING("UserID"), userID);
+    }
+  }
+  return NS_OK;
+}
+
 nsresult UnsetExceptionHandler()
 {
   // do this here in the unlikely case that we succeeded in allocating
   // our strings but failed to allocate gExceptionHandler.
   if (crashReporterAPIData_Hash) {
     delete crashReporterAPIData_Hash;
     crashReporterAPIData_Hash = nsnull;
   }
--- a/toolkit/airbag/nsAirbagExceptionHandler.h
+++ b/toolkit/airbag/nsAirbagExceptionHandler.h
@@ -44,11 +44,12 @@
 
 namespace CrashReporter {
 nsresult SetExceptionHandler(nsILocalFile* aXREDirectory,
                              const char* aServerURL);
 nsresult SetMinidumpPath(const nsAString& aPath);
 nsresult UnsetExceptionHandler();
 nsresult AnnotateCrashReport(const nsACString &key, const nsACString &data);
 nsresult SetRestartArgs(int argc, char **argv);
+nsresult SetupExtraData(nsILocalFile* aAppDataDirectory);
 }
 
 #endif /* nsAirbagExceptionHandler_h__ */
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2432,16 +2432,26 @@ XRE_main(int argc, char* argv[], const n
                                      nsDependentCString(appData.name));
     if (appData.version)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"),
                                      nsDependentCString(appData.version));
     if (appData.buildID)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"),
                                      nsDependentCString(appData.buildID));
     CrashReporter::SetRestartArgs(argc, argv);
+
+    // annotate other data (user id etc)
+    nsXREDirProvider dirProvider;
+    nsCOMPtr<nsILocalFile> userAppDataDir;
+    rv = dirProvider.Initialize(gAppData->directory, gAppData->xreDirectory);
+    if (NS_SUCCEEDED(rv) &&
+        NS_SUCCEEDED(dirProvider.GetUserAppDataDirectory(
+                                 getter_AddRefs(userAppDataDir)))) {
+      CrashReporter::SetupExtraData(userAppDataDir);
+    }
   }
 #endif
 
 #ifdef XP_MACOSX
   if (PR_GetEnv("MOZ_LAUNCHED_CHILD")) {
     // This is needed, on relaunch, to force the OS to use the "Cocoa Dock
     // API".  Otherwise the call to ReceiveNextEvent() below will make it
     // use the "Carbon Dock API".  For more info see bmo bug 377166.