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)
|