ART: Checker tests for --debuggable
Checker was disabled for --debuggable because the code was not compiled
with Optimizing. Now that it is, we might want to write Checker tests
only for this mode. With this patch, CHECK-START(-ARCH)-DEBUGGABLE
tests will only be invoked on output of debuggable compilation.
Existing CHECK-START(-ARCH) tests will not be invoked.
Change-Id: I00c864f77b038af913d0d22ba7cf5655687f7c7c
diff --git a/test/537-checker-debuggable/expected.txt b/test/537-checker-debuggable/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/537-checker-debuggable/expected.txt
diff --git a/test/537-checker-debuggable/info.txt b/test/537-checker-debuggable/info.txt
new file mode 100644
index 0000000..25597d3
--- /dev/null
+++ b/test/537-checker-debuggable/info.txt
@@ -0,0 +1 @@
+Test that CHECK-START-DEBUGGABLE runs only on --debuggable code.
\ No newline at end of file
diff --git a/test/537-checker-debuggable/smali/TestCase.smali b/test/537-checker-debuggable/smali/TestCase.smali
new file mode 100644
index 0000000..8e6c7ef
--- /dev/null
+++ b/test/537-checker-debuggable/smali/TestCase.smali
@@ -0,0 +1,42 @@
+# Copyright (C) 2015 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+# The phi in this method has no actual uses but one environment use. It will
+# be eliminated in normal mode but kept live in debuggable mode. Test that
+# Checker runs the correct test for each compilation mode.
+
+## CHECK-START: int TestCase.deadPhi(int, int, int) ssa_builder (after)
+## CHECK-NOT: Phi
+
+## CHECK-START-DEBUGGABLE: int TestCase.deadPhi(int, int, int) ssa_builder (after)
+## CHECK: Phi
+
+.method public static deadPhi(III)I
+ .registers 8
+
+ move v0, p1
+ if-eqz p0, :after
+ move v0, p2
+ :after
+ # v0 = Phi [p1, p2] with no uses
+
+ invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
+
+ :return
+ return p2
+.end method
diff --git a/test/537-checker-debuggable/src/Main.java b/test/537-checker-debuggable/src/Main.java
new file mode 100644
index 0000000..a572648
--- /dev/null
+++ b/test/537-checker-debuggable/src/Main.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) { }
+}
diff --git a/test/run-test b/test/run-test
index a5b6e92..2892ce9 100755
--- a/test/run-test
+++ b/test/run-test
@@ -637,18 +637,24 @@
# on a particular DEX output, keep building them with dx for now (b/19467889).
USE_JACK="false"
- if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$debuggable" = "no" ]; then
+ if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then
# In no-prebuild mode, the compiler is only invoked if both dex2oat and
# patchoat are available. Disable Checker otherwise (b/22552692).
if [ "$prebuild_mode" = "yes" ] || [ "$have_patchoat" = "yes" -a "$have_dex2oat" = "yes" ]; then
run_checker="yes"
+
if [ "$target_mode" = "no" ]; then
cfg_output_dir="$tmp_dir"
- checker_arch_option="--arch=${host_arch_name^^}"
+ checker_args="--arch=${host_arch_name^^}"
else
cfg_output_dir="$DEX_LOCATION"
- checker_arch_option="--arch=${target_arch_name^^}"
+ checker_args="--arch=${target_arch_name^^}"
fi
+
+ if [ "$debuggable" = "yes" ]; then
+ checker_args="$checker_args --debuggable"
+ fi
+
run_args="${run_args} -Xcompiler-option --dump-cfg=$cfg_output_dir/$cfg_output \
-Xcompiler-option -j1"
fi
@@ -702,7 +708,7 @@
if [ "$target_mode" = "yes" ]; then
adb pull $cfg_output_dir/$cfg_output &> /dev/null
fi
- "$checker" $checker_arch_option "$cfg_output" "$tmp_dir" 2>&1
+ "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1
checker_exit="$?"
if [ "$checker_exit" = "0" ]; then
good="yes"
@@ -727,7 +733,7 @@
if [ "$target_mode" = "yes" ]; then
adb pull $cfg_output_dir/$cfg_output &> /dev/null
fi
- "$checker" -q $checker_arch_option "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+ "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
fi
sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
good="yes"
@@ -768,7 +774,7 @@
if [ "$target_mode" = "yes" ]; then
adb pull $cfg_output_dir/$cfg_output &> /dev/null
fi
- "$checker" -q $checker_arch_option "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+ "$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
diff --git a/tools/checker/checker.py b/tools/checker/checker.py
index bc5e17d..2e9faba 100755
--- a/tools/checker/checker.py
+++ b/tools/checker/checker.py
@@ -36,7 +36,9 @@
parser.add_argument("--dump-pass", dest="dump_pass", metavar="PASS",
help="print a compiler pass dump")
parser.add_argument("--arch", dest="arch", choices=archs_list,
- help="Run the tests for the specified target architecture.")
+ help="Run tests for the specified target architecture.")
+ parser.add_argument("--debuggable", action="store_true",
+ help="Run tests for debuggable code.")
parser.add_argument("-q", "--quiet", action="store_true",
help="print only errors")
return parser.parse_args()
@@ -83,13 +85,13 @@
Logger.fail("Source path \"" + path + "\" not found")
-def RunTests(checkPrefix, checkPath, outputFilename, targetArch):
+def RunTests(checkPrefix, checkPath, outputFilename, targetArch, debuggableMode):
c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r"))
for checkFilename in FindCheckerFiles(checkPath):
checkerFile = ParseCheckerStream(os.path.basename(checkFilename),
checkPrefix,
open(checkFilename, "r"))
- MatchFiles(checkerFile, c1File, targetArch)
+ MatchFiles(checkerFile, c1File, targetArch, debuggableMode)
if __name__ == "__main__":
@@ -103,4 +105,4 @@
elif args.dump_pass:
DumpPass(args.tested_file, args.dump_pass)
else:
- RunTests(args.check_prefix, args.source_path, args.tested_file, args.arch)
+ RunTests(args.check_prefix, args.source_path, args.tested_file, args.arch, args.debuggable)
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
index 446302f..f199a50 100644
--- a/tools/checker/file_format/checker/parser.py
+++ b/tools/checker/file_format/checker/parser.py
@@ -22,7 +22,7 @@
def __isCheckerLine(line):
return line.startswith("///") or line.startswith("##")
-def __extractLine(prefix, line, arch = None):
+def __extractLine(prefix, line, arch = None, debuggable = False):
""" Attempts to parse a check line. The regex searches for a comment symbol
followed by the CHECK keyword, given attribute and a colon at the very
beginning of the line. Whitespaces are ignored.
@@ -30,10 +30,11 @@
rIgnoreWhitespace = r"\s*"
rCommentSymbols = [r"///", r"##"]
arch_specifier = r"-%s" % arch if arch is not None else r""
+ dbg_specifier = r"-DEBUGGABLE" if debuggable else r""
regexPrefix = rIgnoreWhitespace + \
r"(" + r"|".join(rCommentSymbols) + r")" + \
rIgnoreWhitespace + \
- prefix + arch_specifier + r":"
+ prefix + arch_specifier + dbg_specifier + r":"
# The 'match' function succeeds only if the pattern is matched at the
# beginning of the line.
@@ -56,10 +57,11 @@
# Lines beginning with 'CHECK-START' start a new test case.
# We currently only consider the architecture suffix in "CHECK-START" lines.
- for arch in [None] + archs_list:
- startLine = __extractLine(prefix + "-START", line, arch)
- if startLine is not None:
- return None, startLine, arch
+ for debuggable in [True, False]:
+ for arch in [None] + archs_list:
+ startLine = __extractLine(prefix + "-START", line, arch, debuggable)
+ if startLine is not None:
+ return None, startLine, (arch, debuggable)
# Lines starting only with 'CHECK' are matched in order.
plainLine = __extractLine(prefix, line)
@@ -167,9 +169,11 @@
fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName)
fnLineOutsideChunk = lambda line, lineNo: \
Logger.fail("Checker line not inside a group", fileName, lineNo)
- for caseName, caseLines, startLineNo, testArch in \
+ for caseName, caseLines, startLineNo, testData in \
SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
- testCase = TestCase(checkerFile, caseName, startLineNo, testArch)
+ testArch = testData[0]
+ forDebuggable = testData[1]
+ testCase = TestCase(checkerFile, caseName, startLineNo, testArch, forDebuggable)
for caseLine in caseLines:
ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2])
return checkerFile
diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py
index 7ee09cd..a31aa54 100644
--- a/tools/checker/file_format/checker/struct.py
+++ b/tools/checker/file_format/checker/struct.py
@@ -36,7 +36,7 @@
class TestCase(PrintableMixin):
- def __init__(self, parent, name, startLineNo, testArch = None):
+ def __init__(self, parent, name, startLineNo, testArch = None, forDebuggable = False):
assert isinstance(parent, CheckerFile)
self.parent = parent
@@ -44,6 +44,7 @@
self.assertions = []
self.startLineNo = startLineNo
self.testArch = testArch
+ self.forDebuggable = forDebuggable
if not self.name:
Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py
index 495dabc..579c190 100644
--- a/tools/checker/file_format/checker/test.py
+++ b/tools/checker/file_format/checker/test.py
@@ -290,7 +290,7 @@
/// CHECK-NEXT: bar
""")
-class CheckerParser_ArchTests(unittest.TestCase):
+class CheckerParser_SuffixTests(unittest.TestCase):
noarch_block = """
/// CHECK-START: Group
@@ -308,11 +308,12 @@
/// CHECK-DAG: yoyo
"""
+ def parse(self, checkerText):
+ return ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText)))
+
def test_NonArchTests(self):
for arch in [None] + archs_list:
- checkerFile = ParseCheckerStream("<test-file>",
- "CHECK",
- io.StringIO(ToUnicode(self.noarch_block)))
+ checkerFile = self.parse(self.noarch_block)
self.assertEqual(len(checkerFile.testCases), 1)
self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
@@ -320,9 +321,7 @@
for targetArch in archs_list:
for testArch in [a for a in archs_list if a != targetArch]:
checkerText = self.arch_block.format(test_arch = testArch)
- checkerFile = ParseCheckerStream("<test-file>",
- "CHECK",
- io.StringIO(ToUnicode(checkerText)))
+ checkerFile = self.parse(checkerText)
self.assertEqual(len(checkerFile.testCases), 1)
self.assertEqual(len(checkerFile.testCasesForArch(testArch)), 1)
self.assertEqual(len(checkerFile.testCasesForArch(targetArch)), 0)
@@ -330,13 +329,42 @@
def test_Arch(self):
for arch in archs_list:
checkerText = self.arch_block.format(test_arch = arch)
- checkerFile = ParseCheckerStream("<test-file>",
- "CHECK",
- io.StringIO(ToUnicode(checkerText)))
+ checkerFile = self.parse(checkerText)
self.assertEqual(len(checkerFile.testCases), 1)
self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1)
self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
+ def test_NoDebugAndArch(self):
+ testCase = self.parse("""
+ /// CHECK-START: Group
+ /// CHECK: foo
+ """).testCases[0]
+ self.assertFalse(testCase.forDebuggable)
+ self.assertEqual(testCase.testArch, None)
+
+ def test_SetDebugNoArch(self):
+ testCase = self.parse("""
+ /// CHECK-START-DEBUGGABLE: Group
+ /// CHECK: foo
+ """).testCases[0]
+ self.assertTrue(testCase.forDebuggable)
+ self.assertEqual(testCase.testArch, None)
+
+ def test_NoDebugSetArch(self):
+ testCase = self.parse("""
+ /// CHECK-START-ARM: Group
+ /// CHECK: foo
+ """).testCases[0]
+ self.assertFalse(testCase.forDebuggable)
+ self.assertEqual(testCase.testArch, "ARM")
+
+ def test_SetDebugAndArch(self):
+ testCase = self.parse("""
+ /// CHECK-START-ARM-DEBUGGABLE: Group
+ /// CHECK: foo
+ """).testCases[0]
+ self.assertTrue(testCase.forDebuggable)
+ self.assertEqual(testCase.testArch, "ARM")
class CheckerParser_EvalTests(unittest.TestCase):
def parseTestCase(self, string):
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
index 6601a1e..3ded074 100644
--- a/tools/checker/match/file.py
+++ b/tools/checker/match/file.py
@@ -159,10 +159,13 @@
matchFrom = match.scope.end + 1
variables = match.variables
-def MatchFiles(checkerFile, c1File, targetArch):
+def MatchFiles(checkerFile, c1File, targetArch, debuggableMode):
for testCase in checkerFile.testCases:
if testCase.testArch not in [None, targetArch]:
continue
+ if testCase.forDebuggable != debuggableMode:
+ continue
+
# TODO: Currently does not handle multiple occurrences of the same group
# name, e.g. when a pass is run multiple times. It will always try to
# match a check group against the first output group of the same name.
diff --git a/tools/checker/run_unit_tests.py b/tools/checker/run_unit_tests.py
index 2e8f208..a0d274d 100755
--- a/tools/checker/run_unit_tests.py
+++ b/tools/checker/run_unit_tests.py
@@ -19,7 +19,7 @@
from file_format.checker.test import CheckerParser_PrefixTest, \
CheckerParser_TestExpressionTest, \
CheckerParser_FileLayoutTest, \
- CheckerParser_ArchTests, \
+ CheckerParser_SuffixTests, \
CheckerParser_EvalTests
from match.test import MatchLines_Test, \
MatchFiles_Test