1 | #ifndef C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_ |
2 | #define C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_ |
3 | |
4 | #include <chrono> |
5 | #include <climits> |
6 | #include <ctime> |
7 | #include <fstream> |
8 | #include <iomanip> |
9 | #include <map> |
10 | #include <set> |
11 | #include <sstream> |
12 | #include <string> |
13 | #include <vector> |
14 | |
15 | #include <c10/util/Flags.h> |
16 | |
17 | const char CAFFE2_SEVERITY_PREFIX[] = "FEWIV" ; |
18 | |
19 | namespace c10 { |
20 | |
21 | // Log severity level constants. |
22 | const int GLOG_FATAL = 3; |
23 | const int GLOG_ERROR = 2; |
24 | const int GLOG_WARNING = 1; |
25 | const int GLOG_INFO = 0; |
26 | |
27 | class C10_API MessageLogger { |
28 | public: |
29 | MessageLogger(const char* file, int line, int severity); |
30 | ~MessageLogger(); |
31 | // Return the stream associated with the logger object. |
32 | std::stringstream& stream() { |
33 | return stream_; |
34 | } |
35 | |
36 | private: |
37 | // When there is a fatal log, we simply abort. |
38 | void DealWithFatal() { |
39 | abort(); |
40 | } |
41 | |
42 | const char* tag_; |
43 | std::stringstream stream_; |
44 | int severity_; |
45 | }; |
46 | |
47 | // This class is used to explicitly ignore values in the conditional |
48 | // logging macros. This avoids compiler warnings like "value computed |
49 | // is not used" and "statement has no effect". |
50 | class C10_API LoggerVoidify { |
51 | public: |
52 | LoggerVoidify() = default; |
53 | // This has to be an operator with a precedence lower than << but |
54 | // higher than ?: |
55 | void operator&(const std::ostream& s) {} |
56 | }; |
57 | |
58 | // Log a message and terminate. |
59 | template <class T> |
60 | void LogMessageFatal(const char* file, int line, const T& message) { |
61 | MessageLogger(file, line, GLOG_FATAL).stream() << message; |
62 | } |
63 | |
64 | // Helpers for TORCH_CHECK_NOTNULL(). Two are necessary to support both raw |
65 | // pointers and smart pointers. |
66 | template <typename T> |
67 | T& CheckNotNullCommon(const char* file, int line, const char* names, T& t) { |
68 | if (t == nullptr) { |
69 | LogMessageFatal(file, line, std::string(names)); |
70 | } |
71 | return t; |
72 | } |
73 | |
74 | template <typename T> |
75 | T* CheckNotNull(const char* file, int line, const char* names, T* t) { |
76 | return CheckNotNullCommon(file, line, names, t); |
77 | } |
78 | |
79 | template <typename T> |
80 | T& CheckNotNull(const char* file, int line, const char* names, T& t) { |
81 | return CheckNotNullCommon(file, line, names, t); |
82 | } |
83 | } // namespace c10 |
84 | |
85 | // ---------------------- Logging Macro definitions -------------------------- |
86 | |
87 | static_assert( |
88 | CAFFE2_LOG_THRESHOLD <= ::c10::GLOG_FATAL, |
89 | "CAFFE2_LOG_THRESHOLD should at most be GLOG_FATAL." ); |
90 | // If n is under the compile time caffe log threshold, The _CAFFE_LOG(n) |
91 | // should not generate anything in optimized code. |
92 | #define LOG(n) \ |
93 | if (::c10::GLOG_##n >= CAFFE2_LOG_THRESHOLD) \ |
94 | ::c10::MessageLogger(__FILE__, __LINE__, ::c10::GLOG_##n).stream() |
95 | #define VLOG(n) \ |
96 | if (-n >= CAFFE2_LOG_THRESHOLD) \ |
97 | ::c10::MessageLogger(__FILE__, __LINE__, -n).stream() |
98 | |
99 | #define LOG_IF(n, condition) \ |
100 | if (::c10::GLOG_##n >= CAFFE2_LOG_THRESHOLD && (condition)) \ |
101 | ::c10::MessageLogger(__FILE__, __LINE__, ::c10::GLOG_##n).stream() |
102 | #define VLOG_IF(n, condition) \ |
103 | if (-n >= CAFFE2_LOG_THRESHOLD && (condition)) \ |
104 | ::c10::MessageLogger(__FILE__, __LINE__, -n).stream() |
105 | |
106 | #define VLOG_IS_ON(verboselevel) (CAFFE2_LOG_THRESHOLD <= -(verboselevel)) |
107 | |
108 | // Log with source location information override (to be used in generic |
109 | // warning/error handlers implemented as functions, not macros) |
110 | #define LOG_AT_FILE_LINE(n, file, line) \ |
111 | if (::c10::GLOG_##n >= CAFFE2_LOG_THRESHOLD) \ |
112 | ::c10::MessageLogger(file, line, ::c10::GLOG_##n).stream() |
113 | |
114 | // Log only if condition is met. Otherwise evaluates to void. |
115 | #define FATAL_IF(condition) \ |
116 | condition ? (void)0 \ |
117 | : ::c10::LoggerVoidify() & \ |
118 | ::c10::MessageLogger(__FILE__, __LINE__, ::c10::GLOG_FATAL).stream() |
119 | |
120 | // Check for a given boolean condition. |
121 | #define CHECK(condition) FATAL_IF(condition) << "Check failed: " #condition " " |
122 | |
123 | #ifndef NDEBUG |
124 | // Debug only version of CHECK |
125 | #define DCHECK(condition) FATAL_IF(condition) << "Check failed: " #condition " " |
126 | #define DLOG(severity) LOG(severity) |
127 | #else // NDEBUG |
128 | // Optimized version - generates no code. |
129 | #define DCHECK(condition) \ |
130 | while (false) \ |
131 | CHECK(condition) |
132 | |
133 | #define DLOG(n) \ |
134 | true ? (void)0 \ |
135 | : ::c10::LoggerVoidify() & \ |
136 | ::c10::MessageLogger(__FILE__, __LINE__, ::c10::GLOG_##n).stream() |
137 | #endif // NDEBUG |
138 | |
139 | #define TORCH_CHECK_OP(val1, val2, op) \ |
140 | FATAL_IF(((val1)op(val2))) << "Check failed: " #val1 " " #op " " #val2 " (" \ |
141 | << (val1) << " vs. " << (val2) << ") " |
142 | |
143 | // TORCH_CHECK_OP macro definitions |
144 | #define TORCH_CHECK_EQ(val1, val2) TORCH_CHECK_OP(val1, val2, ==) |
145 | #define TORCH_CHECK_NE(val1, val2) TORCH_CHECK_OP(val1, val2, !=) |
146 | #define TORCH_CHECK_LE(val1, val2) TORCH_CHECK_OP(val1, val2, <=) |
147 | #define TORCH_CHECK_LT(val1, val2) TORCH_CHECK_OP(val1, val2, <) |
148 | #define TORCH_CHECK_GE(val1, val2) TORCH_CHECK_OP(val1, val2, >=) |
149 | #define TORCH_CHECK_GT(val1, val2) TORCH_CHECK_OP(val1, val2, >) |
150 | |
151 | #ifndef NDEBUG |
152 | // Debug only versions of TORCH_CHECK_OP macros. |
153 | #define TORCH_DCHECK_EQ(val1, val2) TORCH_CHECK_OP(val1, val2, ==) |
154 | #define TORCH_DCHECK_NE(val1, val2) TORCH_CHECK_OP(val1, val2, !=) |
155 | #define TORCH_DCHECK_LE(val1, val2) TORCH_CHECK_OP(val1, val2, <=) |
156 | #define TORCH_DCHECK_LT(val1, val2) TORCH_CHECK_OP(val1, val2, <) |
157 | #define TORCH_DCHECK_GE(val1, val2) TORCH_CHECK_OP(val1, val2, >=) |
158 | #define TORCH_DCHECK_GT(val1, val2) TORCH_CHECK_OP(val1, val2, >) |
159 | #else // !NDEBUG |
160 | // These versions generate no code in optimized mode. |
161 | #define TORCH_DCHECK_EQ(val1, val2) \ |
162 | while (false) \ |
163 | TORCH_CHECK_OP(val1, val2, ==) |
164 | #define TORCH_DCHECK_NE(val1, val2) \ |
165 | while (false) \ |
166 | TORCH_CHECK_OP(val1, val2, !=) |
167 | #define TORCH_DCHECK_LE(val1, val2) \ |
168 | while (false) \ |
169 | TORCH_CHECK_OP(val1, val2, <=) |
170 | #define TORCH_DCHECK_LT(val1, val2) \ |
171 | while (false) \ |
172 | TORCH_CHECK_OP(val1, val2, <) |
173 | #define TORCH_DCHECK_GE(val1, val2) \ |
174 | while (false) \ |
175 | TORCH_CHECK_OP(val1, val2, >=) |
176 | #define TORCH_DCHECK_GT(val1, val2) \ |
177 | while (false) \ |
178 | TORCH_CHECK_OP(val1, val2, >) |
179 | #endif // NDEBUG |
180 | |
181 | // Check that a pointer is not null. |
182 | #define TORCH_CHECK_NOTNULL(val) \ |
183 | ::c10::CheckNotNull( \ |
184 | __FILE__, __LINE__, "Check failed: '" #val "' Must be non NULL", (val)) |
185 | |
186 | #ifndef NDEBUG |
187 | // Debug only version of TORCH_CHECK_NOTNULL |
188 | #define TORCH_DCHECK_NOTNULL(val) \ |
189 | ::c10::CheckNotNull( \ |
190 | __FILE__, __LINE__, "Check failed: '" #val "' Must be non NULL", (val)) |
191 | #else // !NDEBUG |
192 | // Optimized version - generates no code. |
193 | #define TORCH_DCHECK_NOTNULL(val) \ |
194 | while (false) \ |
195 | TORCH_CHECK_NOTNULL(val) |
196 | #endif // NDEBUG |
197 | |
198 | // ---------------------- Support for std objects -------------------------- |
199 | // These are adapted from glog to support a limited set of logging capability |
200 | // for STL objects. |
201 | |
202 | namespace std { |
203 | // Forward declare these two, and define them after all the container streams |
204 | // operators so that we can recurse from pair -> container -> container -> pair |
205 | // properly. |
206 | template <class First, class Second> |
207 | std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p); |
208 | } // namespace std |
209 | |
210 | namespace c10 { |
211 | template <class Iter> |
212 | void PrintSequence(std::ostream& ss, Iter begin, Iter end); |
213 | } // namespace c10 |
214 | |
215 | namespace std { |
216 | #define INSTANTIATE_FOR_CONTAINER(container) \ |
217 | template <class... Types> \ |
218 | std::ostream& operator<<( \ |
219 | std::ostream& out, const container<Types...>& seq) { \ |
220 | c10::PrintSequence(out, seq.begin(), seq.end()); \ |
221 | return out; \ |
222 | } |
223 | |
224 | INSTANTIATE_FOR_CONTAINER(std::vector) |
225 | INSTANTIATE_FOR_CONTAINER(std::map) |
226 | INSTANTIATE_FOR_CONTAINER(std::set) |
227 | #undef INSTANTIATE_FOR_CONTAINER |
228 | |
229 | template <class First, class Second> |
230 | inline std::ostream& operator<<( |
231 | std::ostream& out, |
232 | const std::pair<First, Second>& p) { |
233 | out << '(' << p.first << ", " << p.second << ')'; |
234 | return out; |
235 | } |
236 | |
237 | inline std::ostream& operator<<(std::ostream& out, const std::nullptr_t&) { |
238 | out << "(null)" ; |
239 | return out; |
240 | } |
241 | } // namespace std |
242 | |
243 | namespace c10 { |
244 | template <class Iter> |
245 | inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { |
246 | // Output at most 100 elements -- appropriate if used for logging. |
247 | for (int i = 0; begin != end && i < 100; ++i, ++begin) { |
248 | if (i > 0) |
249 | out << ' '; |
250 | out << *begin; |
251 | } |
252 | if (begin != end) { |
253 | out << " ..." ; |
254 | } |
255 | } |
256 | } // namespace c10 |
257 | |
258 | #endif // C10_UTIL_LOGGING_IS_NOT_GOOGLE_GLOG_H_ |
259 | |