1 | /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. |
2 | |
3 | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | you may not use this file except in compliance with the License. |
5 | You may obtain a copy of the License at |
6 | |
7 | http://www.apache.org/licenses/LICENSE-2.0 |
8 | |
9 | Unless required by applicable law or agreed to in writing, software |
10 | distributed under the License is distributed on an "AS IS" BASIS, |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | See the License for the specific language governing permissions and |
13 | limitations under the License. |
14 | ==============================================================================*/ |
15 | |
16 | #if defined(_WIN32) |
17 | // prevent compile error because MSVC doesn't realize in debug build that |
18 | // LOG(FATAL) finally invokes abort() |
19 | #pragma warning(disable : 4716) |
20 | #endif // _WIN32 |
21 | |
22 | #ifndef TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_ |
23 | #define TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_ |
24 | |
25 | // IWYU pragma: private, include "third_party/tensorflow/tsl/platform/logging.h" |
26 | // IWYU pragma: friend third_party/tensorflow/tsl/platform/logging.h |
27 | |
28 | #include <atomic> |
29 | #include <limits> |
30 | #include <memory> |
31 | #include <sstream> |
32 | #include <string> |
33 | #include <vector> |
34 | |
35 | #include "absl/base/log_severity.h" |
36 | #include "absl/strings/string_view.h" |
37 | #include "tensorflow/tsl/platform/macros.h" |
38 | #include "tensorflow/tsl/platform/types.h" |
39 | |
40 | // TODO(mrry): Prevent this Windows.h #define from leaking out of our headers. |
41 | #undef ERROR |
42 | |
43 | namespace tsl { |
44 | const int INFO = 0; // base_logging::INFO; |
45 | const int WARNING = 1; // base_logging::WARNING; |
46 | const int ERROR = 2; // base_logging::ERROR; |
47 | const int FATAL = 3; // base_logging::FATAL; |
48 | const int NUM_SEVERITIES = 4; // base_logging::NUM_SEVERITIES; |
49 | |
50 | namespace internal { |
51 | |
52 | // Emit "message" as a log message to the log for the specified |
53 | // "severity" as if it came from a LOG call at "fname:line" |
54 | void LogString(const char* fname, int line, int severity, |
55 | const std::string& message); |
56 | |
57 | class LogMessage : public std::basic_ostringstream<char> { |
58 | public: |
59 | LogMessage(const char* fname, int line, int severity); |
60 | ~LogMessage() override; |
61 | |
62 | // Change the location of the log message. |
63 | LogMessage& AtLocation(const char* fname, int line); |
64 | |
65 | // Returns the maximum log level for VLOG statements. |
66 | // E.g., if MaxVLogLevel() is 2, then VLOG(2) statements will produce output, |
67 | // but VLOG(3) will not. Defaults to 0. |
68 | static int64_t MaxVLogLevel(); |
69 | |
70 | // Returns whether VLOG level lvl is activated for the file fname. |
71 | // |
72 | // E.g. if the environment variable TF_CPP_VMODULE contains foo=3 and fname is |
73 | // foo.cc and lvl is <= 3, this will return true. It will also return true if |
74 | // the level is lower or equal to TF_CPP_MAX_VLOG_LEVEL (default zero). |
75 | // |
76 | // It is expected that the result of this query will be cached in the VLOG-ing |
77 | // call site to avoid repeated lookups. This routine performs a hash-map |
78 | // access against the VLOG-ing specification provided by the env var. |
79 | static bool VmoduleActivated(const char* fname, int level); |
80 | |
81 | protected: |
82 | void GenerateLogMessage(); |
83 | |
84 | private: |
85 | const char* fname_; |
86 | int line_; |
87 | int severity_; |
88 | }; |
89 | |
90 | // Uses the lower operator & precedence to voidify a LogMessage reference, so |
91 | // that the ternary VLOG() implementation is balanced, type wise. |
92 | struct Voidifier { |
93 | template <typename T> |
94 | void operator&(const T&) const {} |
95 | }; |
96 | |
97 | // LogMessageFatal ensures the process will exit in failure after |
98 | // logging this message. |
99 | class LogMessageFatal : public LogMessage { |
100 | public: |
101 | LogMessageFatal(const char* file, int line) TF_ATTRIBUTE_COLD; |
102 | TF_ATTRIBUTE_NORETURN ~LogMessageFatal() override; |
103 | }; |
104 | |
105 | // LogMessageNull supports the DVLOG macro by simply dropping any log messages. |
106 | class LogMessageNull : public std::basic_ostringstream<char> { |
107 | public: |
108 | LogMessageNull() {} |
109 | ~LogMessageNull() override {} |
110 | }; |
111 | |
112 | #define _TF_LOG_INFO \ |
113 | ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::INFO) |
114 | #define _TF_LOG_WARNING \ |
115 | ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::WARNING) |
116 | #define _TF_LOG_ERROR \ |
117 | ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::ERROR) |
118 | #define _TF_LOG_FATAL ::tsl::internal::LogMessageFatal(__FILE__, __LINE__) |
119 | |
120 | #define _TF_LOG_QFATAL _TF_LOG_FATAL |
121 | |
122 | #define LOG(severity) _TF_LOG_##severity |
123 | |
124 | #ifdef IS_MOBILE_PLATFORM |
125 | |
126 | // Turn VLOG off when under mobile devices for considerations of binary size. |
127 | #define VLOG_IS_ON(lvl) ((lvl) <= 0) |
128 | |
129 | #else |
130 | |
131 | // Otherwise, set TF_CPP_MAX_VLOG_LEVEL environment to update minimum log level |
132 | // of VLOG, or TF_CPP_VMODULE to set the minimum log level for individual |
133 | // translation units. |
134 | #define VLOG_IS_ON(lvl) \ |
135 | (([](int level, const char* fname) { \ |
136 | static const bool vmodule_activated = \ |
137 | ::tsl::internal::LogMessage::VmoduleActivated(fname, level); \ |
138 | return vmodule_activated; \ |
139 | })(lvl, __FILE__)) |
140 | |
141 | #endif |
142 | |
143 | #define VLOG(level) \ |
144 | TF_PREDICT_TRUE(!VLOG_IS_ON(level)) \ |
145 | ? (void)0 \ |
146 | : ::tsl::internal::Voidifier() & \ |
147 | ::tsl::internal::LogMessage(__FILE__, __LINE__, tsl::INFO) |
148 | |
149 | // `DVLOG` behaves like `VLOG` in debug mode (i.e. `#ifndef NDEBUG`). |
150 | // Otherwise, it compiles away and does nothing. |
151 | #ifndef NDEBUG |
152 | #define DVLOG VLOG |
153 | #else |
154 | #define DVLOG(verbose_level) \ |
155 | while (false && (verbose_level) > 0) ::tsl::internal::LogMessageNull() |
156 | #endif |
157 | |
158 | class LogEveryNState { |
159 | public: |
160 | bool ShouldLog(int n); |
161 | uint32_t counter() { return counter_.load(std::memory_order_relaxed); } |
162 | |
163 | private: |
164 | std::atomic<uint32> counter_{0}; |
165 | }; |
166 | |
167 | class LogFirstNState { |
168 | public: |
169 | bool ShouldLog(int n); |
170 | uint32 counter() { return counter_.load(std::memory_order_relaxed); } |
171 | |
172 | private: |
173 | std::atomic<uint32> counter_{0}; |
174 | }; |
175 | |
176 | class LogEveryPow2State { |
177 | public: |
178 | bool ShouldLog(int ignored); |
179 | uint32 counter() { return counter_.load(std::memory_order_relaxed); } |
180 | |
181 | private: |
182 | std::atomic<uint32> counter_{0}; |
183 | }; |
184 | |
185 | class LogEveryNSecState { |
186 | public: |
187 | bool ShouldLog(double seconds); |
188 | uint32 counter() { return counter_.load(std::memory_order_relaxed); } |
189 | |
190 | private: |
191 | std::atomic<uint32> counter_{0}; |
192 | // Cycle count according to CycleClock that we should next log at. |
193 | std::atomic<int64_t> next_log_time_cycles_{0}; |
194 | }; |
195 | |
196 | // This macro has a lot going on! |
197 | // |
198 | // * A local static (`logging_internal_stateful_condition_state`) is |
199 | // declared in a scope such that each `LOG_EVERY_N` (etc.) line has its own |
200 | // state. |
201 | // * `COUNTER`, the third variable, is used to support `<< COUNTER`. It is not |
202 | // mangled, so shadowing can be a problem, albeit more of a |
203 | // shoot-yourself-in-the-foot one. Don't name your variables `COUNTER`. |
204 | // * A single for loop can declare state and also test |
205 | // `condition && state.ShouldLog()`, but there's no way to constrain it to run |
206 | // only once (or not at all) without declaring another variable. The outer |
207 | // for-loop declares this variable (`do_log`). |
208 | // * Using for loops instead of if statements means there's no risk of an |
209 | // ambiguous dangling else statement. |
210 | #define LOGGING_INTERNAL_STATEFUL_CONDITION(kind, condition, arg) \ |
211 | for (bool logging_internal_stateful_condition_do_log(condition); \ |
212 | logging_internal_stateful_condition_do_log; \ |
213 | logging_internal_stateful_condition_do_log = false) \ |
214 | for (static ::tsl::internal::Log##kind##State \ |
215 | logging_internal_stateful_condition_state; \ |
216 | logging_internal_stateful_condition_do_log && \ |
217 | logging_internal_stateful_condition_state.ShouldLog(arg); \ |
218 | logging_internal_stateful_condition_do_log = false) \ |
219 | for (const uint32_t COUNTER ABSL_ATTRIBUTE_UNUSED = \ |
220 | logging_internal_stateful_condition_state.counter(); \ |
221 | logging_internal_stateful_condition_do_log; \ |
222 | logging_internal_stateful_condition_do_log = false) |
223 | |
224 | // An instance of `LOG_EVERY_N` increments a hidden zero-initialized counter |
225 | // every time execution passes through it and logs the specified message when |
226 | // the counter's value is a multiple of `n`, doing nothing otherwise. Each |
227 | // instance has its own counter. The counter's value can be logged by streaming |
228 | // the symbol `COUNTER`. `LOG_EVERY_N` is thread-safe. |
229 | // Example: |
230 | // |
231 | // for (const auto& user : all_users) { |
232 | // LOG_EVERY_N(INFO, 1000) << "Processing user #" << COUNTER; |
233 | // ProcessUser(user); |
234 | // } |
235 | #define LOG_EVERY_N(severity, n) \ |
236 | LOGGING_INTERNAL_STATEFUL_CONDITION(EveryN, true, n) \ |
237 | LOG(severity) |
238 | // `LOG_FIRST_N` behaves like `LOG_EVERY_N` except that the specified message is |
239 | // logged when the counter's value is less than `n`. `LOG_FIRST_N` is |
240 | // thread-safe. |
241 | #define LOG_FIRST_N(severity, n) \ |
242 | LOGGING_INTERNAL_STATEFUL_CONDITION(FirstN, true, n) \ |
243 | LOG(severity) |
244 | // `LOG_EVERY_POW_2` behaves like `LOG_EVERY_N` except that the specified |
245 | // message is logged when the counter's value is a power of 2. |
246 | // `LOG_EVERY_POW_2` is thread-safe. |
247 | #define LOG_EVERY_POW_2(severity) \ |
248 | LOGGING_INTERNAL_STATEFUL_CONDITION(EveryPow2, true, 0) \ |
249 | LOG(severity) |
250 | // An instance of `LOG_EVERY_N_SEC` uses a hidden state variable to log the |
251 | // specified message at most once every `n_seconds`. A hidden counter of |
252 | // executions (whether a message is logged or not) is also maintained and can be |
253 | // logged by streaming the symbol `COUNTER`. `LOG_EVERY_N_SEC` is thread-safe. |
254 | // Example: |
255 | // |
256 | // LOG_EVERY_N_SEC(INFO, 2.5) << "Got " << COUNTER << " cookies so far"; |
257 | #define LOG_EVERY_N_SEC(severity, n_seconds) \ |
258 | LOGGING_INTERNAL_STATEFUL_CONDITION(EveryNSec, true, n_seconds) \ |
259 | LOG(severity) |
260 | |
261 | // CHECK dies with a fatal error if condition is not true. It is *not* |
262 | // controlled by NDEBUG, so the check will be executed regardless of |
263 | // compilation mode. Therefore, it is safe to do things like: |
264 | // CHECK(fp->Write(x) == 4) |
265 | #define CHECK(condition) \ |
266 | if (TF_PREDICT_FALSE(!(condition))) \ |
267 | LOG(FATAL) << "Check failed: " #condition " " |
268 | |
269 | // Function is overloaded for integral types to allow static const |
270 | // integrals declared in classes and not defined to be used as arguments to |
271 | // CHECK* macros. It's not encouraged though. |
272 | template <typename T> |
273 | inline const T& GetReferenceableValue(const T& t) { |
274 | return t; |
275 | } |
276 | inline char GetReferenceableValue(char t) { return t; } |
277 | inline unsigned char GetReferenceableValue(unsigned char t) { return t; } |
278 | inline signed char GetReferenceableValue(signed char t) { return t; } |
279 | inline int16 GetReferenceableValue(int16_t t) { return t; } |
280 | inline uint16 GetReferenceableValue(uint16 t) { return t; } |
281 | inline int GetReferenceableValue(int t) { return t; } |
282 | inline unsigned int GetReferenceableValue(unsigned int t) { return t; } |
283 | inline int64_t GetReferenceableValue(int64_t t) { return t; } |
284 | inline uint64 GetReferenceableValue(uint64 t) { return t; } |
285 | |
286 | // This formats a value for a failing CHECK_XX statement. Ordinarily, |
287 | // it uses the definition for operator<<, with a few special cases below. |
288 | template <typename T> |
289 | inline void MakeCheckOpValueString(std::ostream* os, const T& v) { |
290 | (*os) << v; |
291 | } |
292 | |
293 | // Overrides for char types provide readable values for unprintable |
294 | // characters. |
295 | template <> |
296 | void MakeCheckOpValueString(std::ostream* os, const char& v); |
297 | template <> |
298 | void MakeCheckOpValueString(std::ostream* os, const signed char& v); |
299 | template <> |
300 | void MakeCheckOpValueString(std::ostream* os, const unsigned char& v); |
301 | |
302 | #if LANG_CXX11 |
303 | // We need an explicit specialization for std::nullptr_t. |
304 | template <> |
305 | void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& v); |
306 | #endif |
307 | |
308 | // A container for a string pointer which can be evaluated to a bool - |
309 | // true iff the pointer is non-NULL. |
310 | struct CheckOpString { |
311 | explicit CheckOpString(string* str) : str_(str) {} |
312 | // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), |
313 | // so there's no point in cleaning up str_. |
314 | explicit operator bool() const { return TF_PREDICT_FALSE(str_ != nullptr); } |
315 | string* str_; |
316 | }; |
317 | |
318 | // Build the error message string. Specify no inlining for code size. |
319 | template <typename T1, typename T2> |
320 | string* MakeCheckOpString(const T1& v1, const T2& v2, |
321 | const char* exprtext) TF_ATTRIBUTE_NOINLINE; |
322 | |
323 | // A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX |
324 | // statement. See MakeCheckOpString for sample usage. Other |
325 | // approaches were considered: use of a template method (e.g., |
326 | // base::BuildCheckOpString(exprtext, base::Print<T1>, &v1, |
327 | // base::Print<T2>, &v2), however this approach has complications |
328 | // related to volatile arguments and function-pointer arguments). |
329 | class CheckOpMessageBuilder { |
330 | public: |
331 | // Inserts "exprtext" and " (" to the stream. |
332 | explicit CheckOpMessageBuilder(const char* exprtext); |
333 | // Deletes "stream_". |
334 | ~CheckOpMessageBuilder(); |
335 | // For inserting the first variable. |
336 | std::ostream* ForVar1() { return stream_; } |
337 | // For inserting the second variable (adds an intermediate " vs. "). |
338 | std::ostream* ForVar2(); |
339 | // Get the result (inserts the closing ")"). |
340 | string* NewString(); |
341 | |
342 | private: |
343 | std::ostringstream* stream_; |
344 | }; |
345 | |
346 | template <typename T1, typename T2> |
347 | string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) { |
348 | CheckOpMessageBuilder comb(exprtext); |
349 | MakeCheckOpValueString(comb.ForVar1(), v1); |
350 | MakeCheckOpValueString(comb.ForVar2(), v2); |
351 | return comb.NewString(); |
352 | } |
353 | |
354 | // Helper functions for CHECK_OP macro. |
355 | // We use the full name Check_EQ, Check_NE, etc. in case the file including |
356 | // base/logging.h provides its own #defines for the simpler names EQ, NE, etc. |
357 | // This happens if, for example, those are used as token names in a |
358 | // yacc grammar. |
359 | // The (int, int) overload works around the issue that the compiler |
360 | // will not instantiate the template version of the function on values of |
361 | // unnamed enum type - see comment below. |
362 | #define TF_DEFINE_CHECK_OP_IMPL(name, op) \ |
363 | template <typename T1, typename T2> \ |
364 | inline string* name##Impl(const T1& v1, const T2& v2, \ |
365 | const char* exprtext) { \ |
366 | if (TF_PREDICT_TRUE(v1 op v2)) \ |
367 | return NULL; \ |
368 | else \ |
369 | return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext); \ |
370 | } \ |
371 | inline string* name##Impl(int v1, int v2, const char* exprtext) { \ |
372 | return name##Impl<int, int>(v1, v2, exprtext); \ |
373 | } |
374 | |
375 | // The (size_t, int) and (int, size_t) specialization are to handle unsigned |
376 | // comparison errors while still being thorough with the comparison. |
377 | |
378 | TF_DEFINE_CHECK_OP_IMPL(Check_EQ, ==) |
379 | // Compilation error with CHECK_EQ(NULL, x)? |
380 | // Use CHECK(x == NULL) instead. |
381 | |
382 | inline string* Check_EQImpl(int v1, size_t v2, const char* exprtext) { |
383 | if (TF_PREDICT_FALSE(v1 < 0)) |
384 | ::tsl::internal::MakeCheckOpString(v1, v2, exprtext); |
385 | |
386 | return Check_EQImpl(size_t(v1), v2, exprtext); |
387 | } |
388 | |
389 | inline string* Check_EQImpl(size_t v1, int v2, const char* exprtext) { |
390 | return Check_EQImpl(v2, v1, exprtext); |
391 | } |
392 | |
393 | TF_DEFINE_CHECK_OP_IMPL(Check_NE, !=) |
394 | |
395 | inline string* Check_NEImpl(int v1, size_t v2, const char* exprtext) { |
396 | if (v1 < 0) return NULL; |
397 | |
398 | return Check_NEImpl(size_t(v1), v2, exprtext); |
399 | } |
400 | |
401 | inline string* Check_NEImpl(size_t v1, int v2, const char* exprtext) { |
402 | return Check_NEImpl(v2, v1, exprtext); |
403 | } |
404 | |
405 | TF_DEFINE_CHECK_OP_IMPL(Check_LE, <=) |
406 | |
407 | inline string* Check_LEImpl(int v1, size_t v2, const char* exprtext) { |
408 | if (v1 <= 0) return NULL; |
409 | |
410 | return Check_LEImpl(size_t(v1), v2, exprtext); |
411 | } |
412 | |
413 | inline string* Check_LEImpl(size_t v1, int v2, const char* exprtext) { |
414 | if (TF_PREDICT_FALSE(v2 < 0)) |
415 | return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext); |
416 | return Check_LEImpl(v1, size_t(v2), exprtext); |
417 | } |
418 | |
419 | TF_DEFINE_CHECK_OP_IMPL(Check_LT, <) |
420 | |
421 | inline string* Check_LTImpl(int v1, size_t v2, const char* exprtext) { |
422 | if (v1 < 0) return NULL; |
423 | |
424 | return Check_LTImpl(size_t(v1), v2, exprtext); |
425 | } |
426 | |
427 | inline string* Check_LTImpl(size_t v1, int v2, const char* exprtext) { |
428 | if (v2 < 0) return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext); |
429 | return Check_LTImpl(v1, size_t(v2), exprtext); |
430 | } |
431 | |
432 | // Implement GE,GT in terms of LE,LT |
433 | template <typename T1, typename T2> |
434 | inline string* Check_GEImpl(const T1& v1, const T2& v2, const char* exprtext) { |
435 | return Check_LEImpl(v2, v1, exprtext); |
436 | } |
437 | |
438 | template <typename T1, typename T2> |
439 | inline string* Check_GTImpl(const T1& v1, const T2& v2, const char* exprtext) { |
440 | return Check_LTImpl(v2, v1, exprtext); |
441 | } |
442 | |
443 | #undef TF_DEFINE_CHECK_OP_IMPL |
444 | |
445 | // In optimized mode, use CheckOpString to hint to compiler that |
446 | // the while condition is unlikely. |
447 | #define CHECK_OP_LOG(name, op, val1, val2) \ |
448 | while (::tsl::internal::CheckOpString _result{::tsl::internal::name##Impl( \ |
449 | ::tsl::internal::GetReferenceableValue(val1), \ |
450 | ::tsl::internal::GetReferenceableValue(val2), #val1 " " #op " " #val2)}) \ |
451 | ::tsl::internal::LogMessageFatal(__FILE__, __LINE__) << *(_result.str_) |
452 | |
453 | #define CHECK_OP(name, op, val1, val2) CHECK_OP_LOG(name, op, val1, val2) |
454 | |
455 | // CHECK_EQ/NE/... |
456 | #define CHECK_EQ(val1, val2) CHECK_OP(Check_EQ, ==, val1, val2) |
457 | #define CHECK_NE(val1, val2) CHECK_OP(Check_NE, !=, val1, val2) |
458 | #define CHECK_LE(val1, val2) CHECK_OP(Check_LE, <=, val1, val2) |
459 | #define CHECK_LT(val1, val2) CHECK_OP(Check_LT, <, val1, val2) |
460 | #define CHECK_GE(val1, val2) CHECK_OP(Check_GE, >=, val1, val2) |
461 | #define CHECK_GT(val1, val2) CHECK_OP(Check_GT, >, val1, val2) |
462 | #define CHECK_NOTNULL(val) \ |
463 | ::tsl::internal::CheckNotNull(__FILE__, __LINE__, \ |
464 | "'" #val "' Must be non NULL", (val)) |
465 | |
466 | #ifndef NDEBUG |
467 | // DCHECK_EQ/NE/... |
468 | #define DCHECK(condition) CHECK(condition) |
469 | #define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) |
470 | #define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) |
471 | #define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) |
472 | #define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) |
473 | #define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) |
474 | #define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) |
475 | |
476 | #else |
477 | |
478 | #define DCHECK(condition) \ |
479 | while (false && (condition)) LOG(FATAL) |
480 | |
481 | // NDEBUG is defined, so DCHECK_EQ(x, y) and so on do nothing. |
482 | // However, we still want the compiler to parse x and y, because |
483 | // we don't want to lose potentially useful errors and warnings. |
484 | // _DCHECK_NOP is a helper, and should not be used outside of this file. |
485 | #define _TF_DCHECK_NOP(x, y) \ |
486 | while (false && ((void)(x), (void)(y), 0)) LOG(FATAL) |
487 | |
488 | #define DCHECK_EQ(x, y) _TF_DCHECK_NOP(x, y) |
489 | #define DCHECK_NE(x, y) _TF_DCHECK_NOP(x, y) |
490 | #define DCHECK_LE(x, y) _TF_DCHECK_NOP(x, y) |
491 | #define DCHECK_LT(x, y) _TF_DCHECK_NOP(x, y) |
492 | #define DCHECK_GE(x, y) _TF_DCHECK_NOP(x, y) |
493 | #define DCHECK_GT(x, y) _TF_DCHECK_NOP(x, y) |
494 | |
495 | #endif |
496 | |
497 | // These are for when you don't want a CHECK failure to print a verbose |
498 | // stack trace. The implementation of CHECK* in this file already doesn't. |
499 | #define QCHECK(condition) CHECK(condition) |
500 | #define QCHECK_EQ(x, y) CHECK_EQ(x, y) |
501 | #define QCHECK_NE(x, y) CHECK_NE(x, y) |
502 | #define QCHECK_LE(x, y) CHECK_LE(x, y) |
503 | #define QCHECK_LT(x, y) CHECK_LT(x, y) |
504 | #define QCHECK_GE(x, y) CHECK_GE(x, y) |
505 | #define QCHECK_GT(x, y) CHECK_GT(x, y) |
506 | |
507 | template <typename T> |
508 | T&& CheckNotNull(const char* file, int line, const char* exprtext, T&& t) { |
509 | if (t == nullptr) { |
510 | LogMessageFatal(file, line) << string(exprtext); |
511 | } |
512 | return std::forward<T>(t); |
513 | } |
514 | |
515 | int64_t MinLogLevelFromEnv(); |
516 | |
517 | int64_t MaxVLogLevelFromEnv(); |
518 | |
519 | } // namespace internal |
520 | |
521 | // LogSink support adapted from //base/logging.h |
522 | // |
523 | // `LogSink` is an interface which can be extended to intercept and process |
524 | // all log messages. LogSink implementations must be thread-safe. A single |
525 | // instance will be called from whichever thread is performing a logging |
526 | // operation. |
527 | class TFLogEntry { |
528 | static absl::LogSeverity AsAbslLogSeverity(int severity) { |
529 | return static_cast<absl::LogSeverity>(severity); |
530 | } |
531 | |
532 | public: |
533 | explicit TFLogEntry(int severity, absl::string_view message) |
534 | : severity_(AsAbslLogSeverity(severity)), message_(message) {} |
535 | |
536 | explicit TFLogEntry(int severity, absl::string_view fname, int line, |
537 | absl::string_view message) |
538 | : severity_(AsAbslLogSeverity(severity)), |
539 | fname_(fname), |
540 | line_(line), |
541 | message_(message) {} |
542 | |
543 | absl::LogSeverity log_severity() const { return severity_; } |
544 | std::string FName() const { return fname_; } |
545 | int Line() const { return line_; } |
546 | std::string ToString() const { return message_; } |
547 | absl::string_view text_message() const { return message_; } |
548 | |
549 | private: |
550 | const absl::LogSeverity severity_; |
551 | const std::string fname_; |
552 | int line_ = -1; |
553 | const std::string message_; |
554 | }; |
555 | |
556 | class TFLogSink { |
557 | public: |
558 | virtual ~TFLogSink() = default; |
559 | |
560 | // `Send` is called synchronously during the log statement. The logging |
561 | // module guarantees not to call `Send` concurrently on the same log sink. |
562 | // Implementations should be careful not to call`LOG` or `CHECK` or take |
563 | // any locks that might be held by the `LOG` caller, to avoid deadlock. |
564 | // |
565 | // `e` is guaranteed to remain valid until the subsequent call to |
566 | // `WaitTillSent` completes, so implementations may store a pointer to or |
567 | // copy of `e` (e.g. in a thread local variable) for use in `WaitTillSent`. |
568 | virtual void Send(const TFLogEntry& entry) = 0; |
569 | |
570 | // `WaitTillSent` blocks the calling thread (the thread that generated a log |
571 | // message) until the sink has finished processing the log message. |
572 | // `WaitTillSent` is called once per log message, following the call to |
573 | // `Send`. This may be useful when log messages are buffered or processed |
574 | // asynchronously by an expensive log sink. |
575 | // The default implementation returns immediately. Like `Send`, |
576 | // implementations should be careful not to call `LOG` or `CHECK or take any |
577 | // locks that might be held by the `LOG` caller, to avoid deadlock. |
578 | virtual void WaitTillSent() {} |
579 | }; |
580 | |
581 | // This is the default log sink. This log sink is used if there are no other |
582 | // log sinks registered. To disable the default log sink, set the |
583 | // "no_default_logger" Bazel config setting to true or define a |
584 | // NO_DEFAULT_LOGGER preprocessor symbol. This log sink will always log to |
585 | // stderr. |
586 | class TFDefaultLogSink : public TFLogSink { |
587 | public: |
588 | void Send(const TFLogEntry& entry) override; |
589 | }; |
590 | |
591 | // Add or remove a `LogSink` as a consumer of logging data. Thread-safe. |
592 | void TFAddLogSink(TFLogSink* sink); |
593 | void TFRemoveLogSink(TFLogSink* sink); |
594 | |
595 | // Get all the log sinks. Thread-safe. |
596 | std::vector<TFLogSink*> TFGetLogSinks(); |
597 | |
598 | // Change verbose level of pre-defined files if envorionment |
599 | // variable `env_var` is defined. This is currently a no op. |
600 | void UpdateLogVerbosityIfDefined(const char* env_var); |
601 | |
602 | } // namespace tsl |
603 | |
604 | #endif // TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_ |
605 | |