Commit d2031d30 authored by Wez's avatar Wez Committed by Commit Bot

Flush coverage statistics in process fast-exit & debug-break paths.

Defines a base::debug::WriteClangCoverageProfile() API which performs
an explicit call to __llvm_profile_dump() in CLANG_COVERAGE builds, with
locking to ensure thread-safety.

This API is invoked in the base::debug::BreakDebugger() and
base::Process::TerminateCurrentProcessImmediately() APIs, immediately
prior to them terminating the process, to mirror the coverage-writing
step that would be performed via an at-exit handler during a normal
process-exit.

This ensures that we get as complete coverage data as possible from
processes which fast-terminate/break (e.g. EXPECT_DEATH() sub-processes,
e.g. browser child processes, etc).

Bug: 849369
Change-Id: I38262334bb5abf8d5ba40c2c32352b38096905ec
Reviewed-on: https://chromium-review.googlesource.com/1172932Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarMax Moroz <mmoroz@chromium.org>
Commit-Queue: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583882}
parent 6e9f5a9f
...@@ -1305,6 +1305,14 @@ jumbo_component("base") { ...@@ -1305,6 +1305,14 @@ jumbo_component("base") {
} }
} }
if (use_clang_coverage) {
# Call-sites use this conditional on the CLANG_COVERAGE macro, for clarity.
sources += [
"test/clang_coverage.cc",
"test/clang_coverage.h",
]
}
# Allow more direct string conversions on platforms with native utf8 # Allow more direct string conversions on platforms with native utf8
# strings # strings
if (is_mac || is_ios || is_chromeos || is_chromecast || is_fuchsia) { if (is_mac || is_ios || is_chromeos || is_chromecast || is_fuchsia) {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <vector> #include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/test/clang_coverage.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -246,6 +247,10 @@ void DebugBreak() { ...@@ -246,6 +247,10 @@ void DebugBreak() {
#endif #endif
void BreakDebugger() { void BreakDebugger() {
#if defined(CLANG_COVERAGE)
WriteClangCoverageProfile();
#endif
// NOTE: This code MUST be async-signal safe (it's used by in-process // NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here. // stack dumping signal handler). NO malloc or stdio is allowed here.
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <windows.h> #include <windows.h>
#include "base/test/clang_coverage.h"
namespace base { namespace base {
namespace debug { namespace debug {
...@@ -15,6 +17,10 @@ bool BeingDebugged() { ...@@ -15,6 +17,10 @@ bool BeingDebugged() {
} }
void BreakDebugger() { void BreakDebugger() {
#if defined(CLANG_COVERAGE)
WriteClangCoverageProfile();
#endif
if (IsDebugUISuppressed()) if (IsDebugUISuppressed())
_exit(1); _exit(1);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/fuchsia/default_job.h" #include "base/fuchsia/default_job.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/test/clang_coverage.h"
namespace base { namespace base {
...@@ -88,6 +89,9 @@ bool Process::CanBackgroundProcesses() { ...@@ -88,6 +89,9 @@ bool Process::CanBackgroundProcesses() {
// static // static
void Process::TerminateCurrentProcessImmediately(int exit_code) { void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if defined(CLANG_COVERAGE)
WriteClangCoverageProfile();
#endif
_exit(exit_code); _exit(exit_code);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h" #include "base/process/kill.h"
#include "base/test/clang_coverage.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -271,6 +272,9 @@ bool Process::CanBackgroundProcesses() { ...@@ -271,6 +272,9 @@ bool Process::CanBackgroundProcesses() {
// static // static
void Process::TerminateCurrentProcessImmediately(int exit_code) { void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if defined(CLANG_COVERAGE)
WriteClangCoverageProfile();
#endif
_exit(exit_code); _exit(exit_code);
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/process/kill.h" #include "base/process/kill.h"
#include "base/test/clang_coverage.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include <windows.h> #include <windows.h>
...@@ -85,6 +86,9 @@ bool Process::CanBackgroundProcesses() { ...@@ -85,6 +86,9 @@ bool Process::CanBackgroundProcesses() {
// static // static
void Process::TerminateCurrentProcessImmediately(int exit_code) { void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if defined(CLANG_COVERAGE)
WriteClangCoverageProfile();
#endif
::TerminateProcess(GetCurrentProcess(), exit_code); ::TerminateProcess(GetCurrentProcess(), exit_code);
// There is some ambiguity over whether the call above can return. Rather than // There is some ambiguity over whether the call above can return. Rather than
// hitting confusing crashes later on we should crash right here. // hitting confusing crashes later on we should crash right here.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/clang_coverage.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
extern "C" int __llvm_profile_dump(void);
namespace base {
void WriteClangCoverageProfile() {
// __llvm_profile_dump() guarantees that it will not dump coverage information
// if it is being called twice or more. However, it is not thread safe, as it
// is supposed to be called from atexit() handler rather than being called
// directly from random places. Since we have to call it ourselves, we must
// ensure thread safety in order to prevent duplication of coverage counters.
static base::NoDestructor<base::Lock> lock;
base::AutoLock auto_lock(*lock);
__llvm_profile_dump();
}
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TEST_CLANG_COVERAGE_H_
#define BASE_TEST_CLANG_COVERAGE_H_
namespace base {
// Write out the accumulated code coverage profile to the configured file.
// This is used internally by e.g. base::Process and FATAL logging, to cause
// coverage information to be stored even when performing an "immediate" exit
// (or triggering a debug crash), where the automatic at-exit writer will not
// be invoked.
// This call is thread-safe, and will write profiling data at-most-once.
// Call-sites invoke this API only if the CLANG_COVERAGE macro is defined.
void WriteClangCoverageProfile();
} // namespace base
#endif // BASE_TEST_CLANG_COVERAGE_H_
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include "base/message_loop/timer_slack.h" #include "base/message_loop/timer_slack.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
...@@ -78,26 +77,9 @@ ...@@ -78,26 +77,9 @@
#include "content/public/common/content_descriptors.h" #include "content/public/common/content_descriptors.h"
#endif #endif
#if defined(CLANG_COVERAGE)
extern "C" int __llvm_profile_dump(void);
#endif
namespace content { namespace content {
namespace { namespace {
void WriteClangCoverageProfile() {
#if defined(CLANG_COVERAGE)
// __llvm_profile_dump() guarantees that it will not dump coverage information
// if it is being called twice or more. However, it is not thread safe, as it
// is supposed to be called from atexit() handler rather than being called
// directly from random places. Since we have to call it ourselves, we must
// ensure thread safety in order to prevent duplication of coverage counters.
static base::NoDestructor<base::Lock> lock;
base::AutoLock auto_lock(*lock);
__llvm_profile_dump();
#endif
}
// How long to wait for a connection to the browser process before giving up. // How long to wait for a connection to the browser process before giving up.
const int kConnectionTimeoutS = 15; const int kConnectionTimeoutS = 15;
...@@ -112,8 +94,8 @@ base::LazyInstance<base::ThreadLocalPointer<ChildThreadImpl>>::DestructorAtExit ...@@ -112,8 +94,8 @@ base::LazyInstance<base::ThreadLocalPointer<ChildThreadImpl>>::DestructorAtExit
#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
defined(UNDEFINED_SANITIZER) defined(UNDEFINED_SANITIZER)
// A thread delegate that waits for |duration| and then exits the process with // A thread delegate that waits for |duration| and then exits the process
// _exit(0). // immediately, without executing finalizers.
class WaitAndExitDelegate : public base::PlatformThread::Delegate { class WaitAndExitDelegate : public base::PlatformThread::Delegate {
public: public:
explicit WaitAndExitDelegate(base::TimeDelta duration) explicit WaitAndExitDelegate(base::TimeDelta duration)
...@@ -121,8 +103,7 @@ class WaitAndExitDelegate : public base::PlatformThread::Delegate { ...@@ -121,8 +103,7 @@ class WaitAndExitDelegate : public base::PlatformThread::Delegate {
void ThreadMain() override { void ThreadMain() override {
base::PlatformThread::Sleep(duration_); base::PlatformThread::Sleep(duration_);
WriteClangCoverageProfile(); base::Process::TerminateCurrentProcessImmediately(0);
_exit(0);
} }
private: private:
...@@ -182,8 +163,7 @@ class SuicideOnChannelErrorFilter : public IPC::MessageFilter { ...@@ -182,8 +163,7 @@ class SuicideOnChannelErrorFilter : public IPC::MessageFilter {
__lsan_do_leak_check(); __lsan_do_leak_check();
#endif #endif
#else #else
WriteClangCoverageProfile(); base::Process::TerminateCurrentProcessImmediately(0);
_exit(0);
#endif #endif
} }
...@@ -649,12 +629,7 @@ ChildThreadImpl::~ChildThreadImpl() { ...@@ -649,12 +629,7 @@ ChildThreadImpl::~ChildThreadImpl() {
g_lazy_tls.Pointer()->Set(nullptr); g_lazy_tls.Pointer()->Set(nullptr);
} }
void ChildThreadImpl::Shutdown() { void ChildThreadImpl::Shutdown() {}
// The renderer process (and others) can to fast shutdown by calling _exit(0),
// in which case the clang-coverage profile does not get written to the file.
// So force write the profile here before shutting down.
WriteClangCoverageProfile();
}
bool ChildThreadImpl::ShouldBeDestroyed() { bool ChildThreadImpl::ShouldBeDestroyed() {
return true; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment