diff options
Diffstat (limited to 'tools/code-coverage/CodeCoverageHandler.cpp')
-rw-r--r-- | tools/code-coverage/CodeCoverageHandler.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/tools/code-coverage/CodeCoverageHandler.cpp b/tools/code-coverage/CodeCoverageHandler.cpp new file mode 100644 index 0000000000..fbe7494c6b --- /dev/null +++ b/tools/code-coverage/CodeCoverageHandler.cpp @@ -0,0 +1,158 @@ +/* -*- 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 <stdio.h> +#ifdef XP_WIN +# include <process.h> +# define getpid _getpid +#else +# include <signal.h> +# include <unistd.h> +#endif +#include "js/experimental/CodeCoverage.h" +#include "mozilla/Atomics.h" +#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI +#include "mozilla/CodeCoverageHandler.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/DebugOnly.h" +#include "nsAppRunner.h" +#include "nsIFile.h" +#include "nsIOutputStream.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "prtime.h" + +using namespace mozilla; + +// The __gcov_flush function writes the coverage counters to gcda files and then +// resets them to zero. It is defined at +// https://github.com/gcc-mirror/gcc/blob/aad93da1a579b9ae23ede6b9cf8523360f0a08b4/libgcc/libgcov-interface.c. +// __gcov_flush is protected by a mutex in GCC, but not in LLVM, so we are using +// a CrossProcessMutex to protect it. + +extern "C" void __gcov_flush(); +extern "C" void __gcov_dump(); +extern "C" void __gcov_reset(); + +StaticAutoPtr<CodeCoverageHandler> CodeCoverageHandler::instance; + +void CodeCoverageHandler::FlushCounters(const bool initialized) { + static Atomic<bool> hasBeenInitialized(false); + if (!hasBeenInitialized) { + hasBeenInitialized = initialized; + return; + } + + printf_stderr("[CodeCoverage] Requested flush for %d.\n", getpid()); + + CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); + +#if defined(__clang__) && __clang_major__ >= 12 + __gcov_dump(); + __gcov_reset(); +#else + __gcov_flush(); +#endif + + printf_stderr("[CodeCoverage] flush completed.\n"); + + const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR"); + if (!outDir || *outDir == 0) { + return; + } + + dom::AutoJSAPI jsapi; + jsapi.Init(); + size_t length; + JS::UniqueChars result = js::GetCodeCoverageSummaryAll(jsapi.cx(), &length); + if (!result) { + return; + } + + nsCOMPtr<nsIFile> file; + + nsresult rv = NS_NewNativeLocalFile(nsDependentCString(outDir), false, + getter_AddRefs(file)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + rv = file->AppendNative( + nsPrintfCString("%lu-%d.info", PR_Now() / PR_USEC_PER_MSEC, getpid())); + + rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsCOMPtr<nsIOutputStream> outputStream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + char* data = result.get(); + while (length) { + uint32_t n = 0; + rv = outputStream->Write(data, length, &n); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + data += n; + length -= n; + } + + rv = outputStream->Close(); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + printf_stderr("[CodeCoverage] JS flush completed.\n"); +} + +void CodeCoverageHandler::FlushCountersSignalHandler(int) { FlushCounters(); } + +void CodeCoverageHandler::SetSignalHandlers() { +#ifndef XP_WIN + printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid()); + + struct sigaction dump_sa; + dump_sa.sa_handler = CodeCoverageHandler::FlushCountersSignalHandler; + dump_sa.sa_flags = SA_RESTART; + sigemptyset(&dump_sa.sa_mask); + DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr); + MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler"); +#endif +} + +CodeCoverageHandler::CodeCoverageHandler() : mGcovLock("GcovLock") { + SetSignalHandlers(); +} + +CodeCoverageHandler::CodeCoverageHandler(CrossProcessMutexHandle aHandle) + : mGcovLock(std::move(aHandle)) { + SetSignalHandlers(); +} + +void CodeCoverageHandler::Init() { + MOZ_ASSERT(!instance); + MOZ_ASSERT(XRE_IsParentProcess()); + instance = new CodeCoverageHandler(); + ClearOnShutdown(&instance); + + // Don't really flush but just make FlushCounters usable. + FlushCounters(true); +} + +void CodeCoverageHandler::Init(CrossProcessMutexHandle aHandle) { + MOZ_ASSERT(!instance); + MOZ_ASSERT(!XRE_IsParentProcess()); + instance = new CodeCoverageHandler(std::move(aHandle)); + ClearOnShutdown(&instance); + + // Don't really flush but just make FlushCounters usable. + FlushCounters(true); +} + +CodeCoverageHandler* CodeCoverageHandler::Get() { + MOZ_ASSERT(instance); + return instance; +} + +CrossProcessMutex* CodeCoverageHandler::GetMutex() { return &mGcovLock; } + +CrossProcessMutexHandle CodeCoverageHandler::GetMutexHandle() { + return mGcovLock.CloneHandle(); +} |