Support ANDROID_LOG_TAGS.

This is the dalvik-compatible solution to the "I don't want non-FATAL logging
in my test output" problem.

Change-Id: I51b7b883ce89604af4661696e7c7b041a0ef8211
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 6017ddb..47193e5 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -422,7 +422,7 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
-bool ParseInt(const char* in, int* out) {
+static bool ParseInt(const char* in, int* out) {
   char* end;
   int result = strtol(in, &end, 10);
   if (in == end || *end != '\0') {
@@ -432,9 +432,9 @@
   return true;
 }
 
-void OpenDexFiles(const std::vector<const char*>& dex_filenames,
-                  const std::vector<const char*>& dex_locations,
-                  std::vector<const DexFile*>& dex_files) {
+static void OpenDexFiles(const std::vector<const char*>& dex_filenames,
+                         const std::vector<const char*>& dex_locations,
+                         std::vector<const DexFile*>& dex_files) {
   for (size_t i = 0; i < dex_filenames.size(); i++) {
     const char* dex_filename = dex_filenames[i];
     const char* dex_location = dex_locations[i];
@@ -447,7 +447,9 @@
   }
 }
 
-int dex2oat(int argc, char** argv) {
+static int dex2oat(int argc, char** argv) {
+  InitLogging();
+
   // Skip over argv[0].
   argv++;
   argc--;
diff --git a/src/logging.cc b/src/logging.cc
index b0f3055..7d176a2 100644
--- a/src/logging.cc
+++ b/src/logging.cc
@@ -24,12 +24,55 @@
 
 LogVerbosity gLogVerbosity;
 
+static bool gInitLoggingCalled = false;
+static LogSeverity gMinimumLogSeverity = INFO;
+
 static Mutex& GetLoggingLock() {
   static Mutex logging_lock("LogMessage lock");
   return logging_lock;
 }
 
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+//      *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon
+// and a letter indicating the minimum priority level we're expected to log.
+// This can be used to reveal or conceal logs with specific tags.
+void InitLogging() {
+  gInitLoggingCalled = true;
+  const char* tags = getenv("ANDROID_LOG_TAGS");
+  if (tags == NULL) {
+    return;
+  }
+
+  std::vector<std::string> specs;
+  Split(tags, ' ', specs);
+  for (size_t i = 0; i < specs.size(); ++i) {
+    // "tag-pattern:[vdiwefs]"
+    std::string spec(specs[i]);
+    if (spec.size() == 3 && StartsWith(spec, "*:")) {
+      switch (spec[2]) {
+        case 'v': gMinimumLogSeverity = VERBOSE; continue;
+        case 'd': gMinimumLogSeverity = DEBUG; continue;
+        case 'i': gMinimumLogSeverity = INFO; continue;
+        case 'w': gMinimumLogSeverity = WARNING; continue;
+        case 'e': gMinimumLogSeverity = ERROR; continue;
+        case 'f': gMinimumLogSeverity = FATAL; continue;
+        // liblog will even suppress FATAL if you say 's' for silent, but that's crazy!
+        case 's': gMinimumLogSeverity = FATAL; continue;
+      }
+    }
+    LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags << ")";
+  }
+}
+
 LogMessage::~LogMessage() {
+  if (data_->severity < gMinimumLogSeverity) {
+    return; // No need to format something we're not going to output.
+  }
+
   // Finish constructing the message.
   if (data_->error != -1) {
     data_->buffer << ": " << strerror(data_->error);
diff --git a/src/logging.h b/src/logging.h
index c1ea3ab..337f12e 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -262,18 +262,10 @@
   bool startup;
   bool third_party_jni; // Enabled with "-verbose:third-party-jni".
   bool threads;
-  std::ostream* logging_stream;
-
-  void SetLoggingStream(std::ostream* new_logging_stream) {
-    DCHECK(new_logging_stream->good());
-    if (logging_stream != NULL) {
-      delete logging_stream;
-    }
-    logging_stream = new_logging_stream;
-  }
 };
 
 extern LogVerbosity gLogVerbosity;
+extern void InitLogging();
 
 }  // namespace art
 
diff --git a/src/logging_linux.cc b/src/logging_linux.cc
index 4e7c796..84a6843 100644
--- a/src/logging_linux.cc
+++ b/src/logging_linux.cc
@@ -34,11 +34,10 @@
 }
 
 void LogMessage::LogLine(const char* message) {
-  std::ostream &out =
-      (gLogVerbosity.logging_stream == NULL ? std::cerr : *gLogVerbosity.logging_stream);
-  out << "VDIWEFF"[data_->severity] << ' '
-      << StringPrintf("%5d %5d", getpid(), ::art::GetTid()) << ' '
-      << data_->file << ':' << data_->line_number << "] " << message << std::endl;
+  std::ostream& os(std::cerr);
+  os << "VDIWEFF"[data_->severity] << ' '
+     << StringPrintf("%5d %5d", getpid(), ::art::GetTid()) << ' '
+     << data_->file << ':' << data_->line_number << "] " << message << std::endl;
 }
 
 }  // namespace art
diff --git a/src/oatdump.cc b/src/oatdump.cc
index ea2e711..28126ae 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -1234,7 +1234,9 @@
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
 
-int oatdump(int argc, char** argv) {
+static int oatdump(int argc, char** argv) {
+  InitLogging();
+
   // Skip over argv[0].
   argv++;
   argc--;
diff --git a/src/oatexec.cc b/src/oatexec.cc
index 4f068db..d080560 100644
--- a/src/oatexec.cc
+++ b/src/oatexec.cc
@@ -105,7 +105,8 @@
 
 // Parse arguments.  Most of it just gets passed through to the runtime.
 // The JNI spec defines a handful of standard arguments.
-int oatexec(int argc, char** argv) {
+static int oatexec(int argc, char** argv) {
+  InitLogging();
   setvbuf(stdout, NULL, _IONBF, 0);
 
   // Skip over argv[0].
diff --git a/src/runtime.cc b/src/runtime.cc
index 18d40f5..a94a93a 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -22,7 +22,6 @@
 #include <cstdlib>
 #include <limits>
 #include <vector>
-#include <fstream>
 
 #include "class_linker.h"
 #include "class_loader.h"
@@ -432,15 +431,6 @@
           gLogVerbosity.third_party_jni = true;
         } else if (verbose_options[i] == "threads") {
           gLogVerbosity.threads = true;
-        } else if (StartsWith(verbose_options[i], "log-to=")) {
-          std::string log_file_name(verbose_options[i].substr(strlen("log-to=")));
-          std::ofstream* log_file = new std::ofstream(log_file_name.c_str());
-          if (log_file->is_open() && log_file->good()) {
-            gLogVerbosity.SetLoggingStream(log_file);
-          } else {
-            LOG(ERROR) << "Fail to open log file: \"" << log_file_name << "\","
-                       << " use default logging stream.";
-          }
         } else {
           LOG(WARNING) << "Ignoring unknown -verbose option: " << verbose_options[i];
         }