We have some significant amount of duplication around CheckFailed functionality. Each sanitizer copy-pasted a chunk of code. Some got random improvements like dealing with recursive failures better. These improvements could benefit all sanitizers, but they don't. Deduplicate CheckFailed logic across sanitizers and let each sanitizer only print the current stack trace. I've tried to dedup stack printing as well, but this got me into cmake hell. So let's keep this part duplicated in each sanitizer for now. Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D102221
100 lines
3.0 KiB
C++
100 lines
3.0 KiB
C++
//===-- sanitizer_termination.cpp -------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// This file contains the Sanitizer termination functions CheckFailed and Die,
|
|
/// and the callback functionalities associated with them.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "sanitizer_common.h"
|
|
#include "sanitizer_libc.h"
|
|
|
|
namespace __sanitizer {
|
|
|
|
static const int kMaxNumOfInternalDieCallbacks = 5;
|
|
static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
|
|
|
|
bool AddDieCallback(DieCallbackType callback) {
|
|
for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
|
|
if (InternalDieCallbacks[i] == nullptr) {
|
|
InternalDieCallbacks[i] = callback;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RemoveDieCallback(DieCallbackType callback) {
|
|
for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
|
|
if (InternalDieCallbacks[i] == callback) {
|
|
internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
|
|
sizeof(InternalDieCallbacks[0]) *
|
|
(kMaxNumOfInternalDieCallbacks - i - 1));
|
|
InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static DieCallbackType UserDieCallback;
|
|
void SetUserDieCallback(DieCallbackType callback) {
|
|
UserDieCallback = callback;
|
|
}
|
|
|
|
void NORETURN Die() {
|
|
if (UserDieCallback)
|
|
UserDieCallback();
|
|
for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
|
|
if (InternalDieCallbacks[i])
|
|
InternalDieCallbacks[i]();
|
|
}
|
|
if (common_flags()->abort_on_error)
|
|
Abort();
|
|
internal__exit(common_flags()->exitcode);
|
|
}
|
|
|
|
static void (*CheckUnwindCallback)();
|
|
void SetCheckUnwindCallback(void (*callback)()) {
|
|
CheckUnwindCallback = callback;
|
|
}
|
|
|
|
void NORETURN CheckFailed(const char *file, int line, const char *cond,
|
|
u64 v1, u64 v2) {
|
|
u32 tid = GetTid();
|
|
Printf("%s: CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx) (tid=%u)\n",
|
|
SanitizerToolName, StripModuleName(file), line, cond, (uptr)v1,
|
|
(uptr)v2, tid);
|
|
static atomic_uint32_t first_tid;
|
|
u32 cmp = 0;
|
|
if (!atomic_compare_exchange_strong(&first_tid, &cmp, tid,
|
|
memory_order_relaxed)) {
|
|
if (cmp == tid) {
|
|
// Recursing into CheckFailed.
|
|
} else {
|
|
// Another thread fails already, let it print the stack and terminate.
|
|
SleepForSeconds(2);
|
|
}
|
|
Trap();
|
|
}
|
|
if (CheckUnwindCallback)
|
|
CheckUnwindCallback();
|
|
Die();
|
|
}
|
|
|
|
} // namespace __sanitizer
|
|
|
|
using namespace __sanitizer;
|
|
|
|
extern "C" {
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
void __sanitizer_set_death_callback(void (*callback)(void)) {
|
|
SetUserDieCallback(callback);
|
|
}
|
|
} // extern "C"
|