| //===-- 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" |