summaryrefslogtreecommitdiff
path: root/tools/standalone_miri/debug.hpp
blob: b3b0d76fe2cc9aa701be957cd1f31f139d165a4a (plain)
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
95
96
97
98
99
100
101
102
103
104
105
/*
 * mrustc Standalone MIRI
 * - by John Hodge (Mutabah)
 *
 * debug.hpp
 * - Interpreter debug logging
 */
#pragma once

#include <iostream>
#include <functional>
#include <memory>

enum class DebugLevel {
    Trace,
    Debug,
    Notice,
    Warn,
    Error,
    Fatal,
    Bug,
};

class DebugSink
{
    static unsigned s_indent;
    static ::std::unique_ptr<std::ofstream> s_out_file;
    ::std::ostream& m_inner;
    DebugSink(::std::ostream& inner);
public:
    ~DebugSink();

    template<typename T>
    ::std::ostream& operator<<(const T& v) { return m_inner << v; }

    static void set_output_file(const ::std::string& s);
    static bool enabled(const char* fcn_name);
    static DebugSink get(const char* fcn_name, const char* file, unsigned line, DebugLevel lvl);
    // TODO: Add a way to insert an annotation before/after an abort/warning/... that indicates what input location caused it.
    //static void set_position();

    static void inc_indent();
    static void dec_indent();
};
template<typename T, typename U>
class FunctionTrace
{
    const char* m_fname;
    const char* m_file;
    unsigned m_line;
    U   m_exit;
public:
    FunctionTrace(const char* fname, const char* file, unsigned line, T entry, U exit):
        m_fname(fname),
        m_file(file),
        m_line(line),
        m_exit(exit)
    {
        if( DebugSink::enabled(fname) ) {
            auto s = DebugSink::get(fname, file, line, DebugLevel::Debug);
            s << "(";
            (entry)(s);
            s << ")";
            DebugSink::inc_indent();
        }
    }
    ~FunctionTrace() {
        if( DebugSink::enabled(m_fname) ) {
            DebugSink::dec_indent();
            auto s = DebugSink::get(m_fname, m_file, m_line, DebugLevel::Debug);
            s << "(";
            m_exit(s);
            s << ")";
        }
    }
};
template<typename T, typename U>
FunctionTrace<T,U> FunctionTrace_d(const char* fname, const char* file, unsigned line, T entry, U exit) {
    return FunctionTrace<T,U>(fname, file, line, entry, exit);
}

struct DebugExceptionTodo:
    public ::std::exception
{
    const char* what() const noexcept override {
        return "TODO hit";
    }
};
struct DebugExceptionError:
    public ::std::exception
{
    const char* what() const noexcept override {
        return "error";
    }
};

#define TRACE_FUNCTION_R(entry, exit) auto ftg##__LINE__ = FunctionTrace_d(__FUNCTION__,__FILE__,__LINE__,[&](DebugSink& FunctionTrace_ss){FunctionTrace_ss << entry;}, [&](DebugSink& FunctionTrace_ss) {FunctionTrace_ss << exit;} )
#define LOG_TRACE(strm) do { if(DebugSink::enabled(__FUNCTION__)) DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Trace) << strm; } while(0)
#define LOG_DEBUG(strm) do { if(DebugSink::enabled(__FUNCTION__)) DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Debug) << strm; } while(0)
#define LOG_NOTICE(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Notice) << strm; } while(0)
#define LOG_ERROR(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Error) << strm; throw DebugExceptionError{}; } while(0)
#define LOG_FATAL(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Fatal) << strm; exit(1); } while(0)
#define LOG_TODO(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Bug) << "TODO: " << strm; throw DebugExceptionTodo{}; } while(0)
#define LOG_BUG(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Bug) << "BUG: " << strm; abort(); } while(0)
#define LOG_ASSERT(cnd,strm) do { if( !(cnd) ) { LOG_BUG("Assertion failure: " #cnd " - " << strm); } } while(0)