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];
         }
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index b892ce9..30a6721 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -60,8 +60,6 @@
 msg "------------------------------"
 
 DATA_DIR=/tmp
-DEBUG_OPTS="-Xcheck:jni"
-
 if [ ! -d $DATA_DIR/art-cache ]; then
     mkdir -p $DATA_DIR/art-cache
     [[ $? -ne 0 ]] && exit
@@ -82,11 +80,9 @@
 
 if [ "$DEBUG" = "y" ]; then
     PORT=8000
-    msg "Waiting for debugger to connect on localhost:$PORT"
-	# This is for jdb:
-    DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
-	# Connect thus:
-	#   jdb -attach localhost:12345
+    msg "Waiting for jdb to connect:"
+    msg "    jdb -attach localhost:$PORT"
+    DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
 fi
 
 if [ "$GDB" = "y" ]; then
@@ -94,7 +90,9 @@
     gdbargs="--args $exe"
 fi
 
+JNI_OPTS="-Xjnigreflimit:256 -Xcheck:jni"
+
 cd $ANDROID_BUILD_TOP
 $INVOKE_WITH $gdb $exe $gdbargs -Ximage:$ANDROID_ROOT/framework/core.art \
-    $DEBUG_OPTS $DEX_DEBUG -verbose:log-to=$DEX_LOCATION/log.txt\
+    $JNI_OPTS $DEBUG_OPTS \
     -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index c892309..31c368d 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -105,18 +105,21 @@
 fi
 
 if [ "$DEBUG" = "y" ]; then
-  # This is for ddms:
-  #DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
-  # Connect by running 'ddms'.
+  # Use this instead for ddms and connect by running 'ddms':
+  # DEBUG_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+  # TODO: add a separate --ddms option?
 
-  # This is for jdb:
-  DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,address=12345,server=y,suspend=y"
-  # Connect thus:
-  #   adb forward tcp:12345 tcp:12345
-  #   jdb -attach localhost:12345
+  PORT=12345
+  msg "Waiting for jdb to connect:"
+  msg "    adb forward tcp:$PORT tcp:$PORT"
+  msg "    jdb -attach localhost:$PORT"
+  DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
 fi
 
-cmdline="cd /data; export DEX_LOCATION=$DEX_LOCATION; $INVOKE_WITH $OATEXEC $DEX_DEBUG $ZYGOTE -Xjnigreflimit:256 \
+JNI_OPTS="-Xjnigreflimit:256 -Xcheck:jni"
+
+cmdline="cd /data; export DEX_LOCATION=$DEX_LOCATION; $INVOKE_WITH $OATEXEC $ZYGOTE \
+    $JNI_OPTS $DEBUG_OPTS \
     -Ximage:/data/art-test/core.art \
     -cp /data/run-test/$TEST_NAME.jar \
     Main"
diff --git a/test/etc/reference-run-test-classes b/test/etc/reference-run-test-classes
index b16d9ff..0294cc2 100755
--- a/test/etc/reference-run-test-classes
+++ b/test/etc/reference-run-test-classes
@@ -53,7 +53,8 @@
 
 if [ "$DEBUG" = "y" ]; then
     PORT=8000
-    msg "Waiting for debugger to connect on localhost:$PORT"
+    msg "Waiting for jdb to connect:"
+    msg "    jdb -attach localhost:$PORT"
     DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
 fi