Add a --test-exec flag to bisect_profile.py

Often one has a small script that can tell whether a bisection has the
target behavior or not. Add a --test-exec flag to allow
bisect_profile.py to drive itself using a non-zero exit to indicate
that the interesting behavior is present and a 0 exit to indicate it
is not.

Test: ./art/tools/bisect_profile.py --apk ~/GoogleExtServices.apk --test-exec './art/tools/compile-jar.py --profile-file bad.prof --arch arm64 ~/GoogleExtServices.apk --force-allow-oj-inlines -j1' --output-source bad.txt bad.prof
Change-Id: I2fbaf15da8177953ad0051274ee974e6c0e9f503
diff --git a/tools/bisect_profile.py b/tools/bisect_profile.py
index 9b2479a..7d323b7 100755
--- a/tools/bisect_profile.py
+++ b/tools/bisect_profile.py
@@ -76,6 +76,10 @@
   profiles.add_argument("--input-profile", help="a profile to use for bisect")
   parser.add_argument(
       "--output-source", help="human readable file create the profile from")
+  parser.add_argument("--test-exec", help="file to exec (without arguments) to test a" +
+                                           " candidate. Test should exit 0 if the issue" +
+                                           " is not present and non-zero if the issue is" +
+                                           " present.")
   parser.add_argument("output_file", help="file we will write the profiles to")
   return parser
 
@@ -97,11 +101,8 @@
   profman.check_returncode()
 
 
-def run_test(meths, args):
-  with open(args.output_source, "wt") as output:
-    dump_files(meths, args, output)
-    print("Currently testing {} methods. ~{} rounds to go.".format(
-        len(meths), 1 + math.floor(math.log2(len(meths)))))
+def get_answer(args):
+  if args.test_exec is None:
     while True:
       answer = input("Does the file at {} cause the issue (y/n):".format(
           args.output_file))
@@ -111,7 +112,21 @@
         return "n"
       else:
         print("Please enter 'y' or 'n' only!")
+  else:
+    test_args = shlex.split(args.test_exec)
+    print(" ".join(map(shlex.quote, test_args)))
+    answer = subprocess.run(test_args)
+    if answer.returncode == 0:
+      return "n"
+    else:
+      return "y"
 
+def run_test(meths, args):
+  with open(args.output_source, "wt") as output:
+    dump_files(meths, args, output)
+    print("Currently testing {} methods. ~{} rounds to go.".format(
+        len(meths), 1 + math.floor(math.log2(len(meths)))))
+  return get_answer(args)
 
 def main():
   parser = get_parser()