Fixed a few bugs in dexfuzz:

- Set error level on exit when there are divergences.
- Fix cleanCodeCache not preprending adb shell.
- Fixed clearCache.
- Fixed bug in reading end of try block.
- Fixed bug in shifting try block.
- Fixed a bug reading debug info.

Test: Extract dex files from art-tests as named below and test run commands:
dexfuzz --arm --interpreter --optimizing --repeat=2 --execute --input=seeds/068-classloader.dex
dexfuzz --arm --interpreter --optimizing --repeat=2 --execute --input=seeds/510-checker-try-catch.dex
You should not get errors.

Change-Id: I0e0fb6dc27ef6f828a6427b088f6b2ca36aae243
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index 7337ffc..18db4c1 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -21,9 +21,9 @@
 import dexfuzz.fuzzers.FuzzerMultipleNoExecute;
 import dexfuzz.fuzzers.FuzzerSingleExecute;
 import dexfuzz.fuzzers.FuzzerSingleNoExecute;
-import dexfuzz.listeners.BaseListener;
 import dexfuzz.listeners.BisectionSearchListener;
 import dexfuzz.listeners.ConsoleLoggerListener;
+import dexfuzz.listeners.FinalStatusListener;
 import dexfuzz.listeners.LogFileListener;
 import dexfuzz.listeners.MultiplexerListener;
 import dexfuzz.listeners.UniqueProgramTrackerListener;
@@ -52,12 +52,15 @@
       Options.usage();
     }
 
-    // Create the Listener, which will listen for events and report them.
-    BaseListener listener = null;
+
+    // Create a Listener that is responsible for multiple Listeners.
+    MultiplexerListener multipleListener = new MultiplexerListener();
+    multipleListener.setup();
+
+    FinalStatusListener statusListener = new FinalStatusListener();
+    multipleListener.addListener(statusListener);
+
     if (Options.repeat > 1 && Options.execute) {
-      // Create a Listener that is responsible for multiple Listeners.
-      MultiplexerListener multipleListener = new MultiplexerListener();
-      multipleListener.setup();
       // Add the live updating listener, but only if we're not printing out lots of logs.
       if (!Log.likelyToLog()) {
         multipleListener.addListener(new UpdatingConsoleListener());
@@ -73,22 +76,21 @@
       }
       // Add the unique program tracker.
       multipleListener.addListener(new UniqueProgramTrackerListener(Options.uniqueDatabaseFile));
-      listener = multipleListener;
     } else {
       // Just use the basic listener.
-      listener = new ConsoleLoggerListener();
+      multipleListener.addListener(new ConsoleLoggerListener());
     }
 
     // Create the Fuzzer that uses a particular strategy for fuzzing.
     Fuzzer fuzzer = null;
     if ((Options.repeat > 1) && Options.execute) {
-      fuzzer = new FuzzerMultipleExecute(listener);
+      fuzzer = new FuzzerMultipleExecute(multipleListener);
     } else if ((Options.repeat > 1) && !Options.execute) {
-      fuzzer = new FuzzerMultipleNoExecute(listener);
+      fuzzer = new FuzzerMultipleNoExecute(multipleListener);
     } else if ((Options.repeat == 1) && Options.execute) {
-      fuzzer = new FuzzerSingleExecute(listener);
+      fuzzer = new FuzzerSingleExecute(multipleListener);
     } else if ((Options.repeat == 1) && !Options.execute) {
-      fuzzer = new FuzzerSingleNoExecute(listener);
+      fuzzer = new FuzzerSingleNoExecute(multipleListener);
     } else {
       Log.errorAndQuit("Invalid options provided, desired fuzzer unknown.");
     }
@@ -101,6 +103,10 @@
     fuzzer.shutdown();
 
     // Cleanup the Listener.
-    listener.shutdown();
+    multipleListener.shutdown();
+
+    if (!statusListener.isSuccessful()) {
+      System.exit(1);
+    }
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
index 72f73b8..ba1365e 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Device.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -272,7 +272,7 @@
   }
 
   public void cleanCodeCache(Architecture architecture, String testLocation, String programName) {
-    String command = "rm -f " + getCacheLocation(architecture)
+    String command = getExecutionPrefixWithAdb("shell") + "rm -f " + getCacheLocation(architecture)
         + getOatFileName(testLocation, programName);
     executeCommand(command, false);
   }
@@ -280,7 +280,11 @@
   public void pushProgramToDevice(String programName, String testLocation) {
     assert(!isHost);
     if (!programPushed) {
-      executeCommand(getExecutionPrefixWithAdb("push") + programName + " " + testLocation, false);
+      String command = getExecutionPrefixWithAdb("push") + programName + " " + testLocation;
+      ExecutionResult result = executeCommand(command, false);
+      if (result.returnValue != 0) {
+        Log.errorAndQuit("Could not ADB PUSH program to device.");
+      }
       programPushed = true;
     }
   }
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/FinalStatusListener.java b/tools/dexfuzz/src/dexfuzz/listeners/FinalStatusListener.java
new file mode 100644
index 0000000..0f85f62
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/FinalStatusListener.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import java.util.List;
+import java.util.Map;
+
+import dexfuzz.executors.Executor;
+
+/**
+ * Counts divergences as they appear and checks if the testing was successful
+ * or not. Testing is successful if all divergences found are either self
+ * divergent or caused by differences in architectures.
+ */
+public class FinalStatusListener extends BaseListener {
+  private long divergence;
+  private long selfDivergent;
+  private long architectureSplit;
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    divergence++;
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    selfDivergent++;
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    architectureSplit++;
+  }
+
+  public boolean isSuccessful() {
+    return (divergence - selfDivergent - architectureSplit) == 0;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
index 650501b..5335d15 100644
--- a/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
+++ b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
@@ -259,8 +259,15 @@
       // Get the MInsns that form the start and end of the try block.
       int startLocation = tryItem.startAddr;
       mTryBlock.startInsn = insnLocationMap.get(startLocation);
-      int endLocation = tryItem.startAddr + tryItem.insnCount;
+
+      // The instructions vary in size, so we have to find the last instruction in the block in a
+      // few tries.
+      int endLocation = tryItem.startAddr + tryItem.insnCount - 1;
       mTryBlock.endInsn = insnLocationMap.get(endLocation);
+      while ((mTryBlock.endInsn == null) && (endLocation >= startLocation)) {
+        endLocation--;
+        mTryBlock.endInsn = insnLocationMap.get(endLocation);
+      }
 
       // Sanity checks.
       if (mTryBlock.startInsn == null) {
@@ -356,8 +363,9 @@
       TryItem tryItem = codeItem.tries[tryItemIdx];
 
       tryItem.startAddr = mTryBlock.startInsn.location;
-      tryItem.insnCount =
-          (short) (mTryBlock.endInsn.location - mTryBlock.startInsn.location);
+      int insnCount = mTryBlock.endInsn.location - mTryBlock.startInsn.location +
+          mTryBlock.endInsn.insn.getSize();
+      tryItem.insnCount = (short) insnCount;
 
       // Get the EncodedCatchHandler.
       EncodedCatchHandler encodedCatchHandler =
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
index 1bf6463..55e3e60 100644
--- a/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
@@ -81,12 +81,15 @@
 
   @Override
   protected boolean canMutate(MutatableCode mutatableCode) {
-    if (mutatableCode.triesSize > 0) {
-      return true;
+    if (mutatableCode.triesSize == 0) {
+      Log.debug("Method contains no tries.");
+      return false;
     }
-
-    Log.debug("Method contains no tries.");
-    return false;
+    if (mutatableCode.getInstructionCount() <= 1) {
+      Log.debug("Not enough instructions to shift try block.");
+      return false;
+    }
+    return true;
   }
 
   @Override
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
index 922ee58..561e986 100644
--- a/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
@@ -16,6 +16,8 @@
 
 package dexfuzz.rawdex;
 
+import dexfuzz.Log;
+
 import java.io.IOException;
 
 // Right now we are not parsing debug_info_item, just take the raw size
@@ -32,6 +34,11 @@
     file.getOffsetTracker().getNewOffsettable(file, this);
     data = new byte[size];
     file.read(data);
+
+    // Since we are not parsing the section, ensure that the last byte is DBG_END_SEQUENCE.
+    if (data[size - 1] != 0) {
+      Log.errorAndQuit("Error reading debug_info_item. The last byte is not DBG_END_SEQUENCE.");
+    }
   }
 
   @Override
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
index 080b5a4..729aa71 100644
--- a/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
@@ -162,10 +162,15 @@
         case MapItem.TYPE_DEBUG_INFO_ITEM:
         {
           // We aren't interested in updating the debug data, so just read it as a blob.
-          int start = mapItem.offset.getOriginalOffset();
-          int end = mapItems.get(mapItemIdx + 1).offset.getOriginalOffset();
-          int size = end - start;
-          rawDexFile.debugInfoItem = new DebugInfoItem(size);
+          long start = mapItem.offset.getOriginalOffset();
+          long end = 0;
+          if (mapItemIdx + 1 == mapItems.size()) {
+            end = file.length();
+          } else {
+            end = mapItems.get(mapItemIdx + 1).offset.getOriginalOffset();
+          }
+          long size = end - start;
+          rawDexFile.debugInfoItem = new DebugInfoItem((int)size);
           rawDexFile.debugInfoItem.read(file);
           break;
         }