• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "tensorflow/core/platform/default/logging.h"
17 
18 // TODO(b/142492876): Avoid depending on absl internal.
19 #ifdef TF_ANDROID_ENABLE_LOG_EVERY_N_SECONDS
20 #include "absl/base/internal/cycleclock.h"
21 #endif
22 #if !defined(PLATFORM_POSIX_ANDROID)
23 #include "absl/base/internal/sysinfo.h"
24 #endif
25 #include "tensorflow/core/platform/env_time.h"
26 #include "tensorflow/core/platform/macros.h"
27 #include "tensorflow/core/platform/mutex.h"
28 
29 #if defined(PLATFORM_POSIX_ANDROID)
30 #include <android/log.h>
31 
32 #include <iostream>
33 #include <sstream>
34 #endif
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 
40 #include <algorithm>
41 #include <queue>
42 #include <unordered_map>
43 
44 #ifdef __ANDROID__
45 #include <unistd.h>
46 #endif
47 
48 namespace tensorflow {
49 
50 namespace internal {
51 namespace {
52 
53 #ifdef TF_ANDROID_ENABLE_LOGSINK
54 // This is an internal singleton class that manages the log sinks. It allows
55 // adding and removing the log sinks, as well as handling sending log messages
56 // to all the registered log sinks.
57 class TFLogSinks {
58  public:
59   // Gets the TFLogSinks instance. This is the entry point for using this class.
60   static TFLogSinks& Instance();
61 
62   // Adds a log sink. The sink argument must not be a nullptr. TFLogSinks
63   // takes ownership of the pointer, the user must not free the pointer.
64   // The pointer will remain valid until the application terminates or
65   // until TFLogSinks::Remove is called for the same pointer value.
66   void Add(TFLogSink* sink);
67 
68   // Removes a log sink. This will also erase the sink object. The pointer
69   // to the sink becomes invalid after this call.
70   void Remove(TFLogSink* sink);
71 
72   // Gets the currently registered log sinks.
73   std::vector<TFLogSink*> GetSinks() const;
74 
75   // Sends a log message to all registered log sinks.
76   //
77   // If there are no log sinks are registered:
78   //
79   // NO_DEFAULT_LOGGER is defined:
80   // Up to 128 messages will be queued until a log sink is added.
81   // The queue will then be logged to the first added log sink.
82   //
83   // NO_DEFAULT_LOGGER is not defined:
84   // The messages will be logged using the default logger. The default logger
85   // will log to stdout on all platforms except for Android. On Androit the
86   // default Android logger will be used.
87   void Send(const TFLogEntry& entry);
88 
89  private:
90   TFLogSinks();
91   void SendToSink(TFLogSink& sink, const TFLogEntry& entry);
92 
93   std::queue<TFLogEntry> log_entry_queue_;
94   static const size_t kMaxLogEntryQueueSize = 128;
95 
96   mutable tensorflow::mutex mutex_;
97   std::vector<TFLogSink*> sinks_;
98 };
99 
TFLogSinks()100 TFLogSinks::TFLogSinks() {
101 #ifndef NO_DEFAULT_LOGGER
102   static TFDefaultLogSink* default_sink = new TFDefaultLogSink();
103   sinks_.emplace_back(default_sink);
104 #endif
105 }
106 
Instance()107 TFLogSinks& TFLogSinks::Instance() {
108   static TFLogSinks* instance = new TFLogSinks();
109   return *instance;
110 }
111 
Add(TFLogSink * sink)112 void TFLogSinks::Add(TFLogSink* sink) {
113   assert(sink != nullptr && "The sink must not be a nullptr");
114 
115   tensorflow::mutex_lock lock(mutex_);
116   sinks_.emplace_back(sink);
117 
118   // If this is the only sink log all the queued up messages to this sink
119   if (sinks_.size() == 1) {
120     while (!log_entry_queue_.empty()) {
121       for (const auto& sink : sinks_) {
122         SendToSink(*sink, log_entry_queue_.front());
123       }
124       log_entry_queue_.pop();
125     }
126   }
127 }
128 
Remove(TFLogSink * sink)129 void TFLogSinks::Remove(TFLogSink* sink) {
130   assert(sink != nullptr && "The sink must not be a nullptr");
131 
132   tensorflow::mutex_lock lock(mutex_);
133   auto it = std::find(sinks_.begin(), sinks_.end(), sink);
134   if (it != sinks_.end()) sinks_.erase(it);
135 }
136 
GetSinks() const137 std::vector<TFLogSink*> TFLogSinks::GetSinks() const {
138   tensorflow::mutex_lock lock(mutex_);
139   return sinks_;
140 }
141 
Send(const TFLogEntry & entry)142 void TFLogSinks::Send(const TFLogEntry& entry) {
143   tensorflow::mutex_lock lock(mutex_);
144 
145   // If we don't have any sinks registered, queue them up
146   if (sinks_.empty()) {
147     // If we've exceeded the maximum queue size, drop the oldest entries
148     while (log_entry_queue_.size() >= kMaxLogEntryQueueSize) {
149       log_entry_queue_.pop();
150     }
151     log_entry_queue_.push(entry);
152     return;
153   }
154 
155   // If we have items in the queue, push them out first
156   while (!log_entry_queue_.empty()) {
157     for (const auto& sink : sinks_) {
158       SendToSink(*sink, log_entry_queue_.front());
159     }
160     log_entry_queue_.pop();
161   }
162 
163   // ... and now we can log the current log entry
164   for (const auto& sink : sinks_) {
165     SendToSink(*sink, entry);
166   }
167 }
168 
SendToSink(TFLogSink & sink,const TFLogEntry & entry)169 void TFLogSinks::SendToSink(TFLogSink& sink, const TFLogEntry& entry) {
170   sink.Send(entry);
171   sink.WaitTillSent();
172 }
173 #endif  // TF_ANDROID_ENABLE_LOGSINK
174 
ParseInteger(const char * str,size_t size)175 int ParseInteger(const char* str, size_t size) {
176   // Ideally we would use env_var / safe_strto64, but it is
177   // hard to use here without pulling in a lot of dependencies,
178   // so we use std:istringstream instead
179   string integer_str(str, size);
180   std::istringstream ss(integer_str);
181   int level = 0;
182   ss >> level;
183   return level;
184 }
185 
186 // Parse log level (int64) from environment variable (char*)
LogLevelStrToInt(const char * tf_env_var_val)187 int64 LogLevelStrToInt(const char* tf_env_var_val) {
188   if (tf_env_var_val == nullptr) {
189     return 0;
190   }
191   return ParseInteger(tf_env_var_val, strlen(tf_env_var_val));
192 }
193 
194 // Using StringPiece breaks Windows build.
195 struct StringData {
196   struct Hasher {
operator ()tensorflow::internal::__anon2b7d55170111::StringData::Hasher197     size_t operator()(const StringData& sdata) const {
198       // For dependency reasons, we cannot use hash.h here. Use DBJHash instead.
199       size_t hash = 5381;
200       const char* data = sdata.data;
201       for (const char* top = data + sdata.size; data < top; ++data) {
202         hash = ((hash << 5) + hash) + (*data);
203       }
204       return hash;
205     }
206   };
207 
208   StringData() = default;
StringDatatensorflow::internal::__anon2b7d55170111::StringData209   StringData(const char* data, size_t size) : data(data), size(size) {}
210 
operator ==tensorflow::internal::__anon2b7d55170111::StringData211   bool operator==(const StringData& rhs) const {
212     return size == rhs.size && memcmp(data, rhs.data, size) == 0;
213   }
214 
215   const char* data = nullptr;
216   size_t size = 0;
217 };
218 
219 using VmoduleMap = std::unordered_map<StringData, int, StringData::Hasher>;
220 
221 // Returns a mapping from module name to VLOG level, derived from the
222 // TF_CPP_VMODULE environment variable; ownership is transferred to the caller.
VmodulesMapFromEnv()223 VmoduleMap* VmodulesMapFromEnv() {
224   // The value of the env var is supposed to be of the form:
225   //    "foo=1,bar=2,baz=3"
226   const char* env = getenv("TF_CPP_VMODULE");
227   if (env == nullptr) {
228     // If there is no TF_CPP_VMODULE configuration (most common case), return
229     // nullptr so that the ShouldVlogModule() API can fast bail out of it.
230     return nullptr;
231   }
232   // The memory returned by getenv() can be invalidated by following getenv() or
233   // setenv() calls. And since we keep references to it in the VmoduleMap in
234   // form of StringData objects, make a copy of it.
235   const char* env_data = strdup(env);
236   VmoduleMap* result = new VmoduleMap();
237   while (true) {
238     const char* eq = strchr(env_data, '=');
239     if (eq == nullptr) {
240       break;
241     }
242     const char* after_eq = eq + 1;
243 
244     // Comma either points at the next comma delimiter, or at a null terminator.
245     // We check that the integer we parse ends at this delimiter.
246     const char* comma = strchr(after_eq, ',');
247     const char* new_env_data;
248     if (comma == nullptr) {
249       comma = strchr(after_eq, '\0');
250       new_env_data = comma;
251     } else {
252       new_env_data = comma + 1;
253     }
254     (*result)[StringData(env_data, eq - env_data)] =
255         ParseInteger(after_eq, comma - after_eq);
256     env_data = new_env_data;
257   }
258   return result;
259 }
260 
EmitThreadIdFromEnv()261 bool EmitThreadIdFromEnv() {
262   const char* tf_env_var_val = getenv("TF_CPP_LOG_THREAD_ID");
263   return tf_env_var_val == nullptr
264              ? false
265              : ParseInteger(tf_env_var_val, strlen(tf_env_var_val)) != 0;
266 }
267 
268 }  // namespace
269 
MinLogLevelFromEnv()270 int64 MinLogLevelFromEnv() {
271   // We don't want to print logs during fuzzing as that would slow fuzzing down
272   // by almost 2x. So, if we are in fuzzing mode (not just running a test), we
273   // return a value so that nothing is actually printed. Since LOG uses >=
274   // (see ~LogMessage in this file) to see if log messages need to be printed,
275   // the value we're interested on to disable printing is the maximum severity.
276   // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
277 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
278   return tensorflow::NUM_SEVERITIES;
279 #else
280   const char* tf_env_var_val = getenv("TF_CPP_MIN_LOG_LEVEL");
281   return LogLevelStrToInt(tf_env_var_val);
282 #endif
283 }
284 
MaxVLogLevelFromEnv()285 int64 MaxVLogLevelFromEnv() {
286   // We don't want to print logs during fuzzing as that would slow fuzzing down
287   // by almost 2x. So, if we are in fuzzing mode (not just running a test), we
288   // return a value so that nothing is actually printed. Since VLOG uses <=
289   // (see VLOG_IS_ON in logging.h) to see if log messages need to be printed,
290   // the value we're interested on to disable printing is 0.
291   // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
292 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
293   return 0;
294 #else
295   const char* tf_env_var_val = getenv("TF_CPP_MAX_VLOG_LEVEL");
296   return LogLevelStrToInt(tf_env_var_val);
297 #endif
298 }
299 
LogMessage(const char * fname,int line,int severity)300 LogMessage::LogMessage(const char* fname, int line, int severity)
301     : fname_(fname), line_(line), severity_(severity) {}
302 
AtLocation(const char * fname,int line)303 LogMessage& LogMessage::AtLocation(const char* fname, int line) {
304   fname_ = fname;
305   line_ = line;
306   return *this;
307 }
308 
~LogMessage()309 LogMessage::~LogMessage() {
310   // Read the min log level once during the first call to logging.
311   static int64 min_log_level = MinLogLevelFromEnv();
312   if (severity_ >= min_log_level) {
313     GenerateLogMessage();
314   }
315 }
316 
GenerateLogMessage()317 void LogMessage::GenerateLogMessage() {
318 #ifdef TF_ANDROID_ENABLE_LOGSINK
319   TFLogSinks::Instance().Send(TFLogEntry(severity_, fname_, line_, str()));
320 #else
321   static bool log_thread_id = EmitThreadIdFromEnv();
322   uint64 now_micros = EnvTime::NowMicros();
323   time_t now_seconds = static_cast<time_t>(now_micros / 1000000);
324   int32 micros_remainder = static_cast<int32>(now_micros % 1000000);
325   const size_t time_buffer_size = 30;
326   char time_buffer[time_buffer_size];
327   strftime(time_buffer, time_buffer_size, "%Y-%m-%d %H:%M:%S",
328            localtime(&now_seconds));
329   const size_t tid_buffer_size = 10;
330   char tid_buffer[tid_buffer_size] = "";
331   if (log_thread_id) {
332     snprintf(tid_buffer, sizeof(tid_buffer), " %7u", gettid());
333   }
334   // TODO(jeff,sanjay): Replace this with something that logs through the env.
335   fprintf(stderr, "%s.%06d: %c%s %s:%d] %s\n", time_buffer, micros_remainder,
336           "IWEF"[severity_], tid_buffer, fname_, line_, str().c_str());
337 #endif  // TF_ANDROID_ENABLE_LOGSINK
338 }
339 
MaxVLogLevel()340 int64 LogMessage::MaxVLogLevel() {
341   static int64 max_vlog_level = MaxVLogLevelFromEnv();
342   return max_vlog_level;
343 }
344 
VmoduleActivated(const char * fname,int level)345 bool LogMessage::VmoduleActivated(const char* fname, int level) {
346   if (level <= MaxVLogLevel()) {
347     return true;
348   }
349   static VmoduleMap* vmodules = VmodulesMapFromEnv();
350   if (TF_PREDICT_TRUE(vmodules == nullptr)) {
351     return false;
352   }
353   const char* last_slash = strrchr(fname, '/');
354   const char* module_start = last_slash == nullptr ? fname : last_slash + 1;
355   const char* dot_after = strchr(module_start, '.');
356   const char* module_limit =
357       dot_after == nullptr ? strchr(fname, '\0') : dot_after;
358   StringData module(module_start, module_limit - module_start);
359   auto it = vmodules->find(module);
360   return it != vmodules->end() && it->second >= level;
361 }
362 
LogMessageFatal(const char * file,int line)363 LogMessageFatal::LogMessageFatal(const char* file, int line)
364     : LogMessage(file, line, FATAL) {}
~LogMessageFatal()365 LogMessageFatal::~LogMessageFatal() {
366   // abort() ensures we don't return (we promised we would not via
367   // ATTRIBUTE_NORETURN).
368   GenerateLogMessage();
369   abort();
370 }
371 
LogString(const char * fname,int line,int severity,const string & message)372 void LogString(const char* fname, int line, int severity,
373                const string& message) {
374   LogMessage(fname, line, severity) << message;
375 }
376 
377 template <>
MakeCheckOpValueString(std::ostream * os,const char & v)378 void MakeCheckOpValueString(std::ostream* os, const char& v) {
379   if (v >= 32 && v <= 126) {
380     (*os) << "'" << v << "'";
381   } else {
382     (*os) << "char value " << static_cast<int16>(v);
383   }
384 }
385 
386 template <>
MakeCheckOpValueString(std::ostream * os,const signed char & v)387 void MakeCheckOpValueString(std::ostream* os, const signed char& v) {
388   if (v >= 32 && v <= 126) {
389     (*os) << "'" << v << "'";
390   } else {
391     (*os) << "signed char value " << static_cast<int16>(v);
392   }
393 }
394 
395 template <>
MakeCheckOpValueString(std::ostream * os,const unsigned char & v)396 void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) {
397   if (v >= 32 && v <= 126) {
398     (*os) << "'" << v << "'";
399   } else {
400     (*os) << "unsigned char value " << static_cast<uint16>(v);
401   }
402 }
403 
404 #if LANG_CXX11
405 template <>
MakeCheckOpValueString(std::ostream * os,const std::nullptr_t & v)406 void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& v) {
407   (*os) << "nullptr";
408 }
409 #endif
410 
CheckOpMessageBuilder(const char * exprtext)411 CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext)
412     : stream_(new std::ostringstream) {
413   *stream_ << "Check failed: " << exprtext << " (";
414 }
415 
~CheckOpMessageBuilder()416 CheckOpMessageBuilder::~CheckOpMessageBuilder() { delete stream_; }
417 
ForVar2()418 std::ostream* CheckOpMessageBuilder::ForVar2() {
419   *stream_ << " vs. ";
420   return stream_;
421 }
422 
NewString()423 string* CheckOpMessageBuilder::NewString() {
424   *stream_ << ")";
425   return new string(stream_->str());
426 }
427 
428 namespace {
429 // The following code behaves like AtomicStatsCounter::LossyAdd() for
430 // speed since it is fine to lose occasional updates.
431 // Returns old value of *counter.
LossyIncrement(std::atomic<uint32> * counter)432 uint32 LossyIncrement(std::atomic<uint32>* counter) {
433   const uint32 value = counter->load(std::memory_order_relaxed);
434   counter->store(value + 1, std::memory_order_relaxed);
435   return value;
436 }
437 
438 }  // namespace
439 
ShouldLog(int n)440 bool LogEveryNState::ShouldLog(int n) {
441   return n != 0 && (LossyIncrement(&counter_) % n) == 0;
442 }
443 
ShouldLog(int n)444 bool LogFirstNState::ShouldLog(int n) {
445   const int counter_value =
446       static_cast<int>(counter_.load(std::memory_order_relaxed));
447   if (counter_value < n) {
448     counter_.store(counter_value + 1, std::memory_order_relaxed);
449     return true;
450   }
451   return false;
452 }
453 
ShouldLog(int ignored)454 bool LogEveryPow2State::ShouldLog(int ignored) {
455   const uint32 new_value = LossyIncrement(&counter_) + 1;
456   return (new_value & (new_value - 1)) == 0;
457 }
458 
459 #ifdef TF_ANDROID_ENABLE_LOG_EVERY_N_SECONDS
ShouldLog(double seconds)460 bool LogEveryNSecState::ShouldLog(double seconds) {
461   LossyIncrement(&counter_);
462   const int64 now_cycles = absl::base_internal::CycleClock::Now();
463   int64 next_cycles = next_log_time_cycles_.load(std::memory_order_relaxed);
464   do {
465     if (now_cycles <= next_cycles) return false;
466   } while (!next_log_time_cycles_.compare_exchange_weak(
467       next_cycles,
468       now_cycles + seconds * absl::base_internal::CycleClock::Frequency(),
469       std::memory_order_relaxed, std::memory_order_relaxed));
470   return true;
471 }
472 #endif
473 
474 }  // namespace internal
475 
476 #ifdef TF_ANDROID_ENABLE_LOGSINK
TFAddLogSink(TFLogSink * sink)477 void TFAddLogSink(TFLogSink* sink) {
478   internal::TFLogSinks::Instance().Add(sink);
479 }
480 
TFRemoveLogSink(TFLogSink * sink)481 void TFRemoveLogSink(TFLogSink* sink) {
482   internal::TFLogSinks::Instance().Remove(sink);
483 }
484 
TFGetLogSinks()485 std::vector<TFLogSink*> TFGetLogSinks() {
486   return internal::TFLogSinks::Instance().GetSinks();
487 }
488 
Send(const TFLogEntry & entry)489 void TFDefaultLogSink::Send(const TFLogEntry& entry) {
490 #ifdef PLATFORM_POSIX_ANDROID
491   int android_log_level;
492   switch (entry.log_severity()) {
493     case absl::LogSeverity::kInfo:
494       android_log_level = ANDROID_LOG_INFO;
495       break;
496     case absl::LogSeverity::kWarning:
497       android_log_level = ANDROID_LOG_WARN;
498       break;
499     case absl::LogSeverity::kError:
500       android_log_level = ANDROID_LOG_ERROR;
501       break;
502     case absl::LogSeverity::kFatal:
503       android_log_level = ANDROID_LOG_FATAL;
504       break;
505     default:
506       if (entry.log_severity() < absl::LogSeverity::kInfo) {
507         android_log_level = ANDROID_LOG_VERBOSE;
508       } else {
509         android_log_level = ANDROID_LOG_ERROR;
510       }
511       break;
512   }
513 
514   std::stringstream ss;
515   const auto& fname = entry.FName();
516   auto pos = fname.find("/");
517   ss << (pos != std::string::npos ? fname.substr(pos + 1) : fname) << ":"
518      << entry.Line() << " " << entry.ToString();
519   __android_log_write(android_log_level, "native", ss.str().c_str());
520 
521   // Also log to stderr (for standalone Android apps).
522   // Don't use 'std::cerr' since it crashes on Android.
523   fprintf(stderr, "native : %s\n", ss.str().c_str());
524 
525   // Android logging at level FATAL does not terminate execution, so abort()
526   // is still required to stop the program.
527   if (entry.log_severity() == absl::LogSeverity::kFatal) {
528     abort();
529   }
530 #else   // PLATFORM_POSIX_ANDROID
531   static bool log_thread_id = internal::EmitThreadIdFromEnv();
532   uint64 now_micros = EnvTime::NowMicros();
533   time_t now_seconds = static_cast<time_t>(now_micros / 1000000);
534   int32 micros_remainder = static_cast<int32>(now_micros % 1000000);
535   const size_t time_buffer_size = 30;
536   char time_buffer[time_buffer_size];
537   strftime(time_buffer, time_buffer_size, "%Y-%m-%d %H:%M:%S",
538            localtime(&now_seconds));
539   const size_t tid_buffer_size = 10;
540   char tid_buffer[tid_buffer_size] = "";
541   if (log_thread_id) {
542     snprintf(tid_buffer, sizeof(tid_buffer), " %7u",
543              absl::base_internal::GetTID());
544   }
545 
546   char sev;
547   switch (entry.log_severity()) {
548     case absl::LogSeverity::kInfo:
549       sev = 'I';
550       break;
551 
552     case absl::LogSeverity::kWarning:
553       sev = 'W';
554       break;
555 
556     case absl::LogSeverity::kError:
557       sev = 'E';
558       break;
559 
560     case absl::LogSeverity::kFatal:
561       sev = 'F';
562       break;
563 
564     default:
565       assert(false && "Unknown logging severity");
566       sev = '?';
567       break;
568   }
569 
570   // TODO(jeff,sanjay): Replace this with something that logs through the env.
571   fprintf(stderr, "%s.%06d: %c%s %s:%d] %s\n", time_buffer, micros_remainder,
572           sev, tid_buffer, entry.FName().c_str(), entry.Line(),
573           entry.ToString().c_str());
574 #endif  // PLATFORM_POSIX_ANDROID
575 }
576 #endif  // TF_ANDROID_ENABLE_LOGSINK
577 
578 }  // namespace tensorflow
579