Merge "Fix broken handling of instrumentation lists."
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 2f43f5f..420db43 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -40,6 +40,9 @@
 # Do you want all tests, even those that are time consuming?
 ART_TEST_FULL ?= false
 
+# Do you want run-test to be quieter? run-tests will only show output if they fail.
+ART_TEST_QUIET ?= true
+
 # Do you want default compiler tests run?
 ART_TEST_DEFAULT_COMPILER ?= true
 
@@ -116,12 +119,25 @@
         || (echo -e "$(1) \e[91mFAILED\e[0m" >&2 )))
 endef
 
+ifeq ($(ART_TEST_QUIET),true)
+  ART_TEST_ANNOUNCE_PASS := ( true )
+  ART_TEST_ANNOUNCE_RUN := ( true )
+  ART_TEST_ANNOUNCE_SKIP_FAILURE := ( true )
+  ART_TEST_ANNOUNCE_SKIP_BROKEN := ( true )
+else
+  # Note the use of '=' and not ':=' is intentional since these are actually functions.
+  ART_TEST_ANNOUNCE_PASS = ( echo -e "$(1) \e[92mPASSED\e[0m" )
+  ART_TEST_ANNOUNCE_RUN = ( echo -e "$(1) \e[95mRUNNING\e[0m")
+  ART_TEST_ANNOUNCE_SKIP_FAILURE = ( echo -e "$(1) \e[93mSKIPPING DUE TO EARLIER FAILURE\e[0m" )
+  ART_TEST_ANNOUNCE_SKIP_BROKEN = ( echo -e "$(1) \e[93mSKIPPING BROKEN TEST\e[0m" )
+endif
+
 # Define the command run on test success. $(1) is the name of the test. Executed by the shell.
 # The command checks prints "PASSED" then checks to see if this was a top-level make target (e.g.
 # "mm test-art-host-oat-HelloWorld32"), if it was then it does nothing, otherwise it creates a file
 # to be printed in the passing test summary.
 define ART_TEST_PASSED
-  ( echo -e "$(1) \e[92mPASSED\e[0m" && \
+  ( $(call ART_TEST_ANNOUNCE_PASS,$(1)) && \
     (echo $(MAKECMDGOALS) | grep -q $(1) || \
       (mkdir -p $(ART_HOST_TEST_DIR)/passed/ && touch $(ART_HOST_TEST_DIR)/passed/$(1))))
 endef
@@ -150,11 +166,11 @@
 define ART_TEST_SKIP
   ((echo $(ART_TEST_KNOWN_BROKEN) | grep -q -v $(1) \
      && ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] || [ $(ART_TEST_KEEP_GOING) = true ])\
-     && echo -e "$(1) \e[95mRUNNING\e[0m") \
+     && $(call ART_TEST_ANNOUNCE_RUN,$(1)) ) \
    || ((mkdir -p $(ART_HOST_TEST_DIR)/skipped/ && touch $(ART_HOST_TEST_DIR)/skipped/$(1) \
      && ([ -d $(ART_HOST_TEST_DIR)/failed/ ] \
-       && echo -e "$(1) \e[93mSKIPPING DUE TO EARLIER FAILURE\e[0m") \
-     || echo -e "$(1) \e[93mSKIPPING BROKEN TEST\e[0m") && false))
+       && $(call ART_TEST_ANNOUNCE_SKIP_FAILURE,$(1)) ) \
+     || $(call ART_TEST_ANNOUNCE_SKIP_BROKEN,$(1)) ) && false))
 endef
 
 # Create a build rule to create the dex file for a test.
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index 79f8f5e..953cfc0 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -18,6 +18,7 @@
 
 ART_CPPLINT := art/tools/cpplint.py
 ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
+ART_CPPLINT_FLAGS := --quiet
 ART_CPPLINT_SRC := $(shell find art -name "*.h" -o -name "*$(ART_CPP_EXTENSION)" | grep -v art/compiler/llvm/generated/ | grep -v art/runtime/elf\.h)
 
 # "mm cpplint-art" to verify we aren't regressing
@@ -39,8 +40,8 @@
 art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file))
 
 $$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk
-	$(hide) $(ART_CPPLINT) $(ART_CPPLINT_FILTER) $$<
-	@mkdir -p $$(dir $$@)
+	$(hide) $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$<
+	$(hide) mkdir -p $$(dir $$@)
 	$(hide) touch $$@
 
 ART_CPPLINT_TARGETS += $$(art_cpplint_touch)
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8d5418d..99080f6 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -662,6 +662,13 @@
 bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
   CHECK(error_msg != nullptr);
 
+  Runtime* runtime = Runtime::Current();
+  if (!runtime->IsDex2OatEnabled()) {
+    *error_msg = "Generation of oat file for dex location " + dex_location_
+      + " not attempted because dex2oat is disabled.";
+    return false;
+  }
+
   if (OatFileName() == nullptr) {
     *error_msg = "Generation of oat file for dex location " + dex_location_
       + " not attempted because the oat file name could not be determined.";
@@ -669,17 +676,6 @@
   }
   const std::string& oat_file_name = *OatFileName();
 
-  Runtime* runtime = Runtime::Current();
-  if (!runtime->IsDex2OatEnabled()) {
-    *error_msg = "Generation of oat file " + oat_file_name
-      + " not attempted because dex2oat is disabled";
-    return false;
-  }
-
-  std::vector<std::string> args;
-  args.push_back("--dex-file=" + dex_location_);
-  args.push_back("--oat-file=" + oat_file_name);
-
   // dex2oat ignores missing dex files and doesn't report an error.
   // Check explicitly here so we can detect the error properly.
   // TODO: Why does dex2oat behave that way?
@@ -688,9 +684,36 @@
     return false;
   }
 
+  std::unique_ptr<File> oat_file;
+  oat_file.reset(OS::CreateEmptyFile(oat_file_name.c_str()));
+  if (oat_file.get() == nullptr) {
+    *error_msg = "Generation of oat file " + oat_file_name
+      + " not attempted because the oat file could not be created.";
+    return false;
+  }
+
+  if (fchmod(oat_file->Fd(), 0644) != 0) {
+    *error_msg = "Generation of oat file " + oat_file_name
+      + " not attempted because the oat file could not be made world readable.";
+    oat_file->Erase();
+    return false;
+  }
+
+  std::vector<std::string> args;
+  args.push_back("--dex-file=" + dex_location_);
+  args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
+  args.push_back("--oat-location=" + oat_file_name);
+
   if (!Dex2Oat(args, error_msg)) {
     // Manually delete the file. This ensures there is no garbage left over if
     // the process unexpectedly died.
+    oat_file->Erase();
+    TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
+    return false;
+  }
+
+  if (oat_file->FlushCloseOrErase() != 0) {
+    *error_msg = "Unable to close oat file " + oat_file_name;
     TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
     return false;
   }
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 2c81edd..c54d7f8 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -849,6 +849,38 @@
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
+// Case: We have a DEX file but can't write the oat file.
+// Expect: We should fail to make the oat file up to date.
+TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) {
+  std::string dex_location = GetScratchDir() + "/LoadDexUnwriteableAlternateOat.jar";
+
+  // Make the oat location unwritable by inserting some non-existent
+  // intermediate directories.
+  std::string oat_location = GetScratchDir() + "/foo/bar/LoadDexUnwriteableAlternateOat.oat";
+
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+  std::string error_msg;
+  ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() == nullptr);
+}
+
+// Case: We don't have a DEX file and can't write the oat file.
+// Expect: We should fail to generate the oat file without crashing.
+TEST_F(OatFileAssistantTest, GenNoDex) {
+  std::string dex_location = GetScratchDir() + "/GenNoDex.jar";
+  std::string oat_location = GetScratchDir() + "/GenNoDex.oat";
+
+  OatFileAssistant oat_file_assistant(
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+  std::string error_msg;
+  ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg));
+}
+
 // Turn an absolute path into a path relative to the current working
 // directory.
 static std::string MakePathRelative(std::string target) {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 6ce3d94..9ff620b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -59,6 +59,9 @@
   else
     run_test_options += --build-with-javac-dx
   endif
+  ifeq ($(ART_TEST_QUIET),true)
+    run_test_options += --quiet
+  endif
 $$(dmart_target): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
 $$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TARGET_JACK_CLASSPATH_DEPENDENCIES)
 	$(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
@@ -858,6 +861,9 @@
   ifneq ($(ART_TEST_ANDROID_ROOT),)
     run_test_options := --android-root $(ART_TEST_ANDROID_ROOT) $$(run_test_options)
   endif
+  ifeq ($(ART_TEST_QUIET),true)
+    run_test_options += --quiet
+  endif
 $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
 $$(run_test_rule_name): PRIVATE_JACK_CLASSPATH := $$(jack_classpath)
 .PHONY: $$(run_test_rule_name)
diff --git a/test/run-test b/test/run-test
index f2bbaa7..9b0261e 100755
--- a/test/run-test
+++ b/test/run-test
@@ -110,6 +110,7 @@
 run_args="--quiet"
 build_args=""
 
+quiet="no"
 debuggable="no"
 prebuild_mode="yes"
 target_mode="yes"
@@ -142,6 +143,9 @@
         DEX_LOCATION=$tmp_dir
         run_args="${run_args} --host"
         shift
+    elif [ "x$1" = "x--quiet" ]; then
+        quiet="yes"
+        shift
     elif [ "x$1" = "x--use-java-home" ]; then
         if [ -n "${JAVA_HOME}" ]; then
           export JAVA="${JAVA_HOME}/bin/java"
@@ -351,6 +355,29 @@
     fi
 done
 
+# Allocate file descriptor real_stderr and redirect it to the shell's error
+# output (fd 2).
+if [ ${BASH_VERSINFO[1]} -ge 4 ] && [ ${BASH_VERSINFO[2]} -ge 1 ]; then
+  exec {real_stderr}>&2
+else
+  # In bash before version 4.1 we need to do a manual search for free file
+  # descriptors.
+  FD=3
+  while [ -e /dev/fd/$FD ]; do FD=$((FD + 1)); done
+  real_stderr=$FD
+  eval "exec ${real_stderr}>&2"
+fi
+if [ "$quiet" = "yes" ]; then
+  # Force the default standard output and error to go to /dev/null so we will
+  # not print them.
+  exec 1>/dev/null
+  exec 2>/dev/null
+fi
+
+function err_echo() {
+  echo "$@" 1>&${real_stderr}
+}
+
 # tmp_dir may be relative, resolve.
 #
 # Cannot use realpath, as it does not exist on Mac.
@@ -383,7 +410,7 @@
         run_args="${run_args} --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin"
     fi
 elif [ "$trace_stream" = "true" ]; then
-    echo "Cannot use --stream without --trace."
+    err_echo "Cannot use --stream without --trace."
     exit 1
 fi
 
@@ -410,7 +437,7 @@
 if [ "$target_mode" = "no" ]; then
     if [ "$runtime" = "jvm" ]; then
         if [ "$prebuild_mode" = "yes" ]; then
-            echo "--prebuild with --jvm is unsupported";
+            err_echo "--prebuild with --jvm is unsupported"
             exit 1;
         fi
     fi
@@ -462,7 +489,7 @@
 
 if [ "$have_image" = "no" ]; then
     if [ "$runtime" != "art" ]; then
-        echo "--no-image is only supported on the art runtime"
+        err_echo "--no-image is only supported on the art runtime"
         exit 1
     fi
     if [ "$target_mode" = "no" ]; then
@@ -485,7 +512,12 @@
 fi
 
 if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
-    echo "--dev and --update are mutually exclusive" 1>&2
+    err_echo "--dev and --update are mutually exclusive"
+    usage="yes"
+fi
+
+if [ "$dev_mode" = "yes" -a "$quiet" = "yes" ]; then
+    err_echo "--dev and --quiet are mutually exclusive"
     usage="yes"
 fi
 
@@ -499,7 +531,7 @@
     if [ '!' -d "$test_dir" ]; then
         td2=`echo ${test_dir}-*`
         if [ '!' -d "$td2" ]; then
-            echo "${test_dir}: no such test directory" 1>&2
+            err_echo "${test_dir}: no such test directory"
             usage="yes"
         fi
         test_dir="$td2"
@@ -580,7 +612,8 @@
         echo "    --pic-image           Use an image compiled with position independent code for the"
         echo "                          boot class path."
         echo "    --pic-test            Compile the test code position independent."
-    ) 1>&2
+        echo "    --quiet               Don't print anything except failure messages"
+    ) 1>&2  # Direct to stderr so usage is not printed if --quiet is set.
     exit 1
 fi
 
@@ -591,12 +624,12 @@
 td_expected="${test_dir}/${expected}"
 
 if [ ! -r $td_info ]; then
-    echo "${test_dir}: missing file $td_info" 1>&2
+    err_echo "${test_dir}: missing file $td_info"
     exit 1
 fi
 
 if [ ! -r $td_expected ]; then
-    echo "${test_dir}: missing file $td_expected" 1>&2
+    err_echo "${test_dir}: missing file $td_expected"
     exit 1
 fi
 
@@ -691,7 +724,7 @@
 if [ ${USE_JACK} = "false" ]; then
   # Set ulimit if we build with dx only, Jack can generate big temp files.
   if ! ulimit -S "$build_file_size_limit"; then
-    echo "ulimit file size setting failed"
+    err_echo "ulimit file size setting failed"
   fi
 fi
 
@@ -704,7 +737,7 @@
     echo "build exit status: $build_exit" 1>&2
     if [ "$build_exit" = '0' ]; then
         if ! ulimit -S "$run_file_size_limit"; then
-          echo "ulimit file size setting failed"
+          err_echo "ulimit file size setting failed"
         fi
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" 2>&1
@@ -720,7 +753,7 @@
                 if [ "$checker_exit" = "0" ]; then
                     good="yes"
                 fi
-                echo "checker exit status: $checker_exit" 1>&2
+                err_echo "checker exit status: $checker_exit"
             else
                 good="yes"
             fi
@@ -732,7 +765,7 @@
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         if ! ulimit -S "$run_file_size_limit"; then
-          echo "ulimit file size setting failed"
+          err_echo "ulimit file size setting failed"
         fi
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" >"$output" 2>&1
@@ -745,8 +778,8 @@
         sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
         good="yes"
     else
-        cat "$build_output" 1>&2
-        echo "build exit status: $build_exit" 1>&2
+        cat "$build_output" 1>&${real_stderr} 1>&2
+        err_echo "build exit status: $build_exit"
     fi
 elif [ "$build_only" = "yes" ]; then
     good="yes"
@@ -758,7 +791,7 @@
         diff --strip-trailing-cr -q "$expected" "$output" >/dev/null
         if [ "$?" '!=' "0" ]; then
             good="no"
-            echo "BUILD FAILED For ${TEST_NAME}"
+            err_echo "BUILD FAILED For ${TEST_NAME}"
         fi
     fi
     # Clean up extraneous files that are not used by tests.
@@ -769,13 +802,13 @@
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         if ! ulimit -S "$run_file_size_limit"; then
-          echo "ulimit file size setting failed"
+          err_echo "ulimit file size setting failed"
         fi
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" >"$output" 2>&1
         run_exit="$?"
         if [ "$run_exit" != "0" ]; then
-            echo "run exit status: $run_exit" 1>&2
+            err_echo "run exit status: $run_exit"
             good_run="no"
         elif [ "$run_checker" = "yes" ]; then
             if [ "$target_mode" = "yes" ]; then
@@ -784,7 +817,7 @@
             "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
             checker_exit="$?"
             if [ "$checker_exit" != "0" ]; then
-                echo "checker exit status: $checker_exit" 1>&2
+                err_echo "checker exit status: $checker_exit"
                 good_run="no"
             else
                 good_run="yes"
@@ -831,7 +864,7 @@
         echo ' '
     fi
 
-) 1>&2
+) 2>&${real_stderr} 1>&2
 
 # Clean up test files.
 if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; then
@@ -859,6 +892,6 @@
         fi
     fi
 
-) 1>&2
+) 2>&${real_stderr} 1>&2
 
 exit 1
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index 5615f8f..d6f55aa 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -28,8 +28,6 @@
    - Use consistent order for heap columns.
       Sometimes I see "app" first, sometimes last (from one heap dump to
       another) How about, always sort by name?
- * For long strings, limit the string length shown in the summary view to
-   something reasonable.  Say 50 chars, then add a "..." at the end.
  * For HeapTable with single heap shown, the heap name isn't centered?
  * Consistently document functions.
  * Should help be part of an AhatHandler, that automatically gets the menu and
@@ -70,6 +68,7 @@
    showing all the instances.
  * That InstanceUtils.asString properly takes into account "offset" and
    "count" fields, if they are present.
+ * InstanceUtils.getDexCacheLocation
 
 Reported Issues:
  * Request to be able to sort tables by size.
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index eb9e363..c2d75c4 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -60,9 +60,21 @@
   }
 
 
-  // Read the string value from an hprof Instance.
-  // Returns null if the object can't be interpreted as a string.
+  /**
+   * Read the string value from an hprof Instance.
+   * Returns null if the object can't be interpreted as a string.
+   */
   public static String asString(Instance inst) {
+    return asString(inst, -1);
+  }
+
+  /**
+   * Read the string value from an hprof Instance.
+   * Returns null if the object can't be interpreted as a string.
+   * The returned string is truncated to maxChars characters.
+   * If maxChars is negative, the returned string is not truncated.
+   */
+  public static String asString(Instance inst, int maxChars) {
     if (!isInstanceOfClass(inst, "java.lang.String")) {
       return null;
     }
@@ -81,13 +93,15 @@
     // array, we should use that here.
     int numChars = chars.getValues().length;
     int count = getIntField(inst, "count", numChars);
-    int offset = getIntField(inst, "offset", 0);
-    int end = offset + count - 1;
-
     if (count == 0) {
       return "";
     }
+    if (0 <= maxChars && maxChars < count) {
+      count = maxChars;
+    }
 
+    int offset = getIntField(inst, "offset", 0);
+    int end = offset + count - 1;
     if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
       return new String(chars.asCharArray(offset, count));
     }
@@ -234,12 +248,14 @@
    * Assuming inst represents a DexCache object, return the dex location for
    * that dex cache. Returns null if the given instance doesn't represent a
    * DexCache object or the location could not be found.
+   * If maxChars is non-negative, the returned location is truncated to
+   * maxChars in length.
    */
-  public static String getDexCacheLocation(Instance inst) {
+  public static String getDexCacheLocation(Instance inst, int maxChars) {
     if (isInstanceOfClass(inst, "java.lang.DexCache")) {
       Instance location = getRefField(inst, "location");
       if (location != null) {
-        return asString(location);
+        return asString(location, maxChars);
       }
     }
     return null;
diff --git a/tools/ahat/src/Value.java b/tools/ahat/src/Value.java
index 9b483fa..4eb27b1 100644
--- a/tools/ahat/src/Value.java
+++ b/tools/ahat/src/Value.java
@@ -25,6 +25,10 @@
  */
 class Value {
 
+  // For string literals, we limit the number of characters we show to
+  // kMaxChars in case the string is really long.
+  private static int kMaxChars = 200;
+
   /**
    * Create a DocString representing a summary of the given instance.
    */
@@ -43,15 +47,19 @@
     link.append(inst.toString());
 
     // Annotate Strings with their values.
-    String stringValue = InstanceUtils.asString(inst);
+    String stringValue = InstanceUtils.asString(inst, kMaxChars);
     if (stringValue != null) {
-      link.appendFormat("\"%s\"", stringValue);
+      link.appendFormat("\"%s", stringValue);
+      link.append(kMaxChars == stringValue.length() ? "..." : "\"");
     }
 
     // Annotate DexCache with its location.
-    String dexCacheLocation = InstanceUtils.getDexCacheLocation(inst);
+    String dexCacheLocation = InstanceUtils.getDexCacheLocation(inst, kMaxChars);
     if (dexCacheLocation != null) {
-      link.append(" for " + dexCacheLocation);
+      link.appendFormat(" for %s", dexCacheLocation);
+      if (kMaxChars == dexCacheLocation.length()) {
+        link.append("...");
+      }
     }
 
     URI objTarget = DocString.formattedUri("object?id=%d", inst.getId());
diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt
index 7efb1a7..421de17 100644
--- a/tools/ahat/src/manifest.txt
+++ b/tools/ahat/src/manifest.txt
@@ -1,4 +1,4 @@
 Name: ahat/
 Implementation-Title: ahat
-Implementation-Version: 0.2
+Implementation-Version: 0.3
 Main-Class: com.android.ahat.Main
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
index 7613df4..11f82a2 100644
--- a/tools/ahat/test/InstanceUtilsTest.java
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -25,21 +25,49 @@
 
 public class InstanceUtilsTest {
   @Test
-  public void basicString() throws IOException {
+  public void asStringBasic() throws IOException {
     TestDump dump = TestDump.getTestDump();
     Instance str = (Instance)dump.getDumpedThing("basicString");
     assertEquals("hello, world", InstanceUtils.asString(str));
   }
 
   @Test
-  public void nullString() throws IOException {
+  public void asStringTruncated() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance str = (Instance)dump.getDumpedThing("basicString");
+    assertEquals("hello", InstanceUtils.asString(str, 5));
+  }
+
+  @Test
+  public void asStringExactMax() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance str = (Instance)dump.getDumpedThing("basicString");
+    assertEquals("hello, world", InstanceUtils.asString(str, 12));
+  }
+
+  @Test
+  public void asStringNotTruncated() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance str = (Instance)dump.getDumpedThing("basicString");
+    assertEquals("hello, world", InstanceUtils.asString(str, 50));
+  }
+
+  @Test
+  public void asStringNegativeMax() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+    Instance str = (Instance)dump.getDumpedThing("basicString");
+    assertEquals("hello, world", InstanceUtils.asString(str, -3));
+  }
+
+  @Test
+  public void asStringNull() throws IOException {
     TestDump dump = TestDump.getTestDump();
     Instance obj = (Instance)dump.getDumpedThing("nullString");
     assertNull(InstanceUtils.asString(obj));
   }
 
   @Test
-  public void notString() throws IOException {
+  public void asStringNotString() throws IOException {
     TestDump dump = TestDump.getTestDump();
     Instance obj = (Instance)dump.getDumpedThing("anObject");
     assertNotNull(obj);
diff --git a/tools/cpplint.py b/tools/cpplint.py
index 4f063d9..308dd8c 100755
--- a/tools/cpplint.py
+++ b/tools/cpplint.py
@@ -90,6 +90,7 @@
 _USAGE = """
 Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
                    [--counting=total|toplevel|detailed]
+                   [--quiet]
         <file> [file] ...
 
   The style guidelines this tries to follow are those in
@@ -115,6 +116,9 @@
     verbose=#
       Specify a number 0-5 to restrict errors to certain verbosity levels.
 
+    quiet
+      Don't print anything if no errors are found.
+
     filter=-x,+y,...
       Specify a comma-separated list of category-filters to apply: only
       error messages whose category names pass the filters will be printed.
@@ -558,6 +562,9 @@
     self.filters = _DEFAULT_FILTERS[:]
     self.counting = 'total'  # In what way are we counting errors?
     self.errors_by_category = {}  # string to int dict storing error counts
+    # BEGIN android-added
+    self.quiet = False      # global setting.
+    # END android-added
 
     # output format:
     # "emacs" - format that emacs can parse (default)
@@ -568,6 +575,14 @@
     """Sets the output format for errors."""
     self.output_format = output_format
 
+  # BEGIN android-added
+  def SetQuiet(self, level):
+    """Sets the module's quiet setting, and returns the previous setting."""
+    last_quiet = self.quiet
+    self.quiet = level
+    return last_quiet
+  # END android-added
+
   def SetVerboseLevel(self, level):
     """Sets the module's verbosity, and returns the previous setting."""
     last_verbose_level = self.verbose_level
@@ -638,6 +653,17 @@
   _cpplint_state.SetOutputFormat(output_format)
 
 
+# BEGIN android-added
+def _Quiet():
+  """Returns the module's quiet setting."""
+  return _cpplint_state.quiet
+
+
+def _SetQuiet(level):
+  """Sets the module's quiet status, and returns the previous setting."""
+  return _cpplint_state.SetQuiet(level)
+# END android-added
+
 def _VerboseLevel():
   """Returns the module's verbosity setting."""
   return _cpplint_state.verbose_level
@@ -3888,6 +3914,9 @@
   """
 
   _SetVerboseLevel(vlevel)
+# BEGIN android-added
+  old_errors = _cpplint_state.error_count
+# END android-added
 
   try:
     # Support the UNIX convention of using "-" for stdin.  Note that
@@ -3938,8 +3967,11 @@
             'One or more unexpected \\r (^M) found;'
             'better to use only a \\n')
 
-  sys.stderr.write('Done processing %s\n' % filename)
-
+# BEGIN android-changed
+  # sys.stderr.write('Done processing %s\n' % filename)
+  if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count:
+    sys.stderr.write('Done processing %s\n' % filename)
+# END android-changed
 
 def PrintUsage(message):
   """Prints a brief usage string and exits, optionally with an error message.
@@ -3977,6 +4009,9 @@
   try:
     (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
                                                  'stdout', # TODO(enh): added --stdout
+                                                 # BEGIN android-added
+                                                 'quiet',
+                                                 # END android-added
                                                  'counting=',
                                                  'filter=',
                                                  'root='])
@@ -3987,6 +4022,9 @@
   output_format = _OutputFormat()
   output_stream = sys.stderr # TODO(enh): added --stdout
   filters = ''
+  # BEGIN android-added
+  quiet = _Quiet()
+  # END android-added
   counting_style = ''
 
   for (opt, val) in opts:
@@ -3994,6 +4032,10 @@
       PrintUsage(None)
     elif opt == '--stdout': # TODO(enh): added --stdout
       output_stream = sys.stdout # TODO(enh): added --stdout
+    # BEGIN android-added
+    elif opt == '--quiet':
+      quiet = True
+    # END android-added
     elif opt == '--output':
       if not val in ('emacs', 'vs7', 'eclipse'):
         PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
@@ -4019,6 +4061,9 @@
   _SetVerboseLevel(verbosity)
   _SetFilters(filters)
   _SetCountingStyle(counting_style)
+  # BEGIN android-added
+  _SetQuiet(quiet)
+  # END android-added
   sys.stderr = output_stream # TODO(enh): added --stdout
 
   return filenames
@@ -4037,7 +4082,11 @@
   _cpplint_state.ResetErrorCounts()
   for filename in filenames:
     ProcessFile(filename, _cpplint_state.verbose_level)
-  _cpplint_state.PrintErrorCounts()
+  # BEGIN android-changed
+  # _cpplint_state.PrintErrorCounts()
+  if not _cpplint_state.quiet or _cpplint_state.error_count > 0:
+    _cpplint_state.PrintErrorCounts()
+  # END android-changed
 
   sys.exit(_cpplint_state.error_count > 0)