1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include <stdio.h>
7 #ifdef XP_WIN
8 #  include <process.h>
9 #  define getpid _getpid
10 #else
11 #  include <signal.h>
12 #  include <unistd.h>
13 #endif
14 #include "mozilla/dom/ScriptSettings.h"  // for AutoJSAPI
15 #include "mozilla/CodeCoverageHandler.h"
16 #include "mozilla/ClearOnShutdown.h"
17 #include "mozilla/DebugOnly.h"
18 #include "nsAppRunner.h"
19 #include "nsIOutputStream.h"
20 #include "nsNetUtil.h"
21 #include "nsPrintfCString.h"
22 #include "prtime.h"
23 
24 using namespace mozilla;
25 
26 // The __gcov_flush function writes the coverage counters to gcda files and then
27 // resets them to zero. It is defined at
28 // https://github.com/gcc-mirror/gcc/blob/aad93da1a579b9ae23ede6b9cf8523360f0a08b4/libgcc/libgcov-interface.c.
29 // __gcov_flush is protected by a mutex in GCC, but not in LLVM, so we are using
30 // a CrossProcessMutex to protect it.
31 
32 // We rename __gcov_flush to __custom_llvm_gcov_flush in our build of LLVM for
33 // Linux, to avoid naming clashes in builds which mix GCC and LLVM. So, when we
34 // are building with LLVM exclusively, we need to use __custom_llvm_gcov_flush
35 // instead.
36 #if !defined(XP_WIN) && defined(__clang__)
37 #  define __gcov_flush __custom_llvm_gcov_flush
38 #endif
39 
40 extern "C" void __gcov_flush();
41 
42 StaticAutoPtr<CodeCoverageHandler> CodeCoverageHandler::instance;
43 
FlushCounters()44 void CodeCoverageHandler::FlushCounters() {
45   printf_stderr("[CodeCoverage] Requested flush for %d.\n", getpid());
46 
47   CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex());
48 
49   __gcov_flush();
50 
51   printf_stderr("[CodeCoverage] flush completed.\n");
52 
53   const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR");
54   if (!outDir || *outDir == 0) {
55     return;
56   }
57 
58   dom::AutoJSAPI jsapi;
59   jsapi.Init();
60   size_t length;
61   JS::UniqueChars result = js::GetCodeCoverageSummaryAll(jsapi.cx(), &length);
62   if (!result) {
63     return;
64   }
65 
66   nsCOMPtr<nsIFile> file;
67 
68   nsresult rv = NS_NewNativeLocalFile(nsDependentCString(outDir), false,
69                                       getter_AddRefs(file));
70   MOZ_ASSERT(NS_SUCCEEDED(rv));
71 
72   rv = file->AppendNative(
73       nsPrintfCString("%lu-%d.info", PR_Now() / PR_USEC_PER_MSEC, getpid()));
74 
75   rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
76   MOZ_ASSERT(NS_SUCCEEDED(rv));
77 
78   nsCOMPtr<nsIOutputStream> outputStream;
79   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
80   MOZ_ASSERT(NS_SUCCEEDED(rv));
81 
82   char* data = result.get();
83   while (length) {
84     uint32_t n = 0;
85     rv = outputStream->Write(data, length, &n);
86     MOZ_ASSERT(NS_SUCCEEDED(rv));
87     data += n;
88     length -= n;
89   }
90 
91   rv = outputStream->Close();
92   MOZ_ASSERT(NS_SUCCEEDED(rv));
93 
94   printf_stderr("[CodeCoverage] JS flush completed.\n");
95 }
96 
FlushCountersSignalHandler(int)97 void CodeCoverageHandler::FlushCountersSignalHandler(int) { FlushCounters(); }
98 
SetSignalHandlers()99 void CodeCoverageHandler::SetSignalHandlers() {
100 #ifndef XP_WIN
101   printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid());
102 
103   struct sigaction dump_sa;
104   dump_sa.sa_handler = CodeCoverageHandler::FlushCountersSignalHandler;
105   dump_sa.sa_flags = SA_RESTART;
106   sigemptyset(&dump_sa.sa_mask);
107   DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
108   MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");
109 #endif
110 }
111 
CodeCoverageHandler()112 CodeCoverageHandler::CodeCoverageHandler() : mGcovLock("GcovLock") {
113   SetSignalHandlers();
114 }
115 
CodeCoverageHandler(const CrossProcessMutexHandle & aHandle)116 CodeCoverageHandler::CodeCoverageHandler(const CrossProcessMutexHandle& aHandle)
117     : mGcovLock(aHandle) {
118   SetSignalHandlers();
119 }
120 
Init()121 void CodeCoverageHandler::Init() {
122   MOZ_ASSERT(!instance);
123   MOZ_ASSERT(XRE_IsParentProcess());
124   instance = new CodeCoverageHandler();
125   ClearOnShutdown(&instance);
126 }
127 
Init(const CrossProcessMutexHandle & aHandle)128 void CodeCoverageHandler::Init(const CrossProcessMutexHandle& aHandle) {
129   MOZ_ASSERT(!instance);
130   MOZ_ASSERT(!XRE_IsParentProcess());
131   instance = new CodeCoverageHandler(aHandle);
132   ClearOnShutdown(&instance);
133 }
134 
Get()135 CodeCoverageHandler* CodeCoverageHandler::Get() {
136   MOZ_ASSERT(instance);
137   return instance;
138 }
139 
GetMutex()140 CrossProcessMutex* CodeCoverageHandler::GetMutex() { return &mGcovLock; }
141 
GetMutexHandle(int aProcId)142 CrossProcessMutexHandle CodeCoverageHandler::GetMutexHandle(int aProcId) {
143   return mGcovLock.ShareToProcess(aProcId);
144 }
145