Bug 1024669 - Part 2: Test cases for thread name annotation in the crash report. r=gsvelto
authorCervantes Yu <cyu@mozilla.com>
Tue, 07 Feb 2017 18:58:36 +0800
changeset 566362 1105f666b01948b7bac4bf3566402baf2359fdb4
parent 566361 fcd98542af390cfb873aee712e43388c779b07e6
child 566363 7952b06fc0d0735e1ebb360cbf557ef704482ca1
push id55180
push userjjong@mozilla.com
push dateFri, 21 Apr 2017 09:36:13 +0000
reviewersgsvelto
bugs1024669
milestone55.0a1
Bug 1024669 - Part 2: Test cases for thread name annotation in the crash report. r=gsvelto MozReview-Commit-ID: 3ahk3hpkfuk
toolkit/crashreporter/moz.build
toolkit/crashreporter/test/gtest/TestCrashThreadAnnotation.cpp
toolkit/crashreporter/test/gtest/moz.build
toolkit/crashreporter/test/unit/test_crash_thread_annotation.js
toolkit/crashreporter/test/unit/xpcshell.ini
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -49,16 +49,19 @@ DIRS += [
 
 if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
     DIRS += ['injector']
     UNIFIED_SOURCES += [
         'InjectCrashReporter.cpp',
         'LoadLibraryRemote.cpp',
     ]
 
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['test/gtest']
+
 TEST_DIRS += ['test']
 
 EXPORTS += [
     'nsExceptionHandler.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsExceptionHandler.cpp',
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/gtest/TestCrashThreadAnnotation.cpp
@@ -0,0 +1,267 @@
+/* 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 "ThreadAnnotation.h"
+
+#include <string.h>
+
+#include "gtest/gtest.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Unused.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+using mozilla::Monitor;
+using mozilla::MonitorAutoLock;
+
+namespace CrashReporter {
+namespace {
+
+TEST(TestCrashThreadAnnotation, TestInitShutdown)
+{
+  InitThreadAnnotation();
+  ShutdownThreadAnnotation();
+}
+
+TEST(TestCrashThreadAnnotation, TestNestedInitShutdown)
+{
+  // No bad things should happen in case we have extra init/shutdown calls.
+  InitThreadAnnotation();
+  InitThreadAnnotation();
+  ShutdownThreadAnnotation();
+  ShutdownThreadAnnotation();
+}
+
+TEST(TestCrashThreadAnnotation, TestUnbalancedInit)
+{
+  // No bad things should happen in case we have unbalanced init/shutdown calls.
+  InitThreadAnnotation();
+  InitThreadAnnotation();
+  ShutdownThreadAnnotation();
+}
+
+TEST(TestCrashThreadAnnotation, TestUnbalancedShutdown)
+{
+  // No bad things should happen in case we have unbalanced init/shutdown calls.
+  InitThreadAnnotation();
+  ShutdownThreadAnnotation();
+  ShutdownThreadAnnotation();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_BeforeInit)
+{
+  // GetFlatThreadAnnotation() should not return anything before init.
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    ASSERT_STREQ(aAnnotation, "");
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_AfterShutdown)
+{
+  // GetFlatThreadAnnotation() should not return anything after shutdown.
+  InitThreadAnnotation();
+  ShutdownThreadAnnotation();
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    ASSERT_STREQ(aAnnotation, "");
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+}
+
+already_AddRefed<nsIThread>
+CreateTestThread(const char* aName, Monitor& aMonitor, bool& aDone)
+{
+  nsCOMPtr<nsIRunnable> setNameRunnable = NS_NewRunnableFunction([aName, &aMonitor, &aDone] () -> void {
+    NS_SetCurrentThreadName(aName);
+
+    MonitorAutoLock lock(aMonitor);
+    aDone = true;
+    aMonitor.NotifyAll();
+  });
+  nsCOMPtr<nsIThread> thread;
+  mozilla::Unused << NS_NewThread(getter_AddRefs(thread), setNameRunnable);
+
+  return thread.forget();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_OneThread)
+{
+  InitThreadAnnotation();
+
+  Monitor monitor("TestCrashThreadAnnotation");
+  bool threadNameSet = false;
+  nsCOMPtr<nsIThread> thread = CreateTestThread("Thread1", monitor, threadNameSet);
+  ASSERT_TRUE(!!thread);
+
+  {
+    MonitorAutoLock lock(monitor);
+    while (!threadNameSet) {
+      monitor.Wait();
+    }
+  }
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    ASSERT_TRUE(!!strstr(aAnnotation, "Thread1"));
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+
+  ShutdownThreadAnnotation();
+
+  thread->Shutdown();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_SetNameTwice)
+{
+  InitThreadAnnotation();
+
+  Monitor monitor("TestCrashThreadAnnotation");
+  bool threadNameSet = false;
+
+  nsCOMPtr<nsIRunnable> setNameRunnable = NS_NewRunnableFunction([&] () -> void {
+    NS_SetCurrentThreadName("Thread1");
+    // Set the name again. We should get the latest name.
+    NS_SetCurrentThreadName("Thread1Again");
+
+    MonitorAutoLock lock(monitor);
+    threadNameSet = true;
+    monitor.NotifyAll();
+  });
+  nsCOMPtr<nsIThread> thread;
+  nsresult rv = NS_NewThread(getter_AddRefs(thread), setNameRunnable);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  {
+    MonitorAutoLock lock(monitor);
+    while (!threadNameSet) {
+      monitor.Wait();
+    }
+  }
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    ASSERT_TRUE(!!strstr(aAnnotation, "Thread1Again"));
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+
+  ShutdownThreadAnnotation();
+
+  thread->Shutdown();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_TwoThreads)
+{
+  InitThreadAnnotation();
+
+  Monitor monitor("TestCrashThreadAnnotation");
+  bool thread1NameSet = false;
+  bool thread2NameSet = false;
+
+  nsCOMPtr<nsIThread> thread1 = CreateTestThread("Thread1", monitor, thread1NameSet);
+  ASSERT_TRUE(!!thread1);
+
+  nsCOMPtr<nsIThread> thread2 = CreateTestThread("Thread2", monitor, thread2NameSet);
+  ASSERT_TRUE(!!thread2);
+
+  {
+    MonitorAutoLock lock(monitor);
+    while (!(thread1NameSet && thread2NameSet)) {
+      monitor.Wait();
+    }
+  }
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    // Assert that Thread1 and Thread2 are both in the annotation data.
+    ASSERT_TRUE(!!strstr(aAnnotation, "Thread1"));
+    ASSERT_TRUE(!!strstr(aAnnotation, "Thread2"));
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+
+  ShutdownThreadAnnotation();
+
+  thread1->Shutdown();
+  thread2->Shutdown();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_ShutdownOneThread)
+{
+  InitThreadAnnotation();
+
+  Monitor monitor("TestCrashThreadAnnotation");
+  bool thread1NameSet = false;
+  bool thread2NameSet = false;
+
+  nsCOMPtr<nsIThread> thread1 = CreateTestThread("Thread1", monitor, thread1NameSet);
+  ASSERT_TRUE(!!thread1);
+
+  nsCOMPtr<nsIThread> thread2 = CreateTestThread("Thread2", monitor, thread2NameSet);
+  ASSERT_TRUE(!!thread2);
+
+  {
+    MonitorAutoLock lock(monitor);
+    while (!(thread1NameSet && thread2NameSet)) {
+      monitor.Wait();
+    }
+  }
+
+  nsresult rv = thread1->Shutdown();
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    // Assert that only Thread2 is present in the annotation data.
+    ASSERT_TRUE(!strstr(aAnnotation, "Thread1"));
+    ASSERT_TRUE(!!strstr(aAnnotation, "Thread2"));
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+
+  ShutdownThreadAnnotation();
+
+  thread2->Shutdown();
+}
+
+TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_ShutdownBothThreads)
+{
+  InitThreadAnnotation();
+
+  Monitor monitor("TestCrashThreadAnnotation");
+  bool thread1NameSet = false;
+  bool thread2NameSet = false;
+
+  nsCOMPtr<nsIThread> thread1 = CreateTestThread("Thread1", monitor, thread1NameSet);
+  ASSERT_TRUE(!!thread1);
+
+  nsCOMPtr<nsIThread> thread2 = CreateTestThread("Thread2", monitor, thread2NameSet);
+  ASSERT_TRUE(!!thread2);
+
+  {
+    MonitorAutoLock lock(monitor);
+    while (!(thread1NameSet && thread2NameSet)) {
+      monitor.Wait();
+    }
+  }
+
+  nsresult rv = thread1->Shutdown();
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = thread2->Shutdown();
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+        [&] (const char * aAnnotation) -> void {
+    // No leftover in annnotation data.
+    ASSERT_STREQ(aAnnotation, "");
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
+
+  ShutdownThreadAnnotation();
+}
+
+} // Anonymous namespace.
+} // namespace CrashReporter
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/gtest/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+    'TestCrashThreadAnnotation.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+    '../../'
+]
+
+FINAL_LIBRARY = 'xul-gtest'
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit/test_crash_thread_annotation.js
@@ -0,0 +1,15 @@
+function run_test() {
+  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
+    dump("INFO | test_crash_thread_annotation.js | Can't test crashreporter in a non-libxul build.\n");
+    return;
+  }
+
+  do_crash(
+    function() {
+      crashType = CrashTestUtils.CRASH_INVALID_POINTER_DEREF;
+    },
+    function(mdump, extra) {
+      do_check_true("ThreadIdNameMapping" in extra);
+    },
+    true);
+}
--- a/toolkit/crashreporter/test/unit/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit/xpcshell.ini
@@ -18,16 +18,19 @@ skip-if = os == 'win' && bits == 64
 [test_crash_oom.js]
 [test_oom_annotation_windows.js]
 skip-if = os != 'win'
 
 [test_crash_abort.js]
 skip-if = os == 'win'
 
 [test_crash_uncaught_exception.js]
+
+[test_crash_thread_annotation.js]
+
 [test_crash_with_memory_report.js]
 [test_crashreporter.js]
 [test_crashreporter_crash.js]
 [test_override_exception_handler.js]
 skip-if = os != 'win'
 
 [test_crashreporter_appmem.js]
 # we need to skip this due to bug 838613