Bug 1363469 - Define signal handlers to dump and reset coverage counters. r=froydnj,kanru
authorMarco Castelluccio <mcastelluccio@mozilla.com>
Wed, 14 Jun 2017 09:50:44 +0100
changeset 363831 f704631f7ab5ff83606151a4e02bb09fe4898436
parent 363830 d4a0301fac3a5a83ac89e77fb942f3765c7d4646
child 363832 6d2026f8b291d0aa09168d41e361dc6fafcfc410
push id91421
push usermcastelluccio@mozilla.com
push dateWed, 14 Jun 2017 08:51:58 +0000
treeherdermozilla-inbound@f704631f7ab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, kanru
bugs1363469
milestone56.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 1363469 - Define signal handlers to dump and reset coverage counters. r=froydnj,kanru
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/PContent.ipdl
dom/ipc/moz.build
ipc/glue/CodeCoverageHandler.cpp
ipc/glue/CodeCoverageHandler.h
ipc/glue/moz.build
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/moz.build
toolkit/xre/moz.build
toolkit/xre/nsAppRunner.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -215,16 +215,19 @@
 
 #ifdef MOZ_WIDGET_GTK
 #include "nsAppRunner.h"
 #endif
 #ifdef MOZ_CRASHREPORTER
 #include "mozilla/ipc/CrashReporterClient.h"
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+#include "mozilla/CodeCoverageHandler.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::workers;
 using namespace mozilla::media;
 using namespace mozilla::embedding;
 using namespace mozilla::gmp;
@@ -3413,10 +3416,22 @@ ContentChild::RecvSetPluginList(const ui
                                 nsTArray<plugins::PluginTag>&& aPluginTags,
                                 nsTArray<plugins::FakePluginTag>&& aFakePluginTags)
 {
   RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
   host->SetPluginsInContent(aPluginEpoch, aPluginTags, aFakePluginTags);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentChild::RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle)
+{
+#ifdef MOZ_CODE_COVERAGE
+  CodeCoverageHandler::Init(aHandle);
+  return IPC_OK();
+#else
+  NS_RUNTIMEABORT("Shouldn't receive this message in non-code coverage builds!");
+  return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -604,16 +604,19 @@ public:
 
   virtual mozilla::ipc::IPCResult
   RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override;
 
   mozilla::ipc::IPCResult
   RecvSetPermissionsWithKey(const nsCString& aPermissionKey,
                             nsTArray<IPC::Permission>&& aPerms) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle);
+
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   bool
   SendGetA11yContentId();
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
   // Get a reference to the font family list passed from the chrome process,
   // for use during gfx initialization.
   InfallibleTArray<mozilla::dom::FontFamilyListEntry>&
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -254,16 +254,20 @@
 #include "nsAccessibilityService.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "nsIProfiler.h"
 #include "ProfilerParent.h"
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+#include "mozilla/CodeCoverageHandler.h"
+#endif
+
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
@@ -2055,18 +2059,23 @@ ContentParent::LaunchSubprocess(ProcessP
     extraArgs.push_back("-safeMode");
   }
 
   if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
     MarkAsDead();
     return false;
   }
 
-  Open(mSubprocess->GetChannel(),
-     base::GetProcId(mSubprocess->GetChildProcessHandle()));
+  base::ProcessId procId = base::GetProcId(mSubprocess->GetChildProcessHandle());
+
+  Open(mSubprocess->GetChannel(), procId);
+
+#ifdef MOZ_CODE_COVERAGE
+  Unused << SendShareCodeCoverageMutex(CodeCoverageHandler::Get()->GetMutexHandle(procId));
+#endif
 
   InitInternal(aInitialPriority,
                true, /* Setup off-main thread compositing */
                true  /* Send registered chrome */);
 
   ContentProcessManager::GetSingleton()->AddContentProcess(this);
 
   mHangMonitorActor = ProcessHangMonitor::AddProcess(this);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -90,16 +90,17 @@ using mozilla::OriginAttributes from "mo
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -619,16 +620,19 @@ child:
      * not have changed since the last SetPluginList message. To keep track of
      * this, the chrome process increments an epoch number every time the set of
      * plugins changes. The chrome process sends up the last epoch it observed.
      * If the epoch last seen by the content process is the same, the content
      * process ignores the update. Otherwise the content process updates its
      * list and reloads its plugins.
      **/
     async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins);
+
+    async ShareCodeCoverageMutex(CrossProcessMutexHandle handle);
+
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId,
                             TabId tabId)
         returns (ContentParentId cpId, bool isForBrowser);
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -158,16 +158,19 @@ if CONFIG['OS_ARCH'] != 'WINNT':
 DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
 
 if CONFIG['MOZ_TOOLKIT_SEARCH']:
     DEFINES['MOZ_TOOLKIT_SEARCH'] = True
 
+if CONFIG['MOZ_CODE_COVERAGE']:
+    DEFINES['MOZ_CODE_COVERAGE'] = True
+
 JAR_MANIFESTS += ['jar.mn']
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
 
new file mode 100644
--- /dev/null
+++ b/ipc/glue/CodeCoverageHandler.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mozilla/CodeCoverageHandler.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsAppRunner.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+// The __gcov_dump function writes the coverage counters to gcda files.
+// The __gcov_reset function resets the coverage counters to zero.
+// They are defined at https://github.com/gcc-mirror/gcc/blob/aad93da1a579b9ae23ede6b9cf8523360f0a08b4/libgcc/libgcov-interface.c
+// __gcov_flush is protected by a mutex, __gcov_dump and __gcov_reset aren't.
+// So we are using a CrossProcessMutex to protect them.
+
+extern "C" void __gcov_dump();
+extern "C" void __gcov_reset();
+
+StaticAutoPtr<CodeCoverageHandler> CodeCoverageHandler::instance;
+
+void CodeCoverageHandler::DumpCounters(int)
+{
+  CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex());
+
+  printf_stderr("[CodeCoverage] Requested dump.\n");
+  __gcov_dump();
+  printf_stderr("[CodeCoverage] Dump completed.\n");
+}
+
+void CodeCoverageHandler::ResetCounters(int)
+{
+  CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex());
+
+  printf_stderr("[CodeCoverage] Requested reset.\n");
+  __gcov_reset();
+  printf_stderr("[CodeCoverage] Reset completed.\n");
+}
+
+void CodeCoverageHandler::SetSignalHandlers()
+{
+  printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid());
+
+  struct sigaction dump_sa;
+  dump_sa.sa_handler = CodeCoverageHandler::DumpCounters;
+  dump_sa.sa_flags = SA_RESTART;
+  sigemptyset(&dump_sa.sa_mask);
+  MOZ_ASSERT(sigaction(SIGUSR1, &dump_sa, nullptr) == 0);
+
+  struct sigaction reset_sa;
+  reset_sa.sa_handler = CodeCoverageHandler::ResetCounters;
+  reset_sa.sa_flags = SA_RESTART;
+  sigemptyset(&reset_sa.sa_mask);
+  MOZ_ASSERT(sigaction(SIGUSR2, &reset_sa, nullptr) == 0);
+}
+
+CodeCoverageHandler::CodeCoverageHandler()
+  : mGcovLock("GcovLock")
+{
+  SetSignalHandlers();
+}
+
+CodeCoverageHandler::CodeCoverageHandler(const CrossProcessMutexHandle& aHandle)
+  : mGcovLock(aHandle)
+{
+  SetSignalHandlers();
+}
+
+void CodeCoverageHandler::Init()
+{
+  MOZ_ASSERT(!instance);
+  MOZ_ASSERT(XRE_IsParentProcess());
+  instance = new CodeCoverageHandler();
+  ClearOnShutdown(&instance);
+}
+
+void CodeCoverageHandler::Init(const CrossProcessMutexHandle& aHandle)
+{
+  MOZ_ASSERT(!instance);
+  MOZ_ASSERT(!XRE_IsParentProcess());
+  instance = new CodeCoverageHandler(aHandle);
+  ClearOnShutdown(&instance);
+}
+
+CodeCoverageHandler* CodeCoverageHandler::Get()
+{
+  MOZ_ASSERT(instance);
+  return instance;
+}
+
+CrossProcessMutex* CodeCoverageHandler::GetMutex()
+{
+  return &mGcovLock;
+}
+
+CrossProcessMutexHandle CodeCoverageHandler::GetMutexHandle(int aProcId)
+{
+  return mGcovLock.ShareToProcess(aProcId);
+}
new file mode 100644
--- /dev/null
+++ b/ipc/glue/CodeCoverageHandler.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_codecoveragehandler_h
+#define mozilla_codecoveragehandler_h
+
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ipc/CrossProcessMutex.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+
+class CodeCoverageHandler {
+public:
+  static void Init();
+  static void Init(const CrossProcessMutexHandle& aHandle);
+  static CodeCoverageHandler* Get();
+  CrossProcessMutex* GetMutex();
+  CrossProcessMutexHandle GetMutexHandle(int aProcId);
+
+private:
+  CodeCoverageHandler();
+  CodeCoverageHandler(const CrossProcessMutexHandle& aHandle);
+
+  static StaticAutoPtr<CodeCoverageHandler> instance;
+  CrossProcessMutex mGcovLock;
+
+  DISALLOW_COPY_AND_ASSIGN(CodeCoverageHandler);
+
+  void SetSignalHandlers();
+  static void DumpCounters(int);
+  static void ResetCounters(int);
+};
+
+}
+
+#endif // mozilla_codecoveragehandler_h
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -209,16 +209,25 @@ IPDL_SOURCES = [
 
 LOCAL_INCLUDES += [
     '/dom/ipc',
     '/toolkit/crashreporter',
     '/toolkit/xre',
     '/xpcom/threads',
 ]
 
+if CONFIG['MOZ_CODE_COVERAGE']:
+    SOURCES += [
+        'CodeCoverageHandler.cpp',
+    ]
+
+    EXPORTS.mozilla += [
+        'CodeCoverageHandler.h',
+    ]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_NAME_PIE',
             'MOZ_CHILD_PROCESS_BUNDLE', 'DLL_PREFIX', 'DLL_SUFFIX'):
     DEFINES[var] = '"%s"' % CONFIG[var]
 
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -44,16 +44,20 @@
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #include <windows.h>
 #if defined(MOZ_SANDBOX)
 #include "SandboxBroker.h"
 #endif
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+#include "mozilla/CodeCoverageHandler.h"
+#endif
+
 // all this crap is needed to do the interactive shell stuff
 #include <stdlib.h>
 #include <errno.h>
 #ifdef HAVE_IO_H
 #include <io.h>     /* for isatty() */
 #endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>     /* for isatty() */
@@ -1465,16 +1469,20 @@ XRE_XPCShellMain(int argc, char** argv, 
           SandboxBroker::CacheRulesDirectories();
         } else {
           NS_WARNING("Failed to initialize broker services, sandboxed "
                      "processes will fail to start.");
         }
 #endif
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+        CodeCoverageHandler::Init();
+#endif
+
         {
             JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
             if (!glob) {
                 return 1;
             }
 
             // Even if we're building in a configuration where source is
             // discarded, there's no reason to do that on XPCShell, and doing so
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -59,8 +59,11 @@ LOCAL_INCLUDES += [
     '/dom/workers',
     '/layout/base',
     '/layout/style',
     '/xpcom/reflect/xptinfo',
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-shadow', '-Werror=format']
+
+if CONFIG['MOZ_CODE_COVERAGE']:
+    DEFINES['MOZ_CODE_COVERAGE'] = True
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -218,8 +218,11 @@ FINAL_TARGET_PP_FILES += [
     'platform.ini'
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
 
 if CONFIG['MOZ_IPDL_TESTS']:
     DEFINES['MOZ_IPDL_TESTS'] = True
+
+if CONFIG['MOZ_CODE_COVERAGE']:
+    DEFINES['MOZ_CODE_COVERAGE'] = True
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -219,16 +219,20 @@
 #if defined(XP_LINUX) && !defined(ANDROID)
 #include "mozilla/SandboxInfo.h"
 #elif defined(XP_WIN)
 #include "SandboxBroker.h"
 #include "SandboxPermissions.h"
 #endif
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+#include "mozilla/CodeCoverageHandler.h"
+#endif
+
 extern uint32_t gRestartMode;
 extern void InstallSignalHandlers(const char *ProgramName);
 
 // This workaround is fixed in Rust 1.19. For details, see bug 1358151.
 // Implementation in toolkit/library/rust/shared/lib.rs
 extern "C" {
   void rust_init_please_remove_this_after_updating_rust_1_19();
 }
@@ -4625,16 +4629,20 @@ XREMain::XRE_main(int argc, char* argv[]
   //   WARNING: XPCOM objects created/destroyed from static ctor/dtor: [..]
   // See bug 1279614.
   XRE_CreateStatsObject();
 
 #if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
   SandboxInfo::ThreadingCheck();
 #endif
 
+#ifdef MOZ_CODE_COVERAGE
+  CodeCoverageHandler::Init();
+#endif
+
   char aLocal;
   AutoProfilerInit profilerInit(&aLocal);
 
   PROFILER_LABEL("Startup", "XRE_Main",
     js::ProfileEntry::Category::OTHER);
 
   nsresult rv = NS_OK;