aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/yassert.cpp
blob: 73ab2980d374a1c651032ebdd8382b6773145259 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "yassert.h"

#include "backtrace.h"
#include "guard.h"
#include "spinlock.h"
#include "src_root.h"

#include <util/datetime/base.h>
#include <util/generic/singleton.h>
#include <util/generic/strbuf.h>
#include <util/generic/string.h>
#include <util/stream/output.h>
#include <util/stream/str.h>
#include <util/string/printf.h>

#include <cstdlib>
#include <stdarg.h>
#include <stdio.h>

#ifdef CLANG_COVERAGE
extern "C" {
    // __llvm_profile_write_file may not be provided if the executable target uses NO_CLANG_COVERAGE() macro and
    // arrives as test's dependency via DEPENDS() macro.
    // That's why we provide a weak no-op implementation for __llvm_profile_write_file,
    // which is used below in the code, to correctly save codecoverage profile before program exits using abort().
    Y_WEAK int __llvm_profile_write_file(void) {
        return 0;
    }
}

#endif

namespace {
    struct TPanicLockHolder: public TAdaptiveLock {
    };
} // namespace
namespace NPrivate {
    [[noreturn]] Y_NO_INLINE void InternalPanicImpl(int line, const char* function, const char* expr, int, int, int, const TStringBuf file, const char* errorMessage, size_t errorMessageSize) noexcept;
} // namespace NPrivate

void ::NPrivate::Panic(const TStaticBuf& file, int line, const char* function, const char* expr, const char* format, ...) noexcept {
    try {
        // We care of panic of first failed thread only
        // Otherwise stderr could contain multiple messages and stack traces shuffled
        auto guard = Guard(*Singleton<TPanicLockHolder>());

        TString errorMsg;
        va_list args;
        va_start(args, format);
        // format has " " prefix to mute GCC warning on empty format
        vsprintf(errorMsg, format[0] == ' ' ? format + 1 : format, args);
        va_end(args);

        constexpr int abiPlaceholder = 0;
        ::NPrivate::InternalPanicImpl(line, function, expr, abiPlaceholder, abiPlaceholder, abiPlaceholder, file.As<TStringBuf>(), errorMsg.c_str(), errorMsg.size());
    } catch (...) {
        // ¯\_(ツ)_/¯
    }

    abort();
}

namespace NPrivate {
    [[noreturn]] Y_NO_INLINE void InternalPanicImpl(int line, const char* function, const char* expr, int, int, int, const TStringBuf file, const char* errorMessage, size_t errorMessageSize) noexcept try {
        TStringBuf errorMsg{errorMessage, errorMessageSize};
        const TString now = TInstant::Now().ToStringLocal();

        TString r;
        TStringOutput o(r);
        if (expr) {
            o << "VERIFY failed (" << now << "): " << errorMsg << Endl;
        } else {
            o << "FAIL (" << now << "): " << errorMsg << Endl;
        }
        o << "  " << file << ":" << line << Endl;
        if (expr) {
            o << "  " << function << "(): requirement " << expr << " failed" << Endl;
        } else {
            o << "  " << function << "() failed" << Endl;
        }
        Cerr << r << Flush;
#ifndef WITH_VALGRIND
        PrintBackTrace();
#endif
#ifdef CLANG_COVERAGE
        if (__llvm_profile_write_file()) {
            Cerr << "Failed to dump clang coverage" << Endl;
        }
#endif
        abort();
    } catch (...) {
        abort();
    }
} // namespace NPrivate